1于样、AOP的入口
上一節(jié)我們在分析解析AOP標(biāo)簽的時候,第一步就是注冊了一個類AspectJAwareAdvisorAutoProxyCreator
掩驱,我們說它是AOP的入口類芒划。為什么這樣說呢?
來看它父類的父類AbstractAutoProxyCreator
欧穴,它繼承了BeanPostProcessor接口民逼。
那么,有兩個方法肯定要被調(diào)用到postProcessBeforeInitialization苔可、postProcessAfterInitialization
缴挖。一個在依賴注入完成之前調(diào)用,一個在之后調(diào)用焚辅。
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
wrapIfNecessary方法則是真正產(chǎn)生代理的地方映屋,我們先看下它的內(nèi)部實(shí)現(xiàn)。
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// Create proxy if we have advice.
//先看它的注釋同蜻,大意說:如果有通知棚点,就創(chuàng)建代理。
//其實(shí)就是在bean.getClass()找到所有的通知和advisor
//這里面其實(shí)又分為兩個步驟:
//第一湾蔓,在Bean工廠找到所有的Advisor 第二瘫析,根據(jù)beanClass和Pointcut去做匹配
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
//真正創(chuàng)建代理并返回,這時候返回的就是代理類了,把真實(shí)的bean替換掉
Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
看到上面的源碼贬循,整個過程就分為了兩步咸包,匹配和創(chuàng)建返回。這樣就完成了代理類的替換杖虾,是不是有點(diǎn)偷梁換柱的感覺烂瘫。
匹配
先是找到所有的Advisor,這個比較簡單奇适。因為我們知道坟比,在解析的時候就把配置的通知封裝成advisor注冊了進(jìn)去。首先拿到beanDefinitionNames容器所有beanName嚷往,循環(huán)判斷bean的類型是不是advisor接口的類型葛账,符合條件返回。
public List<Advisor> findAdvisorBeans() {
// Determine list of advisor bean names, if not cached already.
String[] advisorNames = null;
//先拿到beanName
advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Advisor.class, true, false);
List<Advisor> advisors = new LinkedList<Advisor>();
for (String name : advisorNames) {
if (isEligibleBean(name)) {
try {
//再從Bean工廠中拿bean的實(shí)例
advisors.add(this.beanFactory.getBean(name, Advisor.class));
}
}
}
//返回的advisors就是配置文件中所有的advice和advisor
return advisors;
}
找到advisors并未結(jié)束皮仁,還要跟pointcut做匹配籍琳,看這些advisor符不符合表達(dá)式條件。它最終調(diào)用到Pointcut的match方法魂贬。這個方法嵌套的太深巩割,就不貼代碼了,核心思想就是判斷class和class對應(yīng)的method是否與pointcut表達(dá)式匹配付燥。
創(chuàng)建代理
經(jīng)過上面查找匹配后宣谈,確定當(dāng)前的bean確實(shí)需要代理,就調(diào)用createProxy方法键科。
- 設(shè)置代理工廠
protected Object createProxy(Class<?> beanClass,
String beanName, Object[] specificInterceptors, TargetSource targetSource) {
//創(chuàng)建代理工廠
ProxyFactory proxyFactory = new ProxyFactory();
// Copy our properties (proxyTargetClass etc) inherited from ProxyConfig.
proxyFactory.copyFrom(this);
//將beanClass上的接口設(shè)置到代理工廠
evaluateProxyInterfaces(beanClass, proxyFactory);
//設(shè)置Advisor到代理工廠
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
for (Advisor advisor : advisors) {
proxyFactory.addAdvisor(advisor);
}
//設(shè)置目標(biāo)對象
proxyFactory.setTargetSource(targetSource);
return proxyFactory.getProxy(this.proxyClassLoader);
}
- 創(chuàng)建
創(chuàng)建代理分為JDK的代理和Cglib的代理闻丑,這里我們先關(guān)注JDK的代理。getProxy方法就調(diào)用到JdkDynamicAopProxy
類的方法勋颖。這個類還實(shí)現(xiàn)了InvocationHandler接口嗦嗡,說明它同時還是調(diào)用處理程序。即在調(diào)用代理類的invoke方法時饭玲,實(shí)際上就會調(diào)用到JdkDynamicAopProxy.invoke()
侥祭。
public Object getProxy(ClassLoader classLoader) {
//這里又給代理工廠加了兩個接口 SpringProxy和Advised
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
//這個方法就比較熟悉了,正是JDK動態(tài)代理的方法
//這里的this就是JdkDynamicAopProxy實(shí)例茄厘。
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
2矮冬、代理類的調(diào)用
在實(shí)例化Bean和完成依賴注入后,會判斷當(dāng)前的Bean是否需要代理次哈,如果需要胎署,就生成代理類把原始類替換掉。在業(yè)務(wù)方法里面調(diào)用的時候窑滞,就會調(diào)用到JdkDynamicAopProxy.invoke()
琼牧。
在執(zhí)行invoke的時候恢筝,我們又可以分為兩個步驟...-_-||,是不是跟二很有緣巨坊,每次都是兩個步驟撬槽。。
- 獲取方法的攔截鏈
首先從代理工廠中拿到所有的advisor抱究,然后判斷是不是PointcutAdvisor類型恢氯,其次先matches一下targetClass,再matches一下method鼓寺,證明這個類的方法在pointcut范圍內(nèi),加入interceptorList勋磕,最后返回妈候。
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, Class<?> targetClass) {
List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
//config就是代理工廠的實(shí)例
for (Advisor advisor : config.getAdvisors()) {
if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
if (MethodMatchers.matches(mm, method, targetClass, hasIntroductions)) {
interceptorList.addAll(Arrays.asList(interceptors));
}
}
}
}
return interceptorList;
}
- 調(diào)用
拿到方法的攔截鏈,然后調(diào)用挂滓。調(diào)用的時候有個地方比較有意思苦银。它先創(chuàng)建了一個對象ReflectiveMethodInvocation
。這個對象有兩個參數(shù)
currentInterceptorIndex //當(dāng)前調(diào)用的攔截器的索引,默認(rèn)值-1
interceptorsAndDynamicMethodMatchers //攔截器的列表
然后看它的調(diào)用方法赶站。
public Object proceed() throws Throwable {
//如果倆個變量相等幔虏,說明已經(jīng)調(diào)用完了所有的攔截器
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
//執(zhí)行被代理方法
return invokeJoinpoint();
}
//從-1開始,每次累加1贝椿。interceptorOrInterceptionAdvice就是對應(yīng)的每一個通知
//比如before想括、after、after-returning...
//對應(yīng)的實(shí)例類分別是:
//MethodBeforeAdviceInterceptor烙博、AspectJAfterAdvice瑟蜈、AfterReturningAdviceInterceptor
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
//在調(diào)用具體通知的invoke方法時,把當(dāng)前對象當(dāng)參數(shù)傳了過去渣窜。
//因為在具體的通知執(zhí)行之后還要回調(diào)回來铺根,執(zhí)行當(dāng)前對象的proceed().
//這樣就形成了一個通知調(diào)用鏈,當(dāng)所有的通知執(zhí)行完畢乔宿,調(diào)用上面的invokeJoinpoint()
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
看完它的調(diào)用流程位迂,我們還有一個疑問。通知一共有5種類型详瑞,前置通知掂林、后置通知、方法返回后通知蛤虐、環(huán)繞通知党饮、異常通知。那么驳庭,它是怎么保證調(diào)用順序的呢刑顺?
我們來看兩個通知類里面具體的實(shí)現(xiàn)氯窍。
前置通知
public Object invoke(MethodInvocation mi) throws Throwable {
//這個比較簡單,執(zhí)行完before就回調(diào)
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
return mi.proceed();
}
后置通知
public Object invoke(MethodInvocation mi) throws Throwable {
//這個就比較有趣了蹲堂,它先執(zhí)行回調(diào)
//通過finally機(jī)制再執(zhí)行自己的通知方法
try {
return mi.proceed();
}
finally {
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
環(huán)繞通知
//環(huán)繞通知會調(diào)用到切面類的自定義方法
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("環(huán)繞通知之前");
//可以自己判斷還要不要回調(diào)回去狼讨。
//回調(diào)回去的話,接著循環(huán)調(diào)用鏈
//如果不再回調(diào)柒竞,就要看鏈的順序了政供,在它之后的就不再執(zhí)行
Object result = joinPoint.proceed();
System.out.println("環(huán)繞通知之后");
return result;
}
3、基于注解的AOP
如果想使用注解AOP朽基,需要開啟一個配置布隔。<aop:aspectj-autoproxy />
。Spring解析配置標(biāo)簽的時候稼虎,調(diào)用到AspectJAutoProxyBeanDefinitionParser.parse()
衅檀。
這個方法主要就干了一件事:注冊入口類AnnotationAwareAspectJAutoProxyCreator
。
XML配置方式的AOP霎俩,Spring注冊的入口類叫做AspectJAwareAdvisorAutoProxyCreator
哀军。
它們的調(diào)用流程是一樣的,都是在實(shí)例化之后調(diào)用爺爺類的AbstractAutoProxyCreator.postProcessAfterInitialization()
打却∩际剩回憶一下,wrapIfNecessary方法是真正產(chǎn)生代理的地方柳击。它先獲取所有的通知并與當(dāng)前的bean class匹配猿推,如果有,就說明當(dāng)前的Bean需要代理腻暮,則產(chǎn)生代理類彤守。我們知道,在XML配置方式的AOP中哭靖,Spring把配置的通知都封裝成advisor注冊到容器里具垫,所以在獲取的時候,直接在Bean工廠中匹配Advisor類型的Bean就行试幽。
但是筝蚕,在解析注解AOP的時候,我們看到它只是注冊了一個入口類而已呀铺坞,并沒有注冊advisor起宽,那么,在這里怎么獲取呢济榨?
目光回到查詢advisor的方法坯沪。
protected List<Advisor> findCandidateAdvisors() {
// Add all the Spring advisors found according to superclass rules.
// 這個是查詢XML配置方式的advoisor
List<Advisor> advisors = super.findCandidateAdvisors();
// Build Advisors for all AspectJ aspects in the bean factory.
// 這個就是查詢注解方式的advisor
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
return advisors;
}
在buildAspectJAdvisors
方法查詢advisor的時候,它大致可以分3個步驟擒滑。
從Bean工廠獲取所有的beanName
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
循環(huán)beanNames腐晾,判斷bean是否包含Aspect注解
for (String beanName : beanNames) {
Class<?> beanType = this.beanFactory.getType(beanName);
if (this.advisorFactory.isAspect(beanType)) {
aspectNames.add(beanName);
......
}
}
- 獲取Advisors叉弦。先拿到Class對象上的所有Method對象,根據(jù)Method對象的Annotation類型藻糖,返回不同的Advice對象淹冰。這個跟XML方式返回的Advice對象是一樣的。
switch (aspectJAnnotation.getAnnotationType()) {
case AtBefore:
springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, ajexp, aif);
break;
case AtAfter:
springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, ajexp, aif);
break;
case AtAfterReturning:
springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, ajexp, aif);
AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterReturningAnnotation.returning())) {
springAdvice.setReturningName(afterReturningAnnotation.returning());
}
break;
case AtAfterThrowing:
springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, ajexp, aif);
AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
}
break;
case AtAround:
springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, ajexp, aif);
break;
case AtPointcut:
if (logger.isDebugEnabled()) {
logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
}
return null;
default:
throw new UnsupportedOperationException(
"Unsupported advice type on method " + candidateAdviceMethod);
}
最后將advice和pointcut封裝成InstantiationModelAwarePointcutAdvisorImpl對象返回巨柒。返回之后樱拴,創(chuàng)建代理類進(jìn)行替換。
由此看來洋满,注解方式的AOP晶乔,是在查詢Advisors的時候才去解析并生成的,其他的與XML的配置方式處理流程都是一樣的芦岂。
4瘪弓、 總結(jié)
本章節(jié)我們重點(diǎn)闡述了3個問題。即怎樣產(chǎn)生代理類禽最、代理類的執(zhí)行過程、AnnotationAOP的處理流程袱饭。結(jié)合本章節(jié)和上一章節(jié)的內(nèi)容川无,可能使我們對Spring AOP的內(nèi)部處理流程加深了印象。
怎樣產(chǎn)生代理類虑乖?
通過循環(huán)beanNames實(shí)例化Bean對象懦趋,判斷此對象是否與pointcut表達(dá)式匹配。如果匹配就根據(jù)advice生成不同的advisor對象疹味,然后調(diào)用JDK或者CGLIB的方法生成代理類返回仅叫。代理類執(zhí)行
調(diào)用JDK或者CGLIB的invoke方法,查詢advisor的調(diào)用鏈糙捺。鏈?zhǔn)秸{(diào)用诫咱,根據(jù)通知類型調(diào)用不同的advice實(shí)現(xiàn)增強(qiáng)。