您好,登錄后才能下訂單哦!
本篇內容主要講解“如何理解AOP中JDK代理實現的原理”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“如何理解AOP中JDK代理實現的原理”吧!
動態代理技術在Spring AOP中分為兩種:
提供一種在運行時創建一個實現了一組接口的新類。由于Java是不支持實例化接口的,因此JDK會在運行期間生成一個代理類對給定的接口進行實現,在調用該代理類接口的時候,將實現邏輯轉發到調用處理器中(Invocation handler)。
JDK進行動態代理的類必須實現接口
代理類是java.lang.reflect.Proxy子類,類名以$Proxy開始。
CGLIB(Code Generation Library)是基于ASM(對Java字節碼進行操作的框架)的類庫。
Spring AOP中,如果被代理類(targetObject)沒有實現接口,即無法通過JDK的動態代理生成代理類,那么就會選擇CGLIB來進行代理。
CGLIB動態代理的原理:創建一個targetObject的子類,覆蓋掉需要父類的方法,在覆蓋的方法中對功能進行增強。
注意,由于是采用繼承覆蓋的方式,所以由final方法修飾的類無法使用CGLIB進行代理。
JDK動態代理要求被代理類實現接口,切面類需要實現InvocationHandler。
CGLIB采用繼承+方法覆蓋實現切面,重寫方法將邏輯委托給MethodInterceptor#intercept。
CGLIB對代理類基本沒有限制,但是需要注意被代理的類不可以被final修飾符和private修飾,因為Java無法重寫final類/private的方法。
@EnableAspectJAutoProxy注解是Spring AOP開啟的標志,在啟動類標記此注解,即啟用可加載對應的切面類邏輯。此注解的ElementType為TYPE,表示標記在類上。
同時用 @Retention(RetentionPolicy.RUNTIME) 聲明了注解在運行時得到保留。此外最重要的是使用了 @Import(AspectJAutoProxyRegistrar.class) 來注冊AOP的。
@EnableAspectJAutoProxy注解正是通過@Import的方式來將 AspectJAutoProxyRegistrar類注冊成Spring的Bean,以便在容器解析切面類時派上用場。那么AspectJAutoProxyRegistrar類的作用是什么?
@Import(AspectJAutoProxyRegistrar.class)
@EnableAspectJAutoProxy支持處理標有AspectJ的@Aspect批注的組件,用戶可以主動聲明proxyTargetClass來指定Spring AOP使用哪種動態代理方式來創建代理類(默認使用基于實現接口的JDK動態代理方式)。
使用CGLIB動態代理來創建代理類
@Configuration @EnableAspectJAutoProxy(proxyTargetClass=true) @ComponentScan("com.libo") public class AppConfig { // ... }
為了解決一些由于代理引發的切面失效問題,Spring AOP在Spring 4.3.1后引入了AopContext類來將代理類的引用存儲在ThreadLocal中,通過AopContext可以快速獲取當前類的代理類。
默認為不支持,如果聲明為true,即可使用AopContext獲取代理類,同時,為了使用AspectJ,需要確保當前jar倉庫存在aspectjweaver。
通過@Import注冊AspectJAutoProxyRegistrar,通常情況下,我們的啟動類本身也是一個Bean,Spring支持使用 @Import來導入一個沒有標記任何Spring注解 的類來將該Java類注冊成Spring的Bean。
Registers an AnnotationAwareAspectJAutoProxyCreator against the current BeanDefinitionRegistry as appropriate based on a given @EnableAspectJAutoProxy annotation.
根據當前BeanDefinitionRegistry在適當的位置注冊AnnotationAwareAspectJAutoProxyCreator。
用來導入一些特殊的BeanDefinition,Spring在處理 @Configuration 時,會去掃描是否有通過 @Import 標簽導入的類,對ImportBeanDefinitionRegistrar這類接口,還會執行其中的registerBeanDefinitions方法。
AspectJAutoProxyRegistrar:實現了ImportBeanDefinitionRegistrar接口,用來注冊AspectJAnnotationAutoProxyCreator,也就是支持注解驅動(同時兼容XML)解析的AspectJ自動代理創建器。
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { // 向容器注冊AspectJAnnotationAutoProxyCreator AopConfigUtils. registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class); // 如果@EnableAspectJAutoProxy上存在標簽內容 if (enableAspectJAutoProxy != null) { // proxyTargetClass為true,則強制指定AutoProxyCreator使用CGLIB進行代理 if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } // 是否開啟exposeProxy特性 if (enableAspectJAutoProxy.getBoolean("exposeProxy")) { AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); } } } }
向容器注冊AspectJAnnotationAutoProxyCreator
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
隨后解析 @EnableAspectJAutoProxy 注解上的元數據來決定是否開啟上述我們講到的proxyTargetClass和exposeProxy特性.
為了了解registerBeanDefinitions方法的執行鏈路和調用時機,我們使用IDE的debug來查看調用棧分析執行流程。
org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass(configClass.getImportBeanDefinitionRegistrars())
這行代碼,從代碼的語義上我們可以大致可以猜出來,這是解析當前配置類上是否存在通過@Import導入的實現了ImportBeanDefinitionRegistrar的類。
最終會調用AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary方法,注冊一個AnnotationAwareAspectJAutoProxyCreator,該類屬于AOP的核心類。
執行AspectJAutoProxyRegistrar#registerBeanDefinitions方法。
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
@Nullable public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary( BeanDefinitionRegistry registry, @Nullable Object source) { return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source); }
從上面的代碼可以看出來AnnotationAwareAspectJAutoProxyCreator這個類作為實際操作者,查看該類的繼承關系圖。(省略一些不必要的類)。
首先看第一個接口,BeanPostProcessor,這個接口作為頂層接口,肯定不會被外部直接調用,所以大概率是底下的幾個具體實現類被調用,然后通過判斷是不是InstantiationAwareBeanPostProcessor接口的類型,再執行相應邏輯,帶著這個疑惑,來看源碼。
AbstractAutoProxyCreator通過postProcessAfterInitialization實現AOP功能。
// 在實例化之后進行操作容器對象 @Override public Object postProcessAfterInitialization(@Nullable 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; } protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { // beanName不為空,并且存在于targetSourcedBeans中,也就是自定義的 // TargetSource被解析過了 if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) { return bean; } // 如果Bean為advisedBeans,也不需要被代理 if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { return bean; } // isInfrastructureClass和shouldSkip的作用: // 識別切面類,加載切面類成advisors // 為什么又執行一次是因為存在循環依賴的情況下無法加載advisor if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } // Create proxy if we have advice. // 返回匹配當前Bean的所有Advice、Advisor、Interceptor Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); // 創建Bean對應的代理,SingletonTargetSource用于封裝實現類的信息 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; }
判斷緩存中是否存在當前Bean或者是當前Bean已經被代理過了,那么直接返回bean.
嘗試再次加載advisor,避免由于循環依賴導致advisor加載不完整.
獲取當前bean符合的advisor數組.
創建代理類.
本文來分析getAdvicesAndAdvisorsForBean方法是如何在所有的advisors中找到匹配的advisor的.
@Override @Nullable protected Object[] getAdvicesAndAdvisorsForBean( Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) { List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName); if (advisors.isEmpty()) { return DO_NOT_PROXY; } return advisors.toArray(); } //這里調用了findEligibleAdvisors來尋找合適的advisors,如果返回的集合為空,那么 // 最后返回null. // 如果返回了advisors,將其數組化返回. protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) { //BeanFactory 中所有 Advisor 的實現 List<Advisor> candidateAdvisors = findCandidateAdvisors(); // 有資格的 Advisor List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); extendAdvisors(eligibleAdvisors); if (!eligibleAdvisors.isEmpty()) { eligibleAdvisors = sortAdvisors(eligibleAdvisors); } return eligibleAdvisors; } //首先獲取之前解析過的advisors列表-candidateAdvisors,這里是所有的切面類解析成的advisors. //在candidateAdvisors中找到當前Bean匹配的advisor-findAdvisorsThatCanApply. //將獲取到的eligibleAdvisors進行排序. protected List<Advisor> findCandidateAdvisors() { Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available"); return this.advisorRetrievalHelper.findAdvisorBeans(); } protected List<Advisor> findAdvisorsThatCanApply( List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) { ProxyCreationContext.setCurrentProxiedBeanName(beanName); try { return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass); } finally { ProxyCreationContext.setCurrentProxiedBeanName(null); } } public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) { if (candidateAdvisors.isEmpty()) { return candidateAdvisors; } // 存儲最終匹配的Advisor集合 List<Advisor> eligibleAdvisors = new ArrayList<>(); for (Advisor candidate : candidateAdvisors) { // 當前advisor對象是否實現了IntroductionAdvisor接口 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; } // canApply->判斷當前的advisor的pointcut表達式是否匹配當前class if (canApply(candidate, clazz, hasIntroductions)) { eligibleAdvisors.add(candidate); } } return eligibleAdvisors; } //最終是調用了AopUtils.findAdvisorsThatCanApply來篩選匹配Bean的Advisors.
ProxyFactory 對象中有要代理的bean和這個Bean上的advisor
Bean使用哪種代理
當Bean實現接口時,Spring就會用JDK的動態代理。 當Bean沒有實現接口時,Spring會自動使用CGlib實現,但是前提是項目中導入了CGlib的相關依賴,否則Spring只能使用JDK來代理那些沒有實現接口的類,這樣生成的代理類會報錯。 AopProxy有兩個實現類JdkDynamicAopProxy和CglibAopProxy。都是構造 ReflectiveMethodInvocation.proceed()。
JdkDynamicAopProxy
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); retVal = invocation.proceed();
CglibAopProxy
// CglibMethodInvocation 繼承于 ReflectiveMethodInvocation retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
ReflectiveMethodInvocation.proceed()
public Object proceed() throws Throwable { // 當所有攔截器都執行后,調用目標類的目標方法 if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { return invokeJoinpoint(); } Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { // 動態攔截器 InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) { return dm.interceptor.invoke(this); } else { // Dynamic matching failed. // Skip this interceptor and invoke the next in the chain. return proceed(); } } else { // MethodInterceptor的實現類在處理完自己的邏輯后,還是會調用procee(),傳入this就是為了達到這個目的 return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } }
Spring用來處理應用上下文中被@AspectJ注解標記的類的。繼續進入registerOrEscalateApcAsRequired方法中看看注冊流程.
org.springframework.aop.config.AopConfigUtils#registerOrEscalateApcAsRequired private static BeanDefinition registerOrEscalateApcAsRequired( Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); // 當前容器是否包含 org.springframework.aop.config.internalAutoProxyCreator 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; } // 將傳入的class包裝成BeanDefinition,然后注冊到容器中,并講其order的執行順序 //調整為最優。 // 在aop中,這里會注冊AnnotationAwareAspectJAutoProxyCreator.class這個類 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; }
首先查看當前容器中是否包含
org.springframework.aop.config.internalAutoProxyCreator的BeanDefiniton.
如果沒有,將傳入的class(在此處傳入了AnnotationAwareAspectJAutoProxyCreator.class)包裝成RootBeanDefinition,然后注冊到容器中.
設置proxyTargetClass與exposeProxy
我們看一下如何設置proxyTargetClass即可,大體上設置proxyTargetClass與exposeProxy的邏輯都是相通的.
// 如果@EnableAspectJAutoProxy上存在標簽內容 if (enableAspectJAutoProxy != null) { // 如果proxyTargetClass為true,則強制指定AutoProxyCreator使用CGLIB進行代理 if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } // 是否開啟exposeProxy特性 if (enableAspectJAutoProxy.getBoolean("exposeProxy")) { AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); } }
進入AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); AopConfigUtils#forceAutoProxyCreatorToUseClassProxying public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) { // 如果容器中包含 org.springframework.aop.config.internalAutoProxyCreator if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) { // 取出 org.springframework.aop.config.internalAutoProxyCreator的BeanDefinition BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME); // 設置proxyTargetClass為true definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE); } }
如容器中包含名org.springframework.aop.config.internalAutoProxyCreator,那么取出該BeanDefinition,設置proxyTargetClass為true。
Spring為了兼容不同的BeanDefinition持有不同的屬性值,將它們都抽象成了MutablePropertyValues,definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE),就跟我們平時JavaBean中的set方法是一樣的.
簡單理解 @Import 和ImportBeanDefinitionRegistrar,下面我們通過兩個用例來理解@Import和ImportBeanDefinitionRegistrar
通過@Import導入類讓Spring進行管理
public class NeedImportBean { public void doSomething(){ Logger.getGlobal().info("Through @Import registry to a bean "); } }
在啟動類中將NeedImportBean導入
/** * @author jaymin * 2020/11/30 20:13 */ @Configuration @ComponentScan(value = "com.xjm") @Import(NeedImportBean.class) @EnableAspectJAutoProxy public class ApplicationConfig { public static AnnotationConfigApplicationContext getApplicationContext() { return new AnnotationConfigApplicationContext(ApplicationConfig.class); } } //對NeedImportBean做getBean public class BeanFactoryDemo { public static void main(String[] args) throws Exception { AnnotationConfigApplicationContext applicationContext = ApplicationConfig.getApplicationContext(); NeedImportBean needImportBean = applicationContext.getBean(NeedImportBean.class); } }
NeedImportBean實現ImportBeanDefinitionRegistrar接口,然后驗證兩個點:
是否會回調registerBeanDefinitions方法。
通過getBean是否能獲取NeedImportBean
public class NeedImportBean implements ImportBeanDefinitionRegistrar{ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { Logger.getGlobal().info("Through implements ImportBeanDefinitionRegistrar and @Import to callback me."); } public void doSomething(){ Logger.getGlobal().info("Through @Import registry to a bean "); } }
refresh中激活后置處理器ConfigurationClassPostProcessor加載@Configuration上的元數據
首先容器會加載refresh方法。
執行invokeBeanFactoryPostProcessors(beanFactory);激活工廠級別的后置處理器。
由于啟動類都是被 @Configuration 標記的,Spring會使用ConfigurationClassPostProcessor來解析被 @Configuration 的類。
使用ConfigurationClassBeanDefinitionReader來加載配置類解析成BeanDefinition。
到此,相信大家對“如何理解AOP中JDK代理實現的原理”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。