一、SpringAOP的概念
一、AOP的基本概念
1、連線點(Joinpoint):可以被增強的方法。
2、切點(Pointcut):實際被增強的方法。
3、通知(Advice)(增強):
3.1.實際增強的邏輯部分叫做通知
3.2.通知型別包括
- 前置通知(執行方法前執行,通常用作引數日誌輸出、許可權校驗等)
- 後置通知(邏輯程式碼執行完,準備執行return的程式碼時通知,通常用作執行結果日誌輸出、結果加密等)
- 環繞通知(是前置通知和後置通知的綜合,方法執行前和方法執行後都要執行,通常用作方法效能統計、介面耗時、統一加密、解密等)
- 異常通知(相當於try{}catch ()中catch執行的部分,程式丟擲異常時執行,通常用作告警處理、事務回滾等)
- 最終通知(相當於try{}catch (Exception e){}finally { }中的finally執行的部分,通常用在關閉資源、清理快取等業務邏輯中)
4、切面(Aspect):把通知(增強)應用到切入點的過程。
二、Spring 框架一般都是基於 AspectJ 實現 AOP 操作
(1)AspectJ 不是 Spring 組成部分,獨立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使 用,進行 AOP 操作
三、基於 AspectJ 實現 AOP 操作
(1)基於 xml 配置檔案實現
(2)基於註解方式實現(使用)
二、SpringAOP的使用
1.通過maven方式引用jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.17</version>
</dependency>
2.建立被代理介面和實現類。程式碼如下:
package com.ybe.aop;
public interface Calculate {
/**
* 除法
* @param numA
* @param numB
* @return
*/
int div(int numA,int numB);
}
package com.ybe.aop.impl;
import com.ybe.aop.Calculate;
public class CalculateImpl implements Calculate {
@Override
public int div(int numA, int numB) {
System.out.println("執行目標方法:div");
return numA / numB;
}
}
3.使用@Aspect註解建立切面類(Aspect),程式碼如下:
package com.ybe.aop.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Aspect
@Component
public class LogAspectj {
@Pointcut("execution(* com.ybe.aop.impl.CalculateImpl.*(..))")
private void pointCut(){
}
@Before(value = "pointCut()")
public void methodBefore(JoinPoint joinPoint) throws Throwable {
String methodName = joinPoint.getSignature().getName();
System.out.println("執行目標方法【"+methodName+"】的<前置通知>,入參"+Arrays.asList(joinPoint.getArgs()));
}
@After(value = "pointCut()")
public void methodAfter(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("執行目標方法【"+methodName+"】的<後置通知>,入參"+Arrays.asList(joinPoint.getArgs()));
}
@AfterReturning(value = "pointCut()",returning = "result")
public void methodReturning(JoinPoint joinPoint, Object result) {
String methodName = joinPoint.getSignature().getName();
System.out.println("執行目標方法【"+methodName+"】的<返回通知>,入參"+Arrays.asList(joinPoint.getArgs()));
}
@AfterThrowing(value = "pointCut()")
public void methodAfterThrowing(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("執行目標方法【"+methodName+"】的<異常通知>,入參"+Arrays.asList(joinPoint.getArgs()));
}
}
4.建立Config 配置類,使用註解@EnableAspectJAutoProxy開啟aop功能,程式碼如下:
package com.ybe.aop.config;
import com.ybe.aop.Calculate;
import com.ybe.aop.impl.CalculateImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan("com.ybe.aop")
@EnableAspectJAutoProxy
public class Config {
@Bean
public Calculate calculate(){
return new CalculateImpl();
}
}
5.Main的程式碼
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
Calculate proxyFactoryBean = context.getBean("calculate", Calculate.class);
context.div(1,1);
6.執行結果如下:
三、SpringAOP的原始碼分析
SpringAop實現利用了SpringIoc容器。在SpringIOC容器的生命週期過程中整合了SpringAOP的功能。大概過程:通過 @Import註冊 實現了ImportBeanDefinitionRegistrar 介面的 AspectJAutoProxyRegistrar 類。在該類中新增實現了 InstantiationAwareBeanPostProcessor 介面的 AnnotationAwareAspectJAutoProxyCreator 類。在建立AnnoteationConfigApplicationContext的建構函式中會呼叫refresh()方法。refresh方法會進行 AspectJAutoProxyRegistrar 的呼叫,並且生成
AnnotationAwareAspectJAutoProxyCreator 的Bean物件。在第一次呼叫 CreateBean 的時候,進行Advisors的建立。在建立完 Bean後會呼叫AnnotationAwareAspectJAutoProxyCreator的 postProcessAfterInitialization方法。從 Advisors 中查詢是否匹配當前正在建立的Bean。如果能匹配,則建立相關的動態代理物件。
完整原始碼分析分三部分:SpringAOP的初始化、建立動態代理、代理方法呼叫過程。
一、SpringAOP的初始化。
主要邏輯是找到所有標註了 @Aspect 的類,並且解析類中所有的通知方法並新增到 BeanFactoryAspectJAdvisorsBuilder.advisorsCache 快取中。
整體程式碼流程圖如下:
說明:
-
建立 AnnotationConfigApplicationContext() 容器。
-
在invokeBeanFactoryPostProcessors()中,會呼叫 ConfigurationClassPostProcessor 的 postProcessBeanDefinitionRegistry() 。在此方法中,會找到 @EnableAspectJAutoProxy 的 @Import 屬性傳入的 AspectJAutoProxyRegistrar.class 類。並且執行該類的registerBeanDefinitions() 方法,建立型別為 AnnotationAwareAspectJAutoProxyCreator 、名稱為org.springframework.aop.
config.internalAutoProxyCreator的 RootBeanDefinition註冊到BeanDefinitionRegistry中。 -
在 registerBeanPostProcessors() 中會根據上面一步生成的 RootBeanDefinition物件建立 AnnotationAwareAspectJAutoProxyCreator 的例項。
-
在 finishBeanFactoryInitialization() 中第一次執行到 AbstractAutowireCapableBeanFactory.createBean() 時,會執行一段這樣的程式碼,如下
try { // 讓 BeanPostProcessors 有機會返回一個代理而不是目標 bean 例項 Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null) { return bean; } }
@Nullable protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) { Object bean = null; if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) { // Make sure bean class is actually resolved at this point. if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { Class<?> targetType = determineTargetType(beanName, mbd); if (targetType != null) { bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName); if (bean != null) { bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); } } } mbd.beforeInstantiationResolved = (bean != null); } return bean; }
@Nullable protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) { for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { Object result = bp.postProcessBeforeInstantiation(beanClass, beanName); if (result != null) { return result; } } return null; }
以上程式碼會執行 AnnotationAwareAspectJAutoProxyCreator 的 postProcessBeforeInstantiation() 方法。在該方法中會 執行 shouldSkip() 方法。程式碼如下:
@Override protected boolean shouldSkip(Class<?> beanClass, String beanName) { // TODO: Consider optimization by caching the list of the aspect names // 找到所有候選的 Advisors List<Advisor> candidateAdvisors = findCandidateAdvisors(); for (Advisor advisor : candidateAdvisors) { if (advisor instanceof AspectJPointcutAdvisor && ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) { return true; } } return super.shouldSkip(beanClass, beanName); }
在 findCandidateAdvisors 中具體會生成所有的 Advisors。
@Override protected List<Advisor> findCandidateAdvisors() { // 找到所有的 實現了 Advisor.class 介面的類,並且生成候選的 Advisors. List<Advisor> advisors = super.findCandidateAdvisors(); // 建立所有的帶了 @Aspect 特性的切面類 . if (this.aspectJAdvisorsBuilder != null) { advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors()); } return advisors; }
aspectJAdvisorsBuilder.buildAspectJAdvisors() 是核心。方法裡面的邏輯如下:
1.獲取容器裡所有的beanNames. 2.遍歷 beanNames,根據beanName獲取對應的beanType物件。 3.判斷beanType是否有@Aspect註解。 4.如果有,呼叫getAdvisorMethods()通過反射獲取該型別所有的 advisor 的 method 後設資料。 5.遍歷 methods 呼叫 getAdvisor() 獲取 Advisor 物件(InstantiationModelAwarePointcutAdvisorImpl) 6.新增到 this.advisorsCache 中。
postProcessBeforeInstantiation方法會快取所有的advisor,方法的最後返回 null。至此整個 SpringAOP的初始化完成。
二、建立動態代理
在建立Bean的生命週期的 initializeBean 方法中,會執行 AnnotationAwareAspectJAutoProxyCreator的 postProcessAfterInitialization方法。該方法會拿快取BeanFactoryAspectJAdvisorsBuilder.advisorsCache 中所有advisor的pointCut去匹配正在建立的例項Bean的所有方法。如果 advisor 和 Bean 的某一個方法能匹配上,則把該advisor新增到 advisor的候選集合中。直到找出匹配Bean的所有Adsivors。最後根據Adsivor的候選集合和Bean型別建立動態代理物件ProxyFactory。
整體程式碼流程圖如下:
說明:
1.List
ExposeInvocationInterceptor
Around
Before
After
AfterReturning
AfterThrowing
2.動態代理的建立
建立動態代理有兩種方法,一種是 JDK ,一種是 CGLib 。
1.如果目標類有實現介面的話,則是使用JDK的方式生成代理物件。
2.配置了使用Cglib進行動態代理或者目標類沒有實現介面,那麼使用Cglib的方式建立代理物件。
三、動態代理呼叫
以 JdkDynamicAopProxy 為例,在呼叫方法的時候會直接呼叫 JdkDynamicAopProxy.invoke()方法,裡面的大概邏輯如下:
1.獲取被代理的實現類;
2.找出所有匹配被呼叫方法的 advisor,並且轉成具體的通知攔截器 MethodInterceptor,返回通知攔截器鏈。轉換程式碼如下:
List<MethodInterceptor> interceptors = new ArrayList<>(3);
// 從Advisor中獲取 Advice
Advice advice = advisor.getAdvice();
// 如果 advice 本身就實現了 MethodInterceptor 介面 ,則直接進行轉換
if (advice instanceof MethodInterceptor) {
interceptors.add((MethodInterceptor) advice);
}
// AfterReturningAdviceInterceptor MethodBeforeAdviceInterceptor ThrowsAdviceInterceptor
// 這三種是通過介面卡的方式進行轉換 MethodInterceptor型別
for (AdvisorAdapter adapter : this.adapters) {
if (adapter.supportsAdvice(advice)) {
interceptors.add(adapter.getInterceptor(advisor));
}
}
if (interceptors.isEmpty()) {
throw new UnknownAdviceTypeException(advisor.getAdvice());
}
return interceptors.toArray(new MethodInterceptor[0]);
3.建立 ReflectiveMethodInvocation 物件(該物件中包括了 代理物件、被代理物件、執行的方法、方法引數、被代理物件的型別、通知攔截器鏈),執行該物件的proceed()方法,該方法中會進行通知攔截器鏈的遞迴呼叫,具體呼叫流程如下圖。ReflectiveMethodInvocation 物件在通知攔截器鏈呼叫中作用很關鍵,有銜接各個攔截器的作用。
程式碼流程如下圖:
說明:
1.在proceed方法中,會先判斷當前攔截器鏈的索引,如果索引等於最後一個那麼則執行被代理類的方法。
2.如果不是,那麼先獲取該通知攔截器並且執行該攔截器的 proceed 方法(方法接受 ReflectiveMethodInvocation 物件例項),每個通知攔截器中都會呼叫 ReflectiveMethodInvocation 物件例項 的proceed 方法。在這裡會形成遞迴呼叫。
3.通知攔截器的排序請看下圖:
4.五個通知攔截器的程式碼解釋請看上面的程式碼流程圖。