一、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 = "https://www.cnblogs.com/yuanbeier/p/pointCut()")
public void methodBefore(JoinPoint joinPoint) throws Throwable {
String methodName = joinPoint.getSignature().getName();
System.out.println("執行目標方法【"+methodName+"】的<前置通知>,入參"+Arrays.asList(joinPoint.getArgs()));
}
@After(value = "https://www.cnblogs.com/yuanbeier/p/pointCut()")
public void methodAfter(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("執行目標方法【"+methodName+"】的<后置通知>,入參"+Arrays.asList(joinPoint.getArgs()));
}
@AfterReturning(value = "https://www.cnblogs.com/yuanbeier/p/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 = "https://www.cnblogs.com/yuanbeier/p/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.五個通知攔截器的代碼解釋請看上面的代碼流程圖,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/458421.html
標籤:Java
