上一節咱們在分析解析AOP標籤的時候,第一步就是註冊了一個類AspectJAwareAdvisorAutoProxyCreator
,咱們說它是AOP的入口類。爲何這樣說呢? 來看它父類的父類AbstractAutoProxyCreator
,它繼承了BeanPostProcessor接口。 那麼,有兩個方法確定要被調用到postProcessBeforeInitialization、postProcessAfterInitialization
。一個在依賴注入完成以前調用,一個在以後調用。spring
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方法則是真正產生代理的地方,咱們先看下它的內部實現。bash
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// Create proxy if we have advice.
//先看它的註釋,大意說:若是有通知,就建立代理。
//其實就是在bean.getClass()找到全部的通知和advisor
//這裏面其實又分爲兩個步驟:
//第一,在Bean工廠找到全部的Advisor 第二,根據beanClass和Pointcut去作匹配
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
//真正建立代理並返回,這時候返回的就是代理類了,把真實的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;
}
複製代碼
看到上面的源碼,整個過程就分爲了兩步,匹配和建立返回。這樣就完成了代理類的替換,是否是有點偷樑換柱的感受。post
先是找到全部的Advisor,這個比較簡單。由於咱們知道,在解析的時候就把配置的通知封裝成advisor註冊了進去。首先拿到beanDefinitionNames容器全部beanName,循環判斷bean的類型是否是advisor接口的類型,符合條件返回。ui
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的實例
advisors.add(this.beanFactory.getBean(name, Advisor.class));
}
}
}
//返回的advisors就是配置文件中全部的advice和advisor
return advisors;
}
複製代碼
找到advisors並未結束,還要跟pointcut作匹配,看這些advisor符不符合表達式條件。它最終調用到Pointcut的match方法。這個方法嵌套的太深,就不貼代碼了,核心思想就是判斷class和class對應的method是否與pointcut表達式匹配。this
通過上面查找匹配後,肯定當前的bean確實須要代理,就調用createProxy方法。lua
protected Object createProxy(Class<?> beanClass,
String beanName, Object[] specificInterceptors, TargetSource targetSource) {
//建立代理工廠
ProxyFactory proxyFactory = new ProxyFactory();
// Copy our properties (proxyTargetClass etc) inherited from ProxyConfig.
proxyFactory.copyFrom(this);
//將beanClass上的接口設置到代理工廠
evaluateProxyInterfaces(beanClass, proxyFactory);
//設置Advisor到代理工廠
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
for (Advisor advisor : advisors) {
proxyFactory.addAdvisor(advisor);
}
//設置目標對象
proxyFactory.setTargetSource(targetSource);
return proxyFactory.getProxy(this.proxyClassLoader);
}
複製代碼
建立代理分爲JDK的代理和Cglib的代理,這裏咱們先關注JDK的代理。getProxy方法就調用到JdkDynamicAopProxy
類的方法。這個類還實現了InvocationHandler接口,說明它同時仍是調用處理程序。即在調用代理類的invoke方法時,實際上就會調用到JdkDynamicAopProxy.invoke()
。spa
public Object getProxy(ClassLoader classLoader) {
//這裏又給代理工廠加了兩個接口 SpringProxy和Advised
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
//這個方法就比較熟悉了,正是JDK動態代理的方法
//這裏的this就是JdkDynamicAopProxy實例。
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
複製代碼
在實例化Bean和完成依賴注入後,會判斷當前的Bean是否須要代理,若是須要,就生成代理類把原始類替換掉。在業務方法裏面調用的時候,就會調用到JdkDynamicAopProxy.invoke()
。debug
在執行invoke的時候,咱們又能夠分爲兩個步驟...-_-||,是否是跟二頗有緣,每次都是兩個步驟。。代理
首先從代理工廠中拿到全部的advisor,而後判斷是否是PointcutAdvisor類型,其次先matches一下targetClass,再matches一下method,證實這個類的方法在pointcut範圍內,加入interceptorList,最後返回。code
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, Class<?> targetClass) {
List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
//config就是代理工廠的實例
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;
}
複製代碼
拿到方法的攔截鏈,而後調用。調用的時候有個地方比較有意思。它先建立了一個對象ReflectiveMethodInvocation
。這個對象有兩個參數
currentInterceptorIndex //當前調用的攔截器的索引,默認值-1
interceptorsAndDynamicMethodMatchers //攔截器的列表
複製代碼
而後看它的調用方法。
public Object proceed() throws Throwable {
//若是倆個變量相等,說明已經調用完了全部的攔截器
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
//執行被代理方法
return invokeJoinpoint();
}
//從-1開始,每次累加1。interceptorOrInterceptionAdvice就是對應的每個通知
//好比before、after、after-returning...
//對應的實例類分別是:
//MethodBeforeAdviceInterceptor、AspectJAfterAdvice、AfterReturningAdviceInterceptor
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
//在調用具體通知的invoke方法時,把當前對象當參數傳了過去。
//由於在具體的通知執行以後還要回調回來,執行當前對象的proceed().
//這樣就造成了一個通知調用鏈,當全部的通知執行完畢,調用上面的invokeJoinpoint()
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
複製代碼
看完它的調用流程,咱們還有一個疑問。通知一共有5種類型,前置通知、後置通知、方法返回後通知、環繞通知、異常通知。那麼,它是怎麼保證調用順序的呢? 咱們來看兩個通知類裏面具體的實現。
public Object invoke(MethodInvocation mi) throws Throwable {
//這個比較簡單,執行完before就回調
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
return mi.proceed();
}
複製代碼
public Object invoke(MethodInvocation mi) throws Throwable {
//這個就比較有趣了,它先執行回調
//經過finally機制再執行本身的通知方法
try {
return mi.proceed();
}
finally {
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
複製代碼
//環繞通知會調用到切面類的自定義方法
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("環繞通知以前");
//能夠本身判斷還要不要回調回去。
//回調回去的話,接着循環調用鏈
//若是再也不回調,就要看鏈的順序了,在它以後的就再也不執行
Object result = joinPoint.proceed();
System.out.println("環繞通知以後");
return result;
}
複製代碼
若是想使用註解AOP,須要開啓一個配置。<aop:aspectj-autoproxy />
。Spring解析配置標籤的時候,調用到AspectJAutoProxyBeanDefinitionParser.parse()
。 這個方法主要就幹了一件事:註冊入口類AnnotationAwareAspectJAutoProxyCreator
。 XML配置方式的AOP,Spring註冊的入口類叫作AspectJAwareAdvisorAutoProxyCreator
。
它們的調用流程是同樣的,都是在實例化以後調用爺爺類的AbstractAutoProxyCreator.postProcessAfterInitialization()
。回憶一下,wrapIfNecessary方法是真正產生代理的地方。它先獲取全部的通知並與當前的bean class匹配,若是有,就說明當前的Bean須要代理,則產生代理類。咱們知道,在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個步驟。
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
for (String beanName : beanNames) {
Class<?> beanType = this.beanFactory.getType(beanName);
if (this.advisorFactory.isAspect(beanType)) {
aspectNames.add(beanName);
......
}
}
複製代碼
先拿到Class對象上的全部Method對象,根據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對象返回。返回以後,建立代理類進行替換。
由此看來,註解方式的AOP,是在查詢Advisors的時候纔去解析並生成的,其餘的與XML的配置方式處理流程都是同樣的。
本章節咱們重點闡述了3個問題。即怎樣產生代理類、代理類的執行過程、AnnotationAOP的處理流程。結合本章節和上一章節的內容,可能使咱們對Spring AOP的內部處理流程加深了印象。
怎樣產生代理類? 經過循環beanNames實例化Bean對象,判斷此對象是否與pointcut表達式匹配。若是匹配就根據advice生成不一樣的advisor對象,而後調用JDK或者CGLIB的方法生成代理類返回。
代理類執行 調用JDK或者CGLIB的invoke方法,查詢advisor的調用鏈。鏈式調用,根據通知類型調用不一樣的advice實現加強。