正文:
在上一篇,我們對IOC核心部分流程已經分析完畢,相信小夥伴們有所收穫,從這一篇開始,我們將會踏上新的旅程,即Spring的另一核心:AOP!
首先,為了讓大家能更有效的理解AOP,先帶大家過一下AOP中的術語:
- 切面(Aspect):指關注點模組化,這個關注點可能會橫切多個物件。事務管理是企業級Java應用中有關橫切關注點的例子。在Spring AOP中,切面可以使用在普通類中以@Aspect註解來實現。
- 連線點(Join point):在Spring AOP中,一個連線點總是代表一個方法的執行,其實就代表增強的方法。
- 通知(Advice):在切面的某個特定的連線點上執行的動作。通知有多種型別,包括
around
,before
和after
等等。許多AOP框架,包括Spring在內,都是以攔截器做通知模型的,並維護著一個以連線點為中心的攔截器鏈。 - 目標物件(Target):目標物件指將要被增強的物件。即包含主業務邏輯的類的物件。
- 切點(Pointcut):匹配連線點的斷言。通知和切點表示式相關聯,並在滿足這個切點的連線點上執行(例如,當執行某個特定名稱的方法時)。切點表示式如何和連線點匹配是AOP的核心:Spring預設使用AspectJ切點語義。
- 顧問(Advisor): 顧問是Advice的一種包裝體現,Advisor是Pointcut以及Advice的一個結合,用來管理Advice和Pointcut。
- 織入(Weaving):將通知切入連線點的過程叫織入
- 引入(Introductions):可以將其他介面和實現動態引入到targetClass中
一個栗子
術語看完了,我們先上個Demo回顧一下吧~
-
首先,使用
EnableAspectJAutoProxy
註解開啟我們的AOP@ComponentScan(basePackages = {"com.my.spring.test.aop"}) @Configuration @EnableAspectJAutoProxy public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class); IService service = context.getBean("service", IService.class); service.doService(); } }
-
寫一個介面
public interface IService { void doService(); }
-
寫一個實現類
@Service("service") public class ServiceImpl implements IService{ @Override public void doService() { System.out.println("do service ..."); } }
-
寫一個切面
@Aspect @Component public class ServiceAspect { @Pointcut(value = "execution(* com.my.spring.test.aop.*.*(..))") public void pointCut() { } @Before(value = "pointCut()") public void methodBefore(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); System.out.println("執行目標方法 【" + methodName + "】 的【前置通知】,入參:" + Arrays.toString(joinPoint.getArgs())); } @After(value = "pointCut()") public void methodAfter(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); System.out.println("執行目標方法 【" + methodName + "】 的【後置通知】,入參:" + Arrays.toString(joinPoint.getArgs())); } @AfterReturning(value = "pointCut()") public void methodReturn(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); System.out.println("執行目標方法 【" + methodName + "】 的【返回通知】,入參:" + Arrays.toString(joinPoint.getArgs())); } @AfterThrowing(value = "pointCut()") public void methodThrow(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); System.out.println("執行目標方法 【" + methodName + "】 的【異常通知】,入參:" + Arrays.toString(joinPoint.getArgs())); } }
-
測試執行
執行目標方法 【doService】 的【前置通知】,入參:[] do service ... 執行目標方法 【doService】 的【返回通知】,入參:[] 執行目標方法 【doService】 的【後置通知】,入參:[]
以上
Demo看完了,執行效果也出來了,AOP已生效,但如何生效的呢?相比於我們普通使用Bean的Demo,在這裡,我們只不過加上了一個@EnableAspectJAutoProxy
註解以及一個標識了@Aspectj
的類,那麼我們先看看@EnableAspectJAutoProxy
這個註解做了什麼吧~
開啟AOP
以下是筆者所畫的大致流程圖
其中AspectJAutoProxyRegistrar
實現了ImportBeanDefinitionRegistrar
,所以在處理BeanFactoryPostProcessor
邏輯時將會呼叫registerBeanDefinitions
方法,此時就會把AnnotationAwareAspectJAutoProxyCreator
註冊到容器中,其中BeanFactoryPostProcessor
的邏輯就不再說了,往期文章有過詳細分析。而AnnotationAwareAspectJAutoProxyCreator
的類圖如下:
我們發現AnnotationAwareAspectJAutoProxyCreator
是實現了BeanPostProcessor
介面的類,所以它其實是一個後置處理器,那麼,還記得在建立Bean過程中的BeanPostProcessor
九次呼叫時機嗎?不記得也沒關係,AnnotationAwareAspectJAutoProxyCreator
起作用的地方是在bean的例項化前以及初始化後,分別對應著解析切面和建立動態代理的過程,現在,就讓我們先來看看解析切面的過程吧~
解析切面
解析切面的流程如下圖所示:
我們已經瞭解到切面解析的過程是由AnnotationAwareAspectJAutoProxyCreator
完成的,而AnnotationAwareAspectJAutoProxyCreator
又繼承了AbstractAutoProxyCreator
,所以首先,我們先會來到AbstractAutoProxyCreator#postProcessBeforeInstantiation
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
// class型別是否為(Advice, Pointcut, Advisor, AopInfrastructureBean)
// shouldSkip中將會解析切面
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}
呼叫到子類的AspectJAwareAdvisorAutoProxyCreator#shouldSkip
@Override
protected boolean shouldSkip(Class<?> beanClass, String beanName) {
// 尋找advisor
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
protected List<Advisor> findCandidateAdvisors() {
// 尋找實現了Advisor介面的類, 由於我們一般不會以介面的方式實現切面,這裡返回null
List<Advisor> advisors = super.findCandidateAdvisors();
if (this.aspectJAdvisorsBuilder != null) {
// 這裡將解析出所有的切面
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
return advisors;
}
buildAspectJAdvisors
public List<Advisor> buildAspectJAdvisors() {
// aspectBeanNames有值則說明切面已解析完畢
List<String> aspectNames = this.aspectBeanNames;
// Double Check
if (aspectNames == null) {
synchronized (this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
List<Advisor> advisors = new ArrayList<>();
aspectNames = new ArrayList<>();
// 取出是Object子類的bean,其實就是所有的bean
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false);
for (String beanName : beanNames) {
// 獲得該bean的class
Class<?> beanType = this.beanFactory.getType(beanName);
// 判斷是否有標識@AspectJ註解
if (this.advisorFactory.isAspect(beanType)) {
// 將beanName放入集合中
aspectNames.add(beanName);
// 將beanType和beanName封裝到AspectMetadata中
AspectMetadata amd = new AspectMetadata(beanType, beanName);
// Kind預設為SINGLETON
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
// 這裡會通過@Before @After等標識的方法獲取到所有的advisor
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
if (this.beanFactory.isSingleton(beanName)) {
// 將獲取到的所有advisor放入快取
this.advisorsCache.put(beanName, classAdvisors);
}
advisors.addAll(classAdvisors);
}
}
}
// 將所有解析過的beanName賦值
this.aspectBeanNames = aspectNames;
return advisors;
}
}
}
// aspectNames不為空,意味有advisor,取出之前解析好的所有advisor
List<Advisor> advisors = new ArrayList<>();
// 獲取到所有解析好的advisor
for (String aspectName : aspectNames) {
List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
if (cachedAdvisors != null) {
advisors.addAll(cachedAdvisors);
}
return advisors;
}
advisorFactory.getAdvisors
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
// 獲取到標識了@AspectJ的class,其實就是剛剛封裝的class
Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
// 獲取className
String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
List<Advisor> advisors = new ArrayList<>();
// 拿出該類除了標識@PointCut的所有方法進行遍歷 getAdvisorMethods時會對method進行一次排序
// 排序順序 Around, Before, After, AfterReturning, AfterThrowing
for (Method method : getAdvisorMethods(aspectClass)) {
// 獲取到advisor
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
if (advisor != null) {
// 加入到集合中
advisors.add(advisor);
}
}
}
我們先看下getAdvisorMethods
方法
private List<Method> getAdvisorMethods(Class<?> aspectClass) {
final List<Method> methods = new ArrayList<>();
// 迴圈遍歷該類和父類的所有方法
ReflectionUtils.doWithMethods(aspectClass, method -> {
// 排除@PointCut標識的方法
if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
methods.add(method);
}
}, ReflectionUtils.USER_DECLARED_METHODS);
if (methods.size() > 1) {
// 以Around, Before, After, AfterReturning, AfterThrowing的順序自定義排序
methods.sort(METHOD_COMPARATOR);
}
return methods;
}
不知道小夥伴們對ReflectionUtils.doWithMethods這個工具類熟不熟悉呢,這個工具類在之前分析Bean建立過程時可是出現了好多次呢,並且我們也是可以使用的
現在,已經獲取到切面中的所有方法了,那麼接下來就該對這些方法解析並進行封裝成advisor了~
getAdvisor
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrderInAspect, String aspectName) {
// 獲取方法上的切點表示式
AspectJExpressionPointcut expressionPointcut = getPointcut(
candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
// 封裝成物件返回,建立物件時將會解析方法建立advice
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}
獲取切點表示式的過程其實非常簡單,即是解析方法上的註解,取出註解上的value即可
getPointcut
private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
// 查詢方法上和AspectJ相關注解
AspectJAnnotation<?> aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
// 設定切點表示式
AspectJExpressionPointcut ajexp =
new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
// PointcutExpression 為註解上value屬性的值
ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
if (this.beanFactory != null) {
ajexp.setBeanFactory(this.beanFactory);
}
return ajexp;
}
new InstantiationModelAwarePointcutAdvisorImpl,在這裡,才會真正建立出advice
public InstantiationModelAwarePointcutAdvisorImpl(){
//...省略賦值過程...
// 例項化出advice
this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
}
private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) {
// 獲取advice,aspectJAdviceMethod為方法,aspectName為切面類
Advice advice = this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut,
this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
return (advice != null ? advice : EMPTY_ADVICE);
}
public Advice getAdvice(){
// 根據方法獲取到註解資訊
AspectJAnnotation<?> aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
AbstractAspectJAdvice springAdvice;
// 根據註解型別返回物件,建立物件的過程都是一樣的,都是呼叫父類的構造方法
// candidateAdviceMethod為切面的方法,expressionPointcut是切點
switch (aspectJAnnotation.getAnnotationType()) {
case AtPointcut
return null;
case AtAround:
springAdvice = new AspectJAroundAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtBefore:
springAdvice = new AspectJMethodBeforeAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfter:
springAdvice = new AspectJAfterAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
//...省略其他的advice
default:
throw new UnsupportedOperationException(
"Unsupported advice type on method: " + candidateAdviceMethod);
}
return springAdvice;
}
springAdvice已建立完畢,意味著切面中的某個方法已經解析完畢了,其他的方法解析過程大致也是相似的
小結
其實解析切面本身並不複雜,只是Spring中將切面類封裝來封裝去容易使人混亂,如buildAspectJAdvisors
方法中,封裝了一個AspectMetadata amd = new AspectMetadata(beanType, beanName);
,又立即發起判定amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON
,其實這裡完全可以變為AjTypeSystem.getAjType(currClass).getPerClause().getKind() == PerClauseKind.SINGLETON
,AjTypeSystem.getAjType(currClass)
為new AspectMetadata
的一部分邏輯,筆者這裡給大家總結一下吧。
首先,迴圈所有的beanName,找到帶有@Aspectj註解的class, 獲取到class中的所有方法進行遍歷解析,取出方法註解上的值(切點:pointcut),然後把方法,切點表示式,封裝了BeanFactory,BeanName的factory封裝成相應的SpringAdvice, 由SpringAdvice和pointcut組合成一個advisor。
建立代理物件
切面已經解析完畢,接下來,我們就來看看如何把解析出的切面織入到目標方法中吧
但,在這之前,還有必要給小夥伴們補充一點前置知識。
我們知道,一個bean是否能夠被aop代理,取決於它是否滿足代理條件,即為是否能夠被切點表示式所命中,而在Spring AOP中,bean與切點表示式進行匹配的是AspectJ實現的,並非Spring所完成的,所以我們先來看看AspectJ如何匹配出合適的bean的吧
栗子
首先需要引入org.aspectj:aspectjweaver
依賴
一個Service,包名為com.my.spring.test.aop
package com.my.spring.test.aop;
/**
* 切點表示式可以匹配的類
*
*/
public class ServiceImpl{
/**
* 切點表示式可以匹配的方法
*/
public void doService() {
System.out.println("do service ...");
}
public void matchMethod() {
System.out.println("ServiceImpl.notMatchMethod");
}
}
然後,我們自己封裝一個用於匹配的工具類,具體功能大家看註釋哈哈
package com.my.spring.test.aspectj;
import org.aspectj.weaver.tools.PointcutExpression;
import org.aspectj.weaver.tools.PointcutParser;
import org.aspectj.weaver.tools.ShadowMatch;
import java.lang.reflect.Method;
/**
* aop工具
*/
public class AOPUtils {
// AspectJ的固定寫法,獲取一個切點解析器
static PointcutParser parser = PointcutParser
.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(
PointcutParser.getAllSupportedPointcutPrimitives(), ClassLoader.getSystemClassLoader());
// 切點表示式
private static PointcutExpression pointcutExpression;
/**
* 初始化工具類,我們需要先獲取一個切點表示式
*
* @param expression 表示式
*/
public static void init(String expression){
// 解析出一個切點表示式
pointcutExpression = parser.parsePointcutExpression(expression);
}
/**
* 第一次篩選,根據類篩選,也叫做粗篩
*
* @param targetClass 目標類
* @return 是否匹配
*/
public static boolean firstMatch(Class<?> targetClass){
// 根據類篩選
return pointcutExpression.couldMatchJoinPointsInType(targetClass);
}
/**
* 第二次篩選,根據方法篩選,也叫做精篩,精篩通過則說明完全匹配
* ps: 也可以使用該方法進行精篩,粗篩的目的是提高效能,第一次直接過濾掉不合適的類再慢慢精篩
*
* @param method 方法
* @return 是否匹配
*/
public static boolean lastMatch(Method method){
// 根據方法篩選
ShadowMatch shadowMatch = pointcutExpression.matchesMethodExecution(method);
return shadowMatch.alwaysMatches();
}
}
測試
public class AOPUtilsTest {
public static void main(String[] args) throws NoSuchMethodException {
// 定義表示式
String expression = "execution(* com.my.spring.test.aop.*.*(..))";
// 初始化工具類
AOPUtils.init(expression);
// 粗篩
boolean firstMatch = AOPUtils.firstMatch(ServiceImpl.class);
if(firstMatch){
System.out.println("第一次篩選通過");
// 正常情況應該是獲取所有方法進行遍歷,我這裡偷懶了~
Method doService = ServiceImpl.class.getDeclaredMethod("doService");
// 精篩
boolean lastMatch = AOPUtils.lastMatch(doService);
if(lastMatch){
System.out.println("第二次篩選通過");
}
else{
System.out.println("第二次篩選未通過");
}
}
else {
System.out.println("第一次篩選未通過");
}
}
}
結果(就不截圖了,懷疑的小夥伴可以自己試試~)
第一次篩選通過
第二次篩選通過
當我們新建一個類Test
,把切點表示式換成
execution(* com.my.spring.test.aop.Test.*(..))
測試結果為
第一次篩選未通過
再把切點表示式換成指定的方法
execution(* com.my.spring.test.aop.*.matchMethod(..))
結果
第一次篩選通過
第二次篩選未通過
到這裡,小夥伴們應該明白了AspectJ的使用方法吧
代理物件建立過程
接下來,我們就來看看Spring是如何使用AspectJ匹配出相應的advisor並建立代理物件的吧,以下為建立代理物件的大致路程圖
建立代理物件是在bean初始化後完成的,所以對應的beanPostProcessor
呼叫時機為postProcessAfterInitialization
AbstractAutoProxyCreator#postProcessAfterInitialization
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
// 獲取快取key值,其實就是beanName
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// 判斷快取中是否有該物件,有則說明該物件已被動態代理,跳過
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
wrapIfNecessary
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// 根據bean獲取到匹配的advisor
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
// 建立代理物件
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
return proxy;
}
return bean;
}
getAdvicesAndAdvisorsForBean
protected Object[] getAdvicesAndAdvisorsForBean(
Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
// 獲取合適的advisor
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
return advisors.toArray();
}
findEligibleAdvisors
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// 先獲取到所有的advisor, 這裡和解析過程相同,由於已經解析好,所以會直接從快取中取出
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// 篩選出匹配的advisor
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
// 增加一個預設的advisor
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
// 排序
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
findAdvisorsThatCanApply
protected List<Advisor> findAdvisorsThatCanApply(
List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
// 查詢匹配的advisor
return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
}
findAdvisorsThatCanApply
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz){
List<Advisor> eligibleAdvisors = new ArrayList<>();
for (Advisor candidate : candidateAdvisors) {
// 判斷是否匹配
if (canApply(candidate, clazz, hasIntroductions)) {
// 加入到合適的advisors集合中
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}
canApply
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pca = (PointcutAdvisor) advisor;
// 判斷是否匹配
return canApply(pca.getPointcut(), targetClass, hasIntroductions);
}
else {
// It doesn't have a pointcut so we assume it applies.
return true;
}
}
canApply
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
// 第一次篩選,對class篩選判斷是否滿足匹配條件
// 這裡將會初始化切點表示式
if (!pc.getClassFilter().matches(targetClass)) {
return false;
}
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
}
for (Class<?> clazz : classes) {
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
// 迴圈所有方法進行第二次篩選,判斷是否有方法滿足匹配條件
for (Method method : methods) {
if (introductionAwareMethodMatcher != null ?
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}
return false;
}
pc.getClassFilter()
public ClassFilter getClassFilter() {
obtainPointcutExpression();
return this;
}
obtainPointcutExpression
private PointcutExpression obtainPointcutExpression() {
if (this.pointcutExpression == null) {
// 確認類載入器
this.pointcutClassLoader = determinePointcutClassLoader();
// 建立切點表示式
this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);
}
return this.pointcutExpression;
}
buildPointcutExpression
private PointcutExpression buildPointcutExpression(@Nullable ClassLoader classLoader) {
// 初始化切點解析器
PointcutParser parser = initializePointcutParser(classLoader);
PointcutParameter[] pointcutParameters = new PointcutParameter[this.pointcutParameterNames.length];
for (int i = 0; i < pointcutParameters.length; i++) {
pointcutParameters[i] = parser.createPointcutParameter(
this.pointcutParameterNames[i], this.pointcutParameterTypes[i]);
}
// 使用切點解析器進行解析表示式獲取切點表示式
return parser.parsePointcutExpression(replaceBooleanOperators(resolveExpression()),
this.pointcutDeclarationScope, pointcutParameters);
}
initializePointcutParser
private PointcutParser initializePointcutParser(@Nullable ClassLoader classLoader) {
// 獲得切點解析器
PointcutParser parser = PointcutParser
.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(
SUPPORTED_PRIMITIVES, classLoader);
parser.registerPointcutDesignatorHandler(new BeanPointcutDesignatorHandler());
return parser;
}
pc.getClassFilter便是完成了以上事情,此時再進行呼叫matchs方法
public boolean matches(Class<?> targetClass) {
PointcutExpression pointcutExpression = obtainPointcutExpression();
// 使用切點表示式進行粗篩
return pointcutExpression.couldMatchJoinPointsInType(targetClass);
}
introductionAwareMethodMatcher.matches 同樣如此
以上便是尋找合適的advisor的過程,下面,就是通過這些advisor進行建立動態代理了
createProxy
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
// 將specificInterceptors(現在是Object)轉化為Advisor返回
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
// 賦值到proxyFactory的advisors屬性中
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
// 建立動態代理
return proxyFactory.getProxy(getProxyClassLoader());
}
proxyFactory.getProxy
public Object getProxy(@Nullable ClassLoader classLoader) {
// 建立代理物件
return createAopProxy().getProxy(classLoader);
}
createAopProxy
protected final synchronized AopProxy createAopProxy() {
// 建立AOP代理物件
return getAopProxyFactory().createAopProxy(this);
}
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// @EnableAspectJAutoProxy的proxyTargetClass是否配置為true
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
// 如何是介面則建立jdk動態代理
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// cglib動態代理
return new ObjenesisCglibAopProxy(config);
}
// 預設是jdk動態代理
else {
return new JdkDynamicAopProxy(config);
}
}
public Object getProxy(@Nullable ClassLoader classLoader) {
// 獲取到代理的介面
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
// 建立jdk代理,傳入的為JdkDynamicAopProxy物件,裡面包含了被代理的bean以及匹配的advisor
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
動態代理建立完成~
代理物件呼叫過程
物件都給你建立好了,接下當然是開..發起呼叫咯
以下是呼叫的大致流程圖
代理物件被呼叫的是invoke方法,我們所建立的代理物件為JdkDynamicAopProxy
,所以
JdkDynamicAopProxy#invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
// 取出包裝了被代理bean的物件->建立代理物件時的SingletonTargetSource, advised為ProxyFactory
TargetSource targetSource = this.advised.targetSource;
Object target = null;
// 拿到bean
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// 將所有advisor中的advice取出,並轉化為對應的interceptor
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// 建立一個最外層的MethodInvocation用於發起呼叫
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// 發起鏈式呼叫
Object retVal = invocation.proceed();
return retVal;
}
我們先看獲取interceptor的過程
getInterceptorsAndDynamicInterceptionAdvice
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
// 將所有advisor中的advice取出並封裝成intercept
return this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(this, method, targetClass);
}
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, @Nullable Class<?> targetClass) {
// 建立一個advisor介面卡的註冊器用於轉化advice,建立時將預設註冊三個介面卡
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
Advisor[] advisors = config.getAdvisors();
// 迴圈遍歷所有advisor
for (Advisor advisor : advisors) {
// 將advisor中的advice轉化為interceptor
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
return interceptorList;
}
}
GlobalAdvisorAdapterRegistry.getInstance() 類初始化時呼叫靜態方法
private static AdvisorAdapterRegistry instance = new DefaultAdvisorAdapterRegistry()
public static AdvisorAdapterRegistry getInstance() {
return instance;
}
public DefaultAdvisorAdapterRegistry() {
// 註冊三個介面卡
registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
registerAdvisorAdapter(new AfterReturningAdviceAdapter());
registerAdvisorAdapter(new ThrowsAdviceAdapter());
}
public void registerAdvisorAdapter(AdvisorAdapter adapter) {
// 將介面卡加入集合
this.adapters.add(adapter);
}
registry.getInterceptors 這裡麵包含了advice轉化成interceptor的過程
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
List<MethodInterceptor> interceptors = new ArrayList<>(3);
Advice advice = advisor.getAdvice();
// advice本身是否就是MethodInterceptor
if (advice instanceof MethodInterceptor) {
interceptors.add((MethodInterceptor) advice);
}
for (AdvisorAdapter adapter : this.adapters) {
// 判斷advice是哪個advice 如:(advice instanceof MethodBeforeAdvice)
if (adapter.supportsAdvice(advice)) {
// 將advice封裝到對應的interceptor
interceptors.add(adapter.getInterceptor(advisor));
}
}
return interceptors.toArray(new MethodInterceptor[0]);
}
若adapter為MethodBeforeAdviceAdapter
,則
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
其他advice轉化過程相同
以上,便將所有的advice轉化成了interceptor,接下來,則是經典的鏈式遞迴呼叫過程
以下過程小夥伴們可以對照流程圖閱讀,畢竟遞迴還是有些複雜,需要一定的功底
ReflectiveMethodInvocation#proceed
public Object proceed() throws Throwable {
// currentInterceptorIndex 初始值為-1
// 當currentInterceptorIndex等於advice的數量減一時,則呼叫目標方法
// 由於advice已排好序,所以呼叫順序為before, after, afterReturn, afterThrowing
// 注意,並非呼叫到相應的advice就會執行advice方法,這裡是類似遞迴呼叫的方式,會存在一個歸過程
// 有些是遞的時候發起呼叫,如beforeAdvice, 但有些則是歸的時候發起呼叫,如afterAdvice
// 遞迴的終止條件則是這下面這個return invokeJoinpoint();
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
// currentInterceptorIndex自增並獲取到interceptor
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
// 將interceptro強轉為MethodInterceptor發起呼叫
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
此時currentInterceptorIndex值為0,而我們的advice為4個(去除了預設的),所以當currentInterceptorIndex為3時便會呼叫我們的實際方法
首先呼叫的是MethodBeforeAdviceInterceptor
public Object invoke(MethodInvocation mi) throws Throwable {
// 呼叫前置通知
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
return mi.proceed();
}
mi為傳入的this,所有mi.proceed()將會回到最開始的方法
再次迴圈,此時currentInterceptorIndex值為1
呼叫的是AspectJAfterAdvice
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
finally {
// finally意味著不管怎樣都會被呼叫
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
繼續,此時currentInterceptorIndex值為2
呼叫的是AfterReturningAdviceInterceptor
public Object invoke(MethodInvocation mi) throws Throwable {
Object retVal = mi.proceed();
this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
return retVal;
}
繼續,此時currentInterceptorIndex值為3
呼叫的是AspectJAfterThrowingAdvice
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
catch (Throwable ex) {
if (shouldInvokeOnThrowing(ex)) {
// 呼叫異常通知
invokeAdviceMethod(getJoinPointMatch(), null, ex);
}
// 往外丟擲異常
throw ex;
}
}
所以如果我們的業務方法發生了異常,會呼叫到異常通知,而這裡又把異常往外拋,所以afterReturn就會被跳過直接到after的finally方法
現在currentInterceptorIndex值為3了,再回撥最初的方法中時,就會呼叫到我們的業務方法了。呼叫完畢則進行歸的過程,呼叫過程便結束了。
以上,便是整個AOP的過程了
本篇文章中涉及到圖片的向量圖地址為:https://www.processon.com/view/link/5fa8afdae401fd45d109f257,有需要的小夥伴可自取
下文預告:Spring原始碼分析之事務管理(上)
Spring 原始碼系列
- Spring原始碼分析之 IOC 容器預啟動流程(已完結)
- Spring原始碼分析之BeanFactory體系結構(已完結)
- Spring原始碼分析之BeanFactoryPostProcessor呼叫過程(已完結)
- Spring原始碼分析之Bean的建立過程(已完結)
- Spring原始碼分析之什麼是迴圈依賴及解決方案(已完結)
- Spring原始碼分析之AOP從解析到呼叫(已完結)
- Spring原始碼分析之事務管理(上),事物管理是spring作為容器的一個特點,總結一下他的基本實現與原理吧
- Spring原始碼分析之事務管理(下) ,關於他的底層事物隔離與事物傳播原理,重點分析一下
Spring Mvc 原始碼系列
- SpringMvc體系結構
- SpringMvc原始碼分析之Handler解析過程
- SpringMvc原始碼分析之請求鏈過程
另外筆者公眾號:奇客時間,有更多精彩的文章,有興趣的同學,可以關注