1. 程式人生 > >Spring系列.AOP原理簡析

Spring系列.AOP原理簡析

## Spring AOP使用簡介 Spring的兩大核心功能是IOC和AOP。當我們使用Spring的AOP功能時是很方便的。只需要進行下面的配置即可。 ```java @Component @Aspect public class MyAspect { //PointCut匹配的方法必須是Spring中bean的方法 //Pointcut可以有下列方式來定義或者通過&& || 和!的方式進行組合. //下面定義的這些切入點就可以通過&& ||組合 private static Logger logger = LoggerFactory.getLogger(MyAspect.class); //*:代表方法的返回值可以是任何型別 //整個表示式匹配controller包下面任何的的echo方法,方法入參樂意是任意 @Pointcut("execution(* com.csx.demo.spring.boot.controller.*.echo(..))") public void pointCut1(){} @Before("pointCut1()") public void befor(){ logger.info("前置通知vvvv..."); logger.info("我要做些事情..."); } } ``` 然後再開啟註解 ```java //自動選擇合適的AOP代理 //傳統xml這樣配置: //exposeProxy = true屬性設定成true,意思是將動態生成的代理類expose到AopContext的ThreadLocal執行緒 //可以通過AopContext.currentProxy();獲取到生成的動態代理類。 //proxyTargetClass屬性設定動態代理使用JDK動態代理還是使用CGlib代理,設定成true是使用CGlib代理,false的話是使用JDK動態代理 //注意:如果使用Spring Boot的話,下面的配置可以不需要。AopAutoConfiguration這個自動配置類中已經自動開啟了AOP //預設使用CGLIB動態代理,Spring Boot配置的優先順序高於下面的配置 @Configuration @EnableAspectJAutoProxy(exposeProxy = true,proxyTargetClass = false) public class AopConfig { } ``` 通上面的配置,當我們呼叫controller包下面的任何類的echo方法時就會觸發前置通知。其實這個說法不是很準確。因為我們呼叫的類已經不是我們自己寫的類了。而是Spring框架通過動態代理生成的類。 稍微瞭解一點Spring AOP的同學都會知道Spring的AOP是通過動態代理實現的。那Spring是怎麼生成動態代理類,並將Advice織入代理類的呢?整個流程是怎樣的呢?下面就分析下Spring生成動態代理類的過程。 需要說明下的是,本部落格旨在梳理整個AOP動態代理的過程,細節方面需要大家自己去看。 ## @EnableAspectJAutoProxy幹了些啥 如果讓你從頭開始研究下AOP的原理,你是不是一頭霧水,根本不知道從何入手。**但其實看`Spring`的程式碼有個小技巧:如果你要研究一個功能,可以從開啟這個功能的Enable註解開始看**。Spring的很多功能都是通過Enable註解開啟的,所以這些註解肯定和這些功能相關。 那麼這邊我們可以從@EnableAspectJAutoProxy這個註解開始著手,看下這個註解做了些什麼操作。 ```java @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AspectJAutoProxyRegistrar.class) public @interface EnableAspectJAutoProxy { //設定為true的話就一直使用cglib動態代理 //設定為false的話,對於介面使用jdk動態代理,對於類使用cglib代理 boolean proxyTargetClass() default false; boolean exposeProxy() default false; } ``` 看到上面的@Impoer註解,我們很自然就會想到去看AspectJAutoProxyRegistrar這個類。 ```java //AspectJAutoProxyRegistrar原始碼 class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar { /** * 主要作用也就是註冊AnnotationAwareAspectJAutoProxyCreator */ @Override public void registerBeanDefinitions( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { //註冊AnnotationAwareAspectJAutoProxyCreator的BeanDefinition AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class); if (enableAspectJAutoProxy != null) { //給上面註冊的BeanDefinition中新增兩個屬相proxyTargetClass和exposeProxy if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } if (enableAspectJAutoProxy.getBoolean("exposeProxy")) { AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); } } } } ``` 我們可以看到上面的類中也沒幹什麼特別的事情,就註冊了一個BeanDefinition。如果我們點進去看下AnnotationAwareAspectJAutoProxyCreator這個類的原始碼會發現這個類竟然實現了InstantiationAwareBeanPostProcessor這個介面。熟悉Spring尿性的朋友會敏銳的感覺到Spring可能是在postProcessBeforeInstantiation或者postProcessAfterInstantiation這些方法中對Bean進行動態代理的。 “大膽假設,小心求證”,讓我們帶著這個猜想去看看AnnotationAwareAspectJAutoProxyCreator到底幹了些什麼? ## AnnotationAwareAspectJAutoProxyCreator生成動態代理類 ```java @Override public Object postProcessBeforeInstantiation(Class beanClass, String beanName) { 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; } } // 一般不指定CustomTargetSource,所以不會進入這段程式碼,所以關鍵程式碼在 // postProcessAfterInitialization中 // 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; } ``` 下面是建立動態代理類的關鍵程式碼。 ```java @Override public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (this.earlyProxyReferences.remove(cacheKey) != bean) { //這邊是建立程式碼類的關鍵程式碼 return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; } ``` ```java 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. //獲取當前Bean配置的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; } ``` 層層dedug進去我們能看到下面這段程式碼,我們口中常說的JDK動態代理和Cglib動態代理就是在這邊生成的。 ```java public class DefaultAopProxyFactory implements AopProxyFactory, Serializable { @Override 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() || Proxy.isProxyClass(targetClass)) { //生成JDK動態代理 return new JdkDynamicAopProxy(config); } //生成Cglib動態代理 return new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); } } /** * Determine whether the supplied {@link AdvisedSupport} has only the * {@link org.springframework.aop.SpringProxy} interface specified * (or no proxy interfaces specified at all). */ private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) { Class[] ifcs = config.getProxiedInterfaces(); return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0]))); } ``` 到此,我們已經簡單分析了Spring動態代理類的生成流程。 > PS:關於InstantiationAwareBeanPostProcessor介面和BeanPostProcessor介面大家可以自行了解下,這兩個介面是Spring中非常重要的介面。看懂了這兩個介面,Spring很多“神祕”的功能你就能理解了。 ## 簡單總結 通過上面分析,其實我們發現如果不去看AOP動態代理類生成的細節的話,整個Spring AOP的流程還是挺簡單的: - @EnableAspectJAutoProxy註解通過AopConfigUtils這個工具類註冊AnnotationAwareAspectJAutoProxyCreator這個類,這個類實現了InstantiationAwareBeanPostProcessor介面,所以會在Bean例項化前後對Bean做一系列額外的操作; - AnnotationAwareAspectJAutoProxyCreator的postProcessAfterInitialization中會找出所有和當前Bean相關的Advice,如果找到就建立相應的動態代理類,如果找不到就不生成,返回原始類。 所以整個大流程就這麼簡單。 一些重要類: - @EnableAspectJAutoProxy; - AspectJAutoProxyRegistrar:註冊AnnotationAwareAspectJAutoProxyCreator - AnnotationAwareAspectJAutoProxyCreator:AOP動態代理自動生成的處理類,其他類似的類有AspectJAwareAdvisorAutoProxyCreator和InfrastructureAdvisorAutoProxyCreator等; - AopConfigUtils:AOP配置工具類 - ProxyFactory:代理工廠 - AopProxy介面:常見實現類ObjenesisCglibAopProxy、JdkDynamicAopProxy ## 參考 - https://blog.csdn.net/zhoushimiao1990/article/details/