Spring AOP 知識點思惟導圖git
AOP(Aspect-oriented Programming) , 名字與 OOP(Object-oriented programming) 僅差一個字母, 其實它是對 OOP 編程的一種補充. AOP 翻譯過來叫面向切面編程, 核心就是這個切面. 切面表示從業務邏輯中分離出來的橫切邏輯, 好比性能監控, 日誌記錄, 權限控制等, 這些功能均可以從核心業務邏輯代碼中抽離出來. 也就是說, 經過 AOP 能夠解決代碼耦合問題, 讓職責更加單一.正則表達式
咱們來經過代碼來理解下概念, 這裏有一個轉帳業務:spring
public interface IAccountService {
//主業務邏輯: 轉帳
void transfer();
}
public class AccountServiceImpl implements IAccountService {
@Override
public void transfer() {
System.out.println("調用dao層,完成轉帳主業務.");
}
}
複製代碼
如今有一個需求是, 在轉帳以前須要驗證用戶身份, 通常狀況下咱們直接就去修改源代碼了:express
public class AccountServiceImpl implements IAccountService {
@Override
public void transfer() {
System.out.println("對轉帳人身份進行驗證.");
System.out.println("調用dao層,完成轉帳主業務.");
}
}
複製代碼
但做爲一個"有經驗"的 Java 開發, 咱們知道 「身份驗證」 這個業務徹底是能夠剝離出來的, 因此使用下代理設計模式的思想:編程
public class AccountProxy implements IAccountService {
//目標對象
private IAccountService target;
public AccountProxy(IAccountService target) {
this.target = target;
}
/**
* 代理方法,實現對目標方法的功能加強
*/
@Override
public void transfer() {
before();
target.transfer();
}
/**
* 身份驗證
*/
private void before() {
System.out.println("對轉帳人身份進行驗證.");
}
}
public class Client {
public static void main(String[] args) {
//建立目標對象
IAccountService target = new AccountServiceImpl();
//建立代理對象
AccountProxy proxy = new AccountProxy(target);
proxy.transfer();
}
}
複製代碼
AOP 的實現原理就是代理設計模式, 上面這個是靜態代理, 但 Spring AOP 是經過動態代理實現的, 因此咱們須要瞭解下動態代理: blog.csdn.net/litianxiang…後端
AOP 並非由 Spring 首創, 在 Spring 以前就有了 AOP 開源框架—AspectJ, 並且 AspectJ 的 AOP 功能要比 Spring 更強大, 因此在 Spring 的後期版本中就集成了 AspectJ. 但咱們仍是有必要了解下 Spring 的 AOP 功能.設計模式
咱們用編程式來感覺下 Spring AOP. 下面例子使用的是環繞加強.bash
(1) 業務類架構
public interface IAccountService {
//主業務邏輯: 轉帳
void transfer();
}
public class AccountServiceImpl implements IAccountService {
@Override
public void transfer() {
System.out.println("調用dao層,完成轉帳主業務.");
}
}
複製代碼
(2) 加強mvc
public class AccountAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
before();
Object result = invocation.proceed();
after();
return result;
}
private void before() {
System.out.println("Before");
}
private void after(){
System.out.println("After");
}
}
複製代碼
(3) 測試
public class Test {
public static void main(String[] args) {
//建立代理工廠
ProxyFactory proxyFactory = new ProxyFactory();
//配置目標對象
proxyFactory.setTarget(new AccountServiceImpl());
//配置加強
proxyFactory.addAdvice(new AccountAdvice());
IAccountService proxy = (IAccountService) proxyFactory.getProxy();
proxy.transfer();
}
}
結果:
Before
調用dao層,完成轉帳主業務.
After
複製代碼
Spring AOP 之聲明式就是使用配置文件來聲明各類 bean.
(1) 業務類
public interface IAccountService {
//主業務邏輯: 轉帳
void transfer();
}
public class AccountServiceImpl implements IAccountService {
@Override
public void transfer() {
System.out.println("調用dao層,完成轉帳主業務.");
}
}
複製代碼
(2) 加強
public class AccountAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
before();
Object result = invocation.proceed();
after();
return result;
}
private void before() {
System.out.println("Before");
}
private void after(){
System.out.println("After");
}
}
複製代碼
(3) 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--聲明bean-->
<bean id="accountService" class="org.service.impl.AccountServiceImpl"></bean>
<bean id="accountAdvice" class="org.aspect.AccountAdvice"></bean>
<!--配置代理工廠-->
<bean id="accountProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--目標接口-->
<property name="interfaces" value="org.service.IAccountService"/>
<!--目標對象-->
<property name="target" ref="accountService"/>
<!--加強-->
<property name="interceptorNames" value="accountAdvice"/>
</bean>
</beans>
複製代碼
(4) 測試
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/spring-service.xml");
IAccountService proxy = (IAccountService) context.getBean("accountProxy");
proxy.transfer();
}
}
結果:
Before
調用dao層,完成轉帳主業務.
After
複製代碼
前面的編程式和聲明式都沒有用到切面, 他們對一個類中的全部方法都實施了加強. 若是咱們須要針對類中的某個方法進行加強, 就可使用切面來解決這個問題.
在聲明式的代碼基礎上修改下配置文件, 加入切面:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--聲明bean-->
<bean id="accountService" class="org.service.impl.AccountServiceImpl"></bean>
<bean id="accountAdvice" class="org.aspect.AccountAdvice"></bean>
<!--配置切面-->
<bean id="accountAspect" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!--加強-->
<property name="advice" ref="accountAdvice"/>
<!--切入點-->
<property name="pattern" value="org.service.impl.AccountServiceImpl.transfer.*"/>
</bean>
<!--配置代理-->
<bean id="accountProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--目標對象-->
<property name="target" ref="accountService"/>
<!--切面-->
<property name="interceptorNames" value="accountAspect"/>
</bean>
</beans>
複製代碼
這裏的切面配置的是基於正則表達式的 RegexpMethodPointcutAdvisor, 表示攔截全部以 「transfer」 開頭的方法. 除此以外, Spring AOP 還有如下配置:
讓用戶去配置一個或少數幾個代理, 彷佛還能夠接受, 但隨着項目的擴大, 代理配置會愈來愈多, 這個時候再讓你手動配置, 那整我的都很差了. 不過不用擔憂, Spring AOP 爲咱們提供了自動生成代理的功能.
在聲明式的代碼基礎上修改下配置文件和測試類:
(1) 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--聲明bean-->
<bean id="accountService" class="org.service.impl.AccountServiceImpl"></bean>
<bean id="accountAdvice" class="org.aspect.AccountAdvice"></bean>
<!--配置切面-->
<bean id="accountAspect" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!--加強-->
<property name="advice" ref="accountAdvice"/>
<!--切入點-->
<property name="pattern" value="org.service.impl.AccountServiceImpl.transfer.*"/>
</bean>
<!--配置自動代理: 自動掃描全部切面類, 併爲其生成代理-->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
</beans>
複製代碼
(2) 測試
public class Test{
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/spring-service.xml");
IAccountService proxy = (IAccountService) context.getBean("accountService");
proxy.transfer();
}
}
結果:
Before
調用dao層,完成轉帳主業務.
After
複製代碼
在企業開發中幾乎不使用 Spring 自身的 AOP 功能, 而是用 AspectJ 代替, Spring 在後期本身集成了 AspectJ 也間接證實了 AspectJ 的強大, 咱們下面來了解下 Spring + AspectJ.
AspectJ 加強類型
execution(* org.service.impl.AccountServiceImpl.*(..))
複製代碼
(1) 業務類
public interface IAccountService {
//主業務邏輯: 轉帳
void transfer();
}
public class AccountServiceImpl implements IAccountService {
@Override
public void transfer() {
System.out.println("調用dao層,完成轉帳主業務.");
}
}
複製代碼
(2) 加強
public class AccountAdvice{
//前置加強
public void myBefore(JoinPoint joinPoint){
before();
}
//後置加強
public void myAfter(JoinPoint joinPoint) {
after();
}
//環繞加強
public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{
before();
Object result = joinPoint.proceed();
after();
return result;
}
//拋出異常加強
public void myThrowing(JoinPoint joinPoint, Throwable e) {
System.out.println("拋出異常加強: " + e.getMessage());
}
private void before() {
System.out.println("Before");
}
private void after(){
System.out.println("After");
}
}
複製代碼
(3) 配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--聲明bean-->
<bean id="accountService" class="org.service.impl.AccountServiceImpl"></bean>
<bean id="accountAdvice" class="org.aspect.AccountAdvice"></bean>
<!--切面-->
<aop:config>
<aop:aspect ref="accountAdvice">
<!--切入點表達式-->
<aop:pointcut expression="execution(* org.service.impl.AccountServiceImpl.*(..))" id="myPointCut"/>
<!--環繞加強-->
<aop:around method="myAround" pointcut-ref="myPointCut"/>
</aop:aspect>
</aop:config>
</beans>
複製代碼
(4) 測試
public class Test{
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/spring-service.xml");
IAccountService proxy = (IAccountService) context.getBean("accountService");
proxy.transfer();
}
}
結果:
Before
調用dao層,完成轉帳主業務.
After
複製代碼
(1) 業務類
public interface IAccountService {
//主業務邏輯: 轉帳
void transfer();
}
@Component
public class AccountServiceImpl implements IAccountService {
@Override
public void transfer() {
System.out.println("調用dao層,完成轉帳主業務.");
}
}
複製代碼
(2) 切面
這裏就再也不叫加強了, 由於有了切入點和加強, 叫切面更好.
@Component
@Aspect
public class AccountAspect{
//切入點
@Pointcut("execution(* org.tyshawn.service.impl.AccountServiceImpl.*(..))")
private void pointCut(){};
//前置加強
@Before("pointCut()")
public void myBefore(JoinPoint joinPoint){
before();
}
//後置加強
@After("pointCut()")
public void myAfter(JoinPoint joinPoint) {
after();
}
//環繞加強
@Around("pointCut()")
public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{
before();
Object result = joinPoint.proceed();
after();
return result;
}
//拋出異常加強
@AfterThrowing(value = "pointCut()", throwing = "e")
public void myThrowing(JoinPoint joinPoint, Throwable e) {
System.out.println("拋出異常加強: " + e.getMessage());
}
private void before() {
System.out.println("Before");
}
private void after(){
System.out.println("After");
}
}
複製代碼
(3) 配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--註解掃描-->
<context:component-scan base-package="org.tyshawn"></context:component-scan>
<!--自動代理 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
複製代碼
(4) 測試
public class Test{
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/spring-service.xml");
IAccountService proxy = (IAccountService) context.getBean("accountServiceImpl");
proxy.transfer();
}
}
結果:
Before
Before
調用dao層,完成轉帳主業務.
After
After
複製代碼
來源:t.im/kzfr
做者的開源項目推薦:
關注公衆號回覆開源項目便可獲取