因爲在本人實際應用中使用的是註解配置AOP,也更傾向於瞭解Spring AOP的整個實現,而不單單是關鍵實現。因而本篇源碼解析,將會從註解開始。瞭解Spring AOP是怎麼掃描Aspect配置,匹配,並生成AOP代理的。html
註解@Aspect定了一個類爲AOP的配置。那麼,便從@Aspect的源碼引用開始吧。java
先從源碼中找有引用到@Aspect,用來判斷Class是否有該註解的代碼。找到方法。spring
/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactory.java ... @Override public boolean isAspect(Class<?> clazz) { return (hasAspectAnnotation(clazz) && !compiledByAjc(clazz)); } private boolean hasAspectAnnotation(Class<?> clazz) { return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null); } /** * We need to detect this as "code-style" AspectJ aspects should not be * interpreted by Spring AOP. */ private boolean compiledByAjc(Class<?> clazz) { // The AJTypeSystem goes to great lengths to provide a uniform appearance between code-style and // annotation-style aspects. Therefore there is no 'clean' way to tell them apart. Here we rely on // an implementation detail of the AspectJ compiler. for (Field field : clazz.getDeclaredFields()) { if (field.getName().startsWith(AJC_MAGIC)) { return true; } } return false; } ...
isAspect(Class<?> clazz)用來判斷clazz對象是否包含Aspect.class註解而且未被AspectJ編譯過,其中hasAspectAnnotation(Class<?> clazz)內容很明顯就很少提。卻是compiledByAjc(Class<?> clazz)的實現比較特別。它的實現是用字段前綴來判斷是否爲"code-style" aspects,看起來是一種比較Hack的方法。緩存
這裏我有一個疑惑點,就是"code-style" aspects和"annotation-style" aspects的具體所指,查了一圈也沒有看到明確的解釋。只在 IDEA的幫助文檔 Overview of AspectJ support這段上有看到相關的解釋。個人理解是"code-style"是由AspectJ Language所定義的aspect,會由AspectJ來編譯,而"annotation-style"則是由使用了@Aspect註解的Java語言所定義的aspect,若有錯誤煩請指出。
經過一步步閱讀和調試,能夠一層一層向上找到org/springframework/aop/framework/autoproxy/AbstractAutoProxyCreator.java中有兩個入口。app
postProcessBeforeInstantiation和postProcessAfterInitialization,是Bean生命週期中的兩個步驟:ide
/org/springframework/aop/framework/autoproxy/AbstractAutoProxyCreator.java ... @Override public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException { Object cacheKey = getCacheKey(beanClass, beanName); if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) { if (this.advisedBeans.containsKey(cacheKey)) { return null; } if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return null; } } // Create proxy here if we have a custom TargetSource. // Suppresses unnecessary default instantiation of the target bean: // The TargetSource will handle target instances in a custom fashion. TargetSource targetSource = getCustomTargetSource(beanClass, beanName); if (targetSource != null) { if (StringUtils.hasLength(beanName)) { this.targetSourcedBeans.add(beanName); } Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource); Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } return null; } ...
這個方法的目的是將含有custom TargetSource的bean進行加強處理。可分爲兩部份,前半部分利用緩存和幾個方法判斷是否須要加強。後半部分則進入主題判斷是否含有custom TargetSource。不過這裏我對custom TargetSource不是特別理解,也沒有細看,由於經過@Aspect註解配置不會執行這裏面的代碼,留着之後有時間再看。
這裏還有另外兩個方法:post
/org/springframework/aop/framework/autoproxy/AbstractAutoProxyCreator.java .... /** * Create a proxy with the configured interceptors if the bean is * identified as one to proxy by the subclass. * @see #getAdvicesAndAdvisorsForBean */ @Override public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (!this.earlyProxyReferences.contains(cacheKey)) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; } /** * Wrap the given bean if necessary, i.e. if it is eligible for being proxied. * @param bean the raw bean instance * @param beanName the name of the bean * @param cacheKey the cache key for metadata access * @return a proxy wrapping the bean, or the raw bean instance as-is */ protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) { return bean; } if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { return bean; } if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } // Create proxy if we have advice. Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); 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; }
能夠看到關鍵內容就在wrapIfNecessary裏面。顧名思義:必要時轉成AOP代理。前半部分判斷是不是不須要加強的,跟postProcessBeforeInstantiation的前半部分有點相似。後半部分根據是否有合適的Advice方法,有則將Bean轉成代理。
好了,這裏其實就是整個流程最關鍵的兩個地方了:this
這兩個方法的具體內容,將在接下來的文章介紹idea