1. AOP概念
1.1 JoinPoint連接點:程序執行中的特定點,如方法執行,調用構造函數或字段復制等,面向切面編程,JoinPoint就是要被切入的對象。??
1.2 Advice通知:在一個連接點中,切面采取的行動,針對切入點,要做的事情。?
1.3?Pointcut切點:一個匹配連接點的正則表達式。每個任何連接點匹配一個切入點時,就執行與該切入點相關聯的指定通知。關注哪些被切入點可以切入。?
1.4?Aspect切面(Advisor):一個分布在應用程序中多個位置的標準代碼/功能,通常與實際的業務邏輯(例事務管理)不同。每個切面都側重于一個特定的橫切功能。
1.5?Weaving織入:鏈接切面和目標對象來創建一個通知對象的過程。
1.6 Advice通知 + Pointcut切點形成了切面Aspect/Advisor
1.7 JoinPoint與Pointcut區別:在Spring AOP中,所有方法執行都是JoinPoint。Pointcut是描述信息,修飾的是JoinPoint,通過Pointcut就可以確定哪些JoinPoint可以織入Advice,JointPoint與Pointcut本質上兩個不同的緯度。Advice是在JointPoint執行,Pointcut確定了哪些JoinPoint執行Advice。
2. AOP實現
2.1 織入節點
????編譯期間:切面在目標類編譯時被織入,需要引入獨立的編譯器。
? ? 編譯后:增強已經編譯出來的類,如我們要增強依賴的 jar 包中的某個類的某個方法。
? ? 類加載期:在 JVM 進行類加載的時候進行織入。
? ? 運行期:切面在應用運行的某個時期被織入。一般情況下,在織入切面時,AOP容器會為目標對象動態創建一個代理對象。
2.2 實現框架
? ? AspectJ:AspectJ 是一個采用Java 實現的AOP框架,它能夠對代碼進行編譯(一般在編譯期進行),讓代碼具有AspectJ 的 AOP 功能,AspectJ 是目前實現 AOP 框架中最成熟,功能最豐富的語言。ApectJ 主要采用的是編譯期靜態織入的方式。
? ? AspectWerkz:基于Java的簡單、動態、輕量級、強大的AOP框架。可以在運行時或編譯時輕松的改造任何(舊)應用程序或除了rt.jar以外的外部類庫。
? ? JBoss AOP:JBoss 4.0帶了一個AOP框架。這個框架和JBoss應用服務器緊密地結合,但是也能夠在應用中單獨運行它。
? ? Spring AOP:Spring AOP 是通過動態代理技術實現的,而動態代理是基于反射設計的。Spring AOP 采用了兩種混合的實現方式:JDK 動態代理和 CGLib 動態代理
2.3 AspectJ 與 Spring AOP比較
3. AspectJ實現
AspectJ是提供了完整的AOP方案,采用靜態織入,不依賴Spring AOP。
3.1 定義切面
針對test()方法進行攔截,方法執行前打印"before interceptor"
@Aspect
public class AspectjInterceptor {
????@Pointcut("execution( * com.hobbit.aspectj.Hello.test())")
????public void execute(){
????}
????@Before("execute()")
????public void beforeLog(){
????????System.out.println("before interceptor");
? ? }
}
3.2 主任務
@Component
public class Hello {
????public void test(){
????????System.out.println("hello aspectj");
? ? }
}
3.3 設置編譯器
Java Compiler -> User compiler 選擇Ajc,同時設置aspectjtools.jar,見下圖
3.4 運行結果
3.5 反編譯文件
4. Spring AOP創建代理
spring aop既可以使用AspectJ注解實現攔截,也可以不依賴AspectJ,使用Spring advice + pointcut,實現攔截注入。
tip: spring aop 使用AspectJ注解時,攔截實現方式是?
4.1 使用AspectJ注解
4.1.1 定義元注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogAnnotation {
????????Stringvalue();
}
4.1.2 定義切面
@Aspect
@Component
public class LogInterceptor {
@Pointcut("@annotation(com.hobbit.interceptor.LogAnnotation)")
public void annotationPointCut(){
}
@Before("annotationPointCut()")
public void beforeLog(JoinPoint joinPoint){
????????MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
? ? ? ? LogAnnotation logAnnotation = methodSignature.getMethod().getAnnotation(LogAnnotation.class);
? ? ? ? if(Objects.nonNull(logAnnotation)){
? ? ? ? ?????System.out.println("beforeLog : " + logAnnotation.value());
? ? ? ? }
????}
}
4.1.3 織入
@Service
public class UserService {
????@Autowired
? ? private UserMapperuserMapper;
????@LogAnnotation("queryUser")
? ? public UserEntityqueryUser(int id){
????????return userMapper.getById(id);
? ? }
}
tip:使用Aspect切面時,底層使用AspectJ實現的靜態織入,還是有Spring AOP實現的動態織入?
4.2 Spring AOP實現
Spring AOP實現了完整的方案,通過定義advice + pointcut 實現對象動態織入。
4.2.1 定義Advice
public class LogBeforeAdviceimplements MethodBeforeAdvice {? ??
????@Override
? ? public void before(Method method, Object[] args, Object target)throws Throwable{
????????System.out.println("before advice for class:" + target.getClass() +" and method:" + method.getName());
? ? }
}
4.2.2 配置PointCut及切面
? ??<bean id="logBeforeAdvice" class="com.hobbit.interceptor.LogBeforeAdvice"></bean>
????<bean id="logPointCut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
? ???????? <property name="pattern" value="com.hobbit.service.ShowService.show"/>
? ? </bean>
????<bean id="logAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
? ???????? <property name="advice" ref="logBeforeAdvice">
? ???????? <property name="pointcut" ref="logPointCut">
????</bean>
4.2.3 通過ProxyFactoryBean創建代理
<bean id="showProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
? ???? <property name="target" ref="showService">
? ? ? ?<property name="interceptorNames" value="logAdvisor">
? ???? <property name="proxyInterfaces" value="com.hobbit.service.ShowInterface">
</bean>
4.3? 創建代理時序圖
Spring的代理對象通過Bean的后置處理器,在createBean對象時封裝的。創建的代理的時序圖如下
5. Spring AOP生效
5.1 Spring 創建代理的節點
Spring 通過org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization實現AspectJ注解代理對象的創建,具體在org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization實現代理對象的創建
代理對象創建前
創建代理對象
5.2 Spring AOP攔截
? ? ? ?Spring AOP通過把Advice轉化成MethodIntercetor,通過遞歸調用的方式實現了Advice的織入,完成advice的調用后,調用目標方法完成切入。
? ? ? ?目標對象:ReflectiveMethodInvocation,在JdkDynamicAopProxy invoke方法中創建,是JoinPiont的一個實現。
? ? ? ? 攔截對象:MethodInterceptor.invoke(MethodInvocation invocation)包括了要攔截的對象。實現不同形式的攔截
? 事務攔截TransactionInterceptor
?? tip:先調用攔截鏈,如何時間后置攔截?Interceptor異常是否會導致事務切面回滾?
5.3 總結
Spring的AOP實現并不依賴于AspectJ任何類,它自己實現了一套AOP的。比如它Spring自己提供的BeforeAdvice和AfterAdvice都是對AOP聯盟規范的標準實現。以及Spring自己抽象出來的對Advice的包裝:org.springframework.aop.Advisor貫穿Spring AOP的始終。