在Spring中AOP是咱們使用的很是頻繁的一個特性。經過AOP咱們能夠補足一些面向對象編程中不足或難以實現的部分。html
首先在學習源碼以前咱們須要瞭解關於AOP的相關概念如切點切面等,以及如何使用AOP,這裏能夠看我以前的文章:Spring系列之AOP的原理及手動實現java
對於Java這種面嚮對象語言來講任何功能的實現都是依賴於對象,AOP也不例外。spring
首先咱們先來準備好在配置文件中配置好AOP相關的屬性。express
spring.xml編程
<bean id="aspect" class="cn.javass.spring.chapter6.aop.HelloWorldAspect"/> <bean id="beforeAdvice" class="cn.javass.spring.chapter6.aop.BeforeAdviceImpl"/> <aop:aspectj-autoproxy/> <aop:config> <aop:pointcut id="pointcutA" expression="execution(* cn.javass..*.sayAfterReturning(..))"></aop:pointcut> <aop:advisor id="advisor" pointcut="execution(* cn.javass..*.sayAdvisorBefore(..))" advice-ref="beforeAdvice"/> <aop:aspect id="aspects" ref="aspect"> <aop:before pointcut="execution(* cn.javass..*.sayBefore(..)) and args(param)" method="beforeAdvice(java.lang.String)" arg-names="param"/> <aop:after-returning pointcut-ref="pointcutA" method="afterReturningAdvice" arg-names="retVal" returning="retVal"/> <aop:after-throwing pointcut="execution(* cn.javass..*.sayAfterThrowing(..))" method="afterThrowingAdvice" arg-names="exception" throwing="exception"/> <aop:after pointcut="execution(* cn.javass..*.sayAfterFinally(..))" method="afterFinallyAdvice"/> <aop:around pointcut="execution(* cn.javass..*.sayAround(..))" method="aroundAdvice"/> </aop:aspect> </aop:config>
在上面的配置中建立了幾種不一樣的advice。這些配置在spring啓動時會被相應的建立爲對象。app
在前面IOC文章中咱們有提到在Spring中經過spi的機制來肯定解析配置文件中不一樣標籤的解析類。函數
在aop包中找到spring.handlers文件:post
http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
能夠肯定的是處理aop相關標籤的就是AopNamespaceHandler
這個類。學習
public void init() { this.registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser()); this.registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser()); this.registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator()); this.registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser()); }
在AopNamespaceHandler
中除了構造函數就只有上面的init方法,上面代碼就是註冊解析不一樣標籤的解析器的BeanDefinition。咱們須要關注的是aspectj-autoproxy
標籤的解析器類AspectJAutoProxyBeanDefinitionParser
。優化
#### AopConfigUtils#registerOrEscalateApcAsRequired 跟蹤```AspectJAutoProxyBeanDefinitionParser```的parse方法最終會進入到```AopConfigUtils#registerOrEscalateApcAsRequired```中。
private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) { BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME); if (!cls.getName().equals(apcDefinition.getBeanClassName())) { int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName()); int requiredPriority = findPriorityForClass(cls); if (currentPriority < requiredPriority) { apcDefinition.setBeanClassName(cls.getName()); } } return null; } RootBeanDefinition beanDefinition = new RootBeanDefinition(cls); beanDefinition.setSource(source); beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE); beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition); return beanDefinition; }
這裏的常量```AUTO_PROXY_CREATOR_BEAN_NAME = "org.springframework.aop.config.internalAutoProxyCreator";``` 首先要明白的是internalAutoProxyCreator並非spring中的一個實際的類,AUTO_PROXY_CREATOR_BEAN_NAME是一個用於建立aop類的Beandefinition的名字。 在上面的代碼邏輯中若是AUTO_PROXY_CREATOR_BEAN_NAME表示的Beandefinition已經存在則判斷新須要註冊的類其優先級和已經存在的類定義進行比較,若是新須要註冊的優先級較高則進行替換。 若是不存在已經註冊的Beandefinition則將其進行註冊。被註冊的Beandefinition表示的類爲```AspectJAwareAdvisorAutoProxyCreator```。 #### 完成aop功能須要建立的對象 在前面IOC文章中分析過了在解析完配置文件後須要建立的對象都會將其BeanDefinition註冊到IOC容器中,因此咱們能夠將斷點設置在配置文件解析完成以後就能夠看到須要建立那些對象了。 ![須要建立的對象](https://user-gold-cdn.xitu.io/2019/7/5/16bc0e4a57f8a819?w=789&h=405&f=png&s=22442) 如上圖```helloWorldService```就是須要被加強的類。
public interface IHelloWorldService {
void sayHello(); void sayBefore(String param); boolean sayAfterReturning(); void sayAfterThrowing(); boolean sayAfterFinally(); void sayAround(String param); void sayAdvisorBefore(String param);
}
而```aspect,beforeAdvice,pointcutA,advisor```都是咱們在配置文件中配置過的,是切點,切面和處理方法的實現類。```org.springframework.aop.config.internalAutoProxyCreator```是上面分析過的用於建立aop代理的實現類。 然後面的以```org.springframework.aop.aspectj.AspectJPointcutAdvisor```開頭的幾個類實際上就是包含了切點和通知的一個切面的實現類,也就是它來決定哪些類須要被加強。 ![加強實現類](https://user-gold-cdn.xitu.io/2019/7/5/16bc0e4a582a62c7?w=785&h=223&f=png&s=15964) ### 功能加強 #### 加強時機 若是看過前面手寫aop文章的同窗應該知道當時咱們分析aop加強時機時有說過aop的加強功能其實是依賴於動態代理實現的。而動態代理若是要對一個對象進行加強那麼首先須要持有該對象才行。 因此咱們在對對象進行加強的前提是該對象已經被建立完成以後。並且咱們要清楚的是一個類對象被加強後咱們全部須要使用該對象的地方都應該使用該對象,這樣就肯定了類加強的時機必定是在類對象建立以後而且在完成注入以前。 #### AspectJAwareAdvisorAutoProxyCreator 前面有說過建立代理對象其實是經過```AspectJAwareAdvisorAutoProxyCreator```來完成,先來了解一下該類,查看該類的繼承體系。 ![繼承體系](https://user-gold-cdn.xitu.io/2019/7/5/16bc0e4a5a031b8b?w=639&h=215&f=png&s=21661) 能夠看到實際上該類自己仍是一個BeanPostProcessor,那麼能夠確定的是咱們只要找到執行BeanPostProcessor的地方而且是在實例化後執行的地方便可。通過調試後定位到```AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization```方法。
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) { result = beanProcessor.postProcessAfterInitialization(result, beanName); if (result == null) { return result; } } return result; }
這裏是對後置處理器進行遍歷,對於aop咱們須要關注的是```AspectJAwareAdvisorAutoProxyCreator```這一個處理器。 ##### wrapIfNecessary
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { //more code // 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; }
這裏去掉了前面一些代碼,getAdvicesAndAdvisorsForBean方法是用來獲取和當前對象匹配的切面。這裏獲取相匹配的切面類是經過```AbstractAdvisorAutoProxyCreator```來實現。
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) { List<Advisor> candidateAdvisors = findCandidateAdvisors(); List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); extendAdvisors(eligibleAdvisors); if (!eligibleAdvisors.isEmpty()) { //根據在配置文件中配置的order屬性或者註解@order()進行從小到大的排序 //order的值越小其優先級越高 eligibleAdvisors = sortAdvisors(eligibleAdvisors); } return eligibleAdvisors; }
首先獲取到全部的切面類,而後經過```AopUtils.findAdvisorsThatCanApply```方法來肯定哪些類可以匹配。 ##### AopUtils.findAdvisorsThatCanApply
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) { if (candidateAdvisors.isEmpty()) { return candidateAdvisors; } List<Advisor> eligibleAdvisors = new LinkedList<Advisor>(); for (Advisor candidate : candidateAdvisors) { if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) { eligibleAdvisors.add(candidate); } } boolean hasIntroductions = !eligibleAdvisors.isEmpty(); for (Advisor candidate : candidateAdvisors) { if (candidate instanceof IntroductionAdvisor) { // already processed continue; } if (canApply(candidate, clazz, hasIntroductions)) { eligibleAdvisors.add(candidate); } } return eligibleAdvisors; } public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) { if (advisor instanceof IntroductionAdvisor) { return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass); } else 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; } }
實現邏輯很簡單,遍歷全部的advisor調用canApply肯定是否匹配。 切面是有切入點和通知組成,切入點用來肯定哪些對象須要被加強,而通知決定如何進行加強。因此很明顯這裏肯定類對象是否匹配是由切入點(pointCut)決定的。 咱們先來看一下切入點是什麼。
public interface Pointcut {
//類過濾器 用於肯定類對象是否匹配 只有當類對象匹配後才進行方法的匹配
ClassFilter getClassFilter();
//方法匹配器 用於肯定具體哪一些方法須要被加強。 MethodMatcher getMethodMatcher(); //生成一個pointcut對象實例 Pointcut TRUE = TruePointcut.INSTANCE;
}
上面咱們能夠看到實際上就是經過ClassFilter和MethodMatcher相互配合來實現的,具體的實現過程會由於實現方式大同小異。其中實現方式包括好比正則匹配,AspectJ匹配等,在咱們以前的手寫系列中就是經過正則來進行匹配的,這裏匹配的實現不深刻探討。 經過上面的邏輯即可以肯定好加強該類會用到哪些advisor。 #### createProxy 當肯定好須要用到的advisor和其順序後就開始進行建立代理對象了。建立代理對象的方法由前面提到的```wrapIfNecessary```來調用```createProxy```方法實現。
protected Object createProxy(Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) { ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.copyFrom(this); if (!proxyFactory.isProxyTargetClass()) { if (shouldProxyTargetClass(beanClass, beanName)) { proxyFactory.setProxyTargetClass(true); } else { evaluateProxyInterfaces(beanClass, proxyFactory); } } Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); for (Advisor advisor : advisors) { proxyFactory.addAdvisor(advisor); } proxyFactory.setTargetSource(targetSource); customizeProxyFactory(proxyFactory); proxyFactory.setFrozen(this.freezeProxy); if (advisorsPreFiltered()) { proxyFactory.setPreFiltered(true); } return proxyFactory.getProxy(getProxyClassLoader()); }
建立實際上市代理經過代理工廠類(ProxyFactory)實現的。 ##### JDK代理仍是cglib 在建立代理對象時須要肯定使用JDK代理仍是cglib代理,前面有提到過若是在配置文件中配置了```proxy-target-class="true"```的話那麼就只會使用cglib進行代理。可是若是沒有配置的話則須要經過實際狀況來決定是JDK代理仍是cglib。 而除了```proxy-target-class```外,咱們實際上還能夠配置一個屬性```optimize```,該屬性默認值爲false,若是咱們將其置爲true那麼就表示容許spring對代理生成策略進行優化,意思就是若是該類有接口,就代理接口(使用JDK代理);若是沒有接口,就代理類(使用CGLIB代理)。而不是像若是隻配置proxyTargetClass=true時強制代理類,而不去考慮代理接口的方式。 綜上在spring中使用代理方式的策略以下: - 若是沒有配置```optimize```和```proxy-target-class```而且該類實現了接口,那麼使用JDK動態代理。 - 若是沒有配置```optimize```和```proxy-target-class```而且該類沒有實現接口,那麼使用cglib動態代理。 - 若是配置了```optimize```和```proxy-target-class```而且該類實現了接口,那麼使用JDK動態代理。 - 若是配置了```optimize```和```proxy-target-class```而且該類沒有實現接口,那麼使用cglib動態代理。 實現代碼以下:
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { 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."); } if (targetClass.isInterface()) { return new JdkDynamicAopProxy(config); } return new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); } }
如今已經取到了建立代理對象的策略和目標對象,就能夠直接建立代理對象了。若是對這放面有興趣的能夠自行搜索。 建立好代理對象以後使用代理對象替代以前建立好的對象,那麼在使用的時候就會調用加強後的方法完成功能。 #### 多個advisor如何肯定順序 在實際開發過程當中,可能會存在一個方法被多個advisor加強,可能有的在方法執行前加強有的在方法執行後進行加強。那麼在spring中如何肯定每個加強方法的調用時機保證不會出問題的呢? 在手寫aop系列中有講過這個問題,當時咱們是經過[責任鏈模式](https://www.runoob.com/design-pattern/chain-of-responsibility-pattern.html)來解決這個問題。實際上spring中就是經過責任鏈模式來解決該問題的。 在```JdkDynamicAopProxy```的```invoke```方法中,會經過getInterceptorsAndDynamicInterceptionAdvice方法來獲取加強當前調用方法的全部advisor的chain,可是須要注意的是這個chain並非根據實際應該的執行順序排列的。僅僅只是全部會被執行的加強方法的集合。
List