AOP 代理
Spring Aop默認是使用標準的JDK動態代碼模式來實現AOP的代理。這個方式任何接口實現的都可以被代理。
Spring AOP 也可以使用CGLIB代理的方式。這個方式代理接口不是必要的。默認的,CGLIB 使用的業務對象不是接口的實現。
啓用 @AspectJ 支持可以使用下面的java配置形式
@Configuration @EnableAspectJAutoProxy
public class AppConfig { }
啓用@AspectJ使用xml的配置方式
<aop:aspectj-autoproxy/>
聲明切面
1,在應用定義一個有@Aspect 註解bean
<bean id="myAspect" class="org.xyz.NotVeryUsefulAspect">
<!-- 配置這個切面的屬性 -->
</bean>
2,一個有@Aspect 註解的NotVeryUsefulAspect 類的定義
package org.xyz;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class NotVeryUsefulAspect {
}
聲明切點
@Pointcut("execution(* transfer(..))")// the pointcut expression
private void anyOldTransfer() {}// the pointcut signature
Supported Pointcut Designators
支持切點的指示符
Spring AOP支持 AspjectJ切點的指示符,可以使用的表達式如下:
execution: 匹配執行方法的切點。
within:限制切點必須包含匹配的類型。
this:匹配的切點spring aop 代理的實例返回的類型。
target: 匹配的切點spring aop 代理的實例返回的代理的對象。
args: 切點匹配的實例的參數類型。
@target: 以註解的方式來指定,匹配的切點的批執行返回對象的類型。
@args: 以註解的方式來指定,匹配的切點運行時返回的參數類型。
@within: 以註解的方式來指定,切點應當包含的類型。
@annotation: 以註解的方式,指定切點包含的子對象。
混合的切點表達式
你可以使用 &&, || and !,也可以使用通過名稱來表示。如下三個例子:
@Pointcut("execution(public * *(..))")
private void anyPublicOperation() {}
@Pointcut("within(com.xyz.someapp.trading..*)")
private void inTrading() {}
@Pointcut("anyPublicOperation() && inTrading()")
private void tradingOperation() {}
anyPublicOperation 表示切點中所有的public方法;
inTrading 匹配的是執行 trading 模塊;
tradingOperation 匹配的是在trading模塊之中的所有的public方法;
package com.xyz.someapp;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect public class SystemArchitecture
{
/** * A join point is in the web layer if the method is defined
* in a type in the com.xyz.someapp.web package or any sub-package
* under that.
*/
@Pointcut("within(com.xyz.someapp.web..*)")
public void inWebLayer() {}
/**
* A join point is in the service layer if the method is defined
* in a type in the com.xyz.someapp.service package or any sub-package
* under that.
*/
@Pointcut("within(com.xyz.someapp.service..*)")
public void inServiceLayer() {}
/** * A join point is in the data access layer if the method is defined
* in a type in the com.xyz.someapp.dao package or any sub-package
* under that. */ @Pointcut("within(com.xyz.someapp.dao..*)")
public void inDataAccessLayer() {}
/**
* A business service is the execution of any method defined on a service
* interface. This definition assumes that interfaces are placed in the
* "service" package, and that implementation types are in sub-packages.
*
* If you group service interfaces by functional area (for example,
* in packages com.xyz.someapp.abc.service and com.xyz.someapp.def.service) then
* the pointcut expression "execution(* com.xyz.someapp..service.*.*(..))" * could be used instead.
*
* Alternatively, you can write the expression using the 'bean'
* PCD, like so "bean(*Service)". (This assumes that you have
* named your Spring service beans in a consistent fashion.)
*/
@Pointcut("execution(* com.xyz.someapp..service.*.*(..))")
public void businessService() {}
/** * A data access operation is the execution of any method defined on a
* dao interface. This definition assumes that interfaces are placed in the
* "dao" package, and that implementation types are in sub-packages.
*/
@Pointcut("execution(* com.xyz.someapp.dao.*.*(..))")
public void dataAccessOperation() {} }
可以在任何地方引用需要引用定義好的切點表達式。例如,服務層的事務管理,如下:
<aop:config>
<aop:advisor pointcut="com.xyz.someapp.SystemArchitecture.businessService()" advice-ref="tx-advice"/>
</aop:config>
<tx:advice id="tx-advice">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
例如: Spring AOP 的用戶可以使用 execution 切點表達式,如:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
下面展示一些常用的execution 表達的切點表達式
- 執行任何的pulblic 方法
execution(public * *(..))
- 執行任何的以 set 開頭的方法
execution(* set*(..))
- 執行 AccountService 爲接口的任何方法
execution(* com.xyz.service.AccountService.*(..))
- 在service 包下面的所有方法;
execution(* com.xyz.service.*.*(..))
- 執行 service 包及其子包下面的所有方法;
execution(* com.xyz.service..*.*(..))
- 任何在 service 包下面的切點
within(com.xyz.service.*)
- 任何在service包及其子包下面的切點
within(com.xyz.service..*)
- 任何實現AccountService 接口的切點
this(com.xyz.service.AccountService)
這裏target 表示實現AccountService 接口的所有鏈接點
target(com.xyz.service.AccountService)
獲取連接點一個參數,這個參數是運行時通過Serializable 實現的
args(java.io.Serializable)
獲取目標對象有註解@Transactional 的所有切點
@target(org.springframework.transaction.annotation.Transactional)
聲明的對象類型中含有註解 @Transactional 的所有切點
@within(org.springframework.transaction.annotation.Transactional)
在執行的方法有添加 @Transactional 註解的所有切點
@annotation(org.springframework.transaction.annotation.Transactional)
運行的時候有一個參數是 @Classified 註解的切點
@args(com.xyz.security.Classified)
有一個spring bean 名字爲 tradeService 的連接點
bean(tradeService)
Spring bean 匹配 *Service 通識符的bean的連接點
bean(*Service)
聲明通知
Before Advice
前置通知
聲明一個切點的前置通知可以使用 @Before 註解如下:
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class BeforeExample {
@Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
public void doAccessCheck() {
// ...
}
}
如果是使用切點表達式,我們要重寫前面的示例:
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class BeforeExample {
@Before("execution(* com.xyz.myapp.dao.*.*(..))")
public void doAccessCheck() {
// ...
}
}
返回結果後通知
返回結果通知通常是用來匹配一個方法正常結束後執行,可以通過@AfterReturning 註解來實現:
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;
@Aspect
public class AfterReturningExample {
@AfterReturning("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
public void doAccessCheck() {
// ...
}
}
有時,我們需要訪問通知消息體的返回值。可以用下面的這個 @AfterReturning 這個註解方式 綁定返回值來獲取訪問權限。具體如下:
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;
@Aspect
public class AfterReturningExample {
@AfterReturning(
pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()",
returning="retVal")
public void doAccessCheck(Object retVal) {
// ...
}
}
異常通知:
當前匹配的一個方法執行拋出異常後退出時,執行異常通知 。可以用 @AfterThrowing 註解的方式來聲明如下:
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;
@Aspect public class AfterThrowingExample {
@AfterThrowing("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
public void doRecoveryActions() {
// ...
}
}