IoC 和 AOP 被稱爲 Spring 兩大基礎模塊,支撐着上層擴展的實現和運行。雖然 AOP 一樣創建在 IoC 的實現基礎之上,可是做爲對 OOP(Object-Oriented Programing) 的補充,AOP(Aspect-Oriented Programming) 在程序設計領域擁有其不可替代的適用場景和地位。Spring AOP 做爲 AOP 思想的實現,被譽爲 Spring 框架的基礎模塊也算是實至名歸。Spring 在 1.0 版本的時候就引入了對 AOP 的支持,而且隨着版本的迭代逐漸提供了基於 XML 配置、註解,以及 schema 配置的使用方式,考慮到實際開發中使用註解配置的方式相對較多,因此本文主要分析註解式 AOP 的實現和運行機制。java
首先咱們仍是經過一個簡單的示例演示一下註解式 AOP 的具體使用。假設咱們聲明瞭一個 IService 接口,並提供了相應的實現類 ServiceImpl,以下:spring
public interface IService { void sayHello(); void sayHelloTo(String name); void sayByebye(); void sayByebyeTo(String name); } @Service public class ServiceImpl implements IService { @Override public void sayHello() { this.sayHelloTo("zhenchao"); } @Override public void sayHelloTo(String name) { System.out.println("hello, " + name); } @Override public void sayByebye() { this.sayByebyeTo("zhenchao"); } @Override public void sayByebyeTo(String name) { System.out.println("byebye, " + name); } }
如今咱們但願藉助 Spring AOP 實現對方法調用的打點功能。首先咱們須要定義一個切面:express
@Aspect @Component public class MetricAspect { @Before("execution(* sayHello*(..))") public void beforeMetrics4sayHello(JoinPoint point) { System.out.println("[BEFORE] metrics for method: " + point.getSignature().getName()); } @Around("execution(* say*(..))") public Object aroundMetrics4say(ProceedingJoinPoint point) throws Throwable { System.out.println("[AROUND] before metrics for method: " + point.getSignature().getName()); Object obj = point.proceed(); System.out.println("[AROUND] after metrics for method: " + point.getSignature().getName()); return obj; } @After("execution(* sayByebye*(..))") public void afterMetrics4sayByebye(JoinPoint point) { System.out.println("[AFTER] metrics for method: " + point.getSignature().getName()); } }
經過 @Aspect
註解標記 MetricAspect 是一個切面,經過註解 @Before
、@After
,以及 @Around
,咱們在切面中定義了相應的前置、後置,以及環繞加強。而後咱們須要在 XML 配置中添加一行以下配置以啓用註解式 AOP:編程
<aop:aspectj-autoproxy/>
如今,咱們就算大功告成了。數組
固然,上面的實現只是註解式 AOP 使用的一個簡單示例,並無覆蓋全部的特性。對於 Spring AOP 特性的介紹不屬於本文的範疇,不過咱們仍是會在下面分析源碼的過程當中進行鍼對性的介紹。緩存
下面從啓用註解式 AOP 的那一行配置切入,即 <aop:aspectj-autoproxy/>
標籤。前面在分析 Spring IoC 實現的文章中,曾專門分析過 Spring 默認標籤和自定義標籤的解析過程。對於一個標籤而言,除了標籤的定義,還須要有對應的標籤的解析器,並在 Spring 啓動時將標籤及其解析器註冊到 Spring 容器中。標籤 <aop:aspectj-autoproxy />
的註冊過程由 AopNamespaceHandler#init
方法實現:bash
// 註冊 <aspectj-autoproxy/> 標籤及其解析器
this.registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
AspectJAutoProxyBeanDefinitionParser 類是標籤 <aop:aspectj-autoproxy />
的解析器,該類實現了 BeanDefinitionParser 接口,並實現了 BeanDefinitionParser#parse
接口方法,屬於標準的標籤解析器定義。Spring 容器在啓動時會調用 AspectJAutoProxyBeanDefinitionParser#parse
方法解析標籤,實現以下:網絡
public BeanDefinition parse(Element element, ParserContext parserContext) { // 註冊標籤解析器,默認使用 AnnotationAwareAspectJAutoProxyCreator AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element); // 解析 <aop:include /> 子標籤,記錄到 BeanDefinition 到 includePatterns 屬性中 this.extendBeanDefinition(element, parserContext); return null; }
該方法作了兩件事情:註冊標籤解析器和處理 <aop:include />
子標籤。本文咱們重點來看標籤解析器的註冊過程,即 >AopNamespaceUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary
方法:併發
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(ParserContext parserContext, Element sourceElement) { // 1. 註冊或更新代理建立器 ProxyCreator 的 BeanDefinition 對象 BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary( parserContext.getRegistry(), parserContext.extractSource(sourceElement)); // 2. 獲取並處理標籤的 proxy-target-class 和 expose-proxy 屬性 useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement); // 3. 註冊組件,併發布事件通知 registerComponentIfNecessary(beanDefinition, parserContext); }
咱們在代碼註釋中標明瞭該方法所作的 3 件事情,其中 1 和 2 是咱們分析的關鍵,首先來看 1 過程所作的事情:app
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary( BeanDefinitionRegistry registry, @Nullable Object source) { return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source); } private static BeanDefinition registerOrEscalateApcAsRequired( Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); // 若是名爲 org.springframework.aop.config.internalAutoProxyCreator 的 bean 已經在冊 if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) { BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME); // 已經在冊的 ProxyCreator 與當前指望的類型不一致,則依據優先級進行選擇 if (!cls.getName().equals(apcDefinition.getBeanClassName())) { int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName()); int requiredPriority = findPriorityForClass(cls); // 選擇優先級高的 ProxyCreator 更新註冊 if (currentPriority < requiredPriority) { apcDefinition.setBeanClassName(cls.getName()); } } return null; } // 沒有對應在冊的 ProxyCreator,註冊一個新的 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
的 BeanDefinition,咱們稱之爲代理建立器(ProxyCreator)。這裏使用的默認實現爲 AnnotationAwareAspectJAutoProxyCreator 類,若是存在多個候選實現,則選擇優先級最高的進行註冊。
接下來看一下過程 2,這一步主要是用來解析標籤 <aop:aspectj-autoproxy/>
的 proxy-target-class
和 expose-proxy
屬性配置,由 AopNamespaceUtils#useClassProxyingIfNecessary
方法實現:
private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, @Nullable Element sourceElement) { if (sourceElement != null) { /* * 獲取並處理 proxy-target-class 屬性: * - false 表示使用 java 原生動態代理 * - true 表示使用 CGLib 動態 * * 可是對於一些沒有接口實現的類來講,即便設置爲 false 也會使用 CGlib 進行代理 */ boolean proxyTargetClass = Boolean.parseBoolean(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE)); if (proxyTargetClass) { // 爲以前註冊的 ProxyCreator 添加一個名爲 proxyTargetClass 的屬性,值爲 true AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } /* * 獲取並處理 expose-proxy 標籤,實現對於內部方法調用的 AOP 加強 */ boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE)); if (exposeProxy) { // 爲以前註冊的 ProxyCreator 添加一個名爲 exposeProxy 的屬性,值爲 true AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); } } }
其中 proxy-target-class
屬性用來配置是否使用 CGLib 代理,而 expose-proxy
屬性則用來配置是否對內部方法調用啓用 AOP 加強。屬性 proxy-target-class
的做用你們應該都比較熟悉,下面介紹一下 expose-proxy
屬性。前面給出的 AOP 示例中,咱們在 IService#sayHello
方法中調用了 IService#sayHelloTo
方法,雖然兩個方法都知足對應的 AOP 加強定義,可是隻有 IService#sayHello 方法被加強了,這主要是由於 IService#sayHelloTo 方法是在對象內部調用的,調用該方法的對象並非代理對象。若是指望內部調用時也可以被加強,咱們須要配置 expose-proxy=true
,並修改 IService#sayHello
方法對於 IService#sayHelloTo
方法的調用方式:
public void sayHello() { ((IService) AopContext.currentProxy()).sayHelloTo("zhenchao"); }
上面分析了這麼多,總的說來就是向 Spring 容器中註冊了一個 AnnotationAwareAspectJAutoProxyCreator 類型的 ProxyCreator,並將配置的 proxy-target-class
和 expose-proxy
屬性添加到對應 BeanDefinition 的屬性列表中。那麼 AnnotationAwareAspectJAutoProxyCreator 究竟是來作什麼的呢?咱們先來看一下它的類繼承關係圖:
從類繼承關係圖中能夠看到該類實現了 BeanPostProcessor 接口,該接口定義以下:
public interface BeanPostProcessor { @Nullable default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Nullable default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } }
由以前對 Spring IoC 容器啓動過程的分析,咱們知道在容器啓動過程當中會在初始化 bean 實例的先後分別調用 BeanPostProcessor 中定義的這兩個方法。針對這兩個方法的實現主要位於繼承鏈的 AbstractAutoProxyCreator 類中,而且主要是實現了 BeanPostProcessor#postProcessAfterInitialization
方法:
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean != null) { // 若是 beanName 不爲空則直接使用 beanName(FactoryBean 則使用 &{beanName}),不然使用 bean 的 className Object cacheKey = this.getCacheKey(bean.getClass(), beanName); if (!this.earlyProxyReferences.contains(cacheKey)) { // 嘗試對 bean 進行加強,建立返回加強後的代理對象 return this.wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }
該方法的核心在於調用 AbstractAutoProxyCreator#wrapIfNecessary
方法嘗試基於 AOP 配置對當前 bean 進行加強,並返回加強後的代理對象。方法 AbstractAutoProxyCreator#wrapIfNecessary
的實現以下:
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { // 已經處理過,直接返回 if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) { return bean; } // 不須要進行加強的 bean 實例,直接跳過 if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { return bean; } // 對於 AOP 的基礎支撐類,或者指定不須要被代理的類,設置爲不進行代理 if (this.isInfrastructureClass(bean.getClass()) || this.shouldSkip(bean.getClass(), beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } // 獲取適用於當前 bean 的 Advisor Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); // 基於獲取到的 Advisor 爲當前 bean 建立代理對象 if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); Object proxy = this.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 實例進行篩選,過濾掉那些已經加強過的、支持 AOP 基礎運行的,以及指定不須要被代理的 bean 實例。對於剩下的 bean 實例來講,首先會調用 AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean
方法獲取適用於當前 bean 的加強器(Advisor),並基於這些加強器調用 AbstractAutoProxyCreator#createProxy
方法爲當前 bean 建立加強後的代理對象。
咱們首先來看一下篩選適用於當前 bean 的合格加強器的過程,實現位於 AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean
方法中:
protected Object[] getAdvicesAndAdvisorsForBean( Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) { // 獲取適用於當前 bean 的 Advisor List<Advisor> advisors = this.findEligibleAdvisors(beanClass, beanName); // 沒有合格的 Advisor,不進行代理 if (advisors.isEmpty()) { return DO_NOT_PROXY; // null } return advisors.toArray(); } protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) { // 獲取全部候選的 Advisor(包括註解的、XML 中配置的) List<Advisor> candidateAdvisors = this.findCandidateAdvisors(); // 從全部 Advisor 中尋找適用於當前 bean 的 Advisor List<Advisor> eligibleAdvisors = this.findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); // 若是 Advisor 不爲空,則在最前面追加一個 ExposeInvocationInterceptor this.extendAdvisors(eligibleAdvisors); // 對 Advisor 進行排序 if (!eligibleAdvisors.isEmpty()) { eligibleAdvisors = this.sortAdvisors(eligibleAdvisors); } return eligibleAdvisors; }
整個方法的執行流程很簡單,獲取全部的候選加強器,並從中找出適用於當前 bean 的加強器。首先來看獲取全部候選加強器的過程,實現位於 AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors
方法中:
protected List<Advisor> findCandidateAdvisors() { // 調用父類的 findCandidateAdvisors 方法,兼容父類查找 Advisor 的規則 List<Advisor> advisors = super.findCandidateAdvisors(); // 獲取全部註解定義的 Advisor if (this.aspectJAdvisorsBuilder != null) { advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors()); } return advisors; }
方法首先調用了父類的實現,這主要是爲了兼容父類查找候選加強器的規則,例如咱們的示例中使用的是註解方式定義的加強,可是父類倒是基於 XML 配置的方式查找加強器,這裏的兼容可以讓咱們在以註解方式編程時兼容其它以 XML 配置的方式定義的加強。下面仍是將主要精力放在解析註解式加強定義上,該過程位於 BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors
方法中。不過該方法實現比較冗長,可是邏輯卻很清晰,因此這裏主要歸納一下其執行流程:
1,獲取全部類型 bean 實例對應的 beanName 集合;
2,過濾不是切面類型的 bean 對應的 beanName,即沒有被 @Aspect
註解, 或包含以 ajc$
開頭的字段,同時支持覆蓋 BeanFactoryAspectJAdvisorsBuilder#isEligibleBean
方法擴展過濾規則;
3,對於切面 bean 類型,獲取 bean 中定義的全部切點,併爲每一個切點生成對應的加強器;
4,緩存解析獲得的加強器,避免重複解析。
上述流程中咱們重點看一下過程 3,實現位於 ReflectiveAspectJAdvisorFactory#getAdvisors
方法中:
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) { // 獲取切面 aspect 對應的 class 和 beanName Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName(); // 校驗切面定義的合法性 this.validate(aspectClass); // We need to wrap the MetadataAwareAspectInstanceFactory with a decorator // so that it will only instantiate once. MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory); List<Advisor> advisors = new ArrayList<>(); // 1. 遍歷處理切面中除被 @Pointcut 註解之外的方法 for (Method method : this.getAdvisorMethods(aspectClass)) { Advisor advisor = this.getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName); if (advisor != null) { advisors.add(advisor); } } // 2. 若是加強器不爲空,同時又配置了加強延遲初始化,則須要追加實例化加強器 SyntheticInstantiationAdvisor if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) { Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory); advisors.add(0, instantiationAdvisor); } // 3. 獲取全部引介加強定義 for (Field field : aspectClass.getDeclaredFields()) { // 建立引介加強器 DeclareParentsAdvisor Advisor advisor = this.getDeclareParentsAdvisor(field); if (advisor != null) { advisors.add(advisor); } } return advisors; }
上述實現的總體執行流程如代碼註釋。拿到一個切面定義,Spring 首先會遍歷獲取切面中的加強方法,即被 @Around
、@Before
、@After
、@AfterReturning
,以及 @AfterThrowing
註解的方法,並調用 ReflectiveAspectJAdvisorFactory#getAdvisor
方法爲每個加強方法生成對應的加強器:
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrderInAspect, String aspectName) { // 校驗切面類定義的合法性 this.validate(aspectInstanceFactory.getAspectMetadata().getAspectClass()); // 獲取註解配置的切點信息,封裝成 AspectJExpressionPointcut 對象 AspectJExpressionPointcut expressionPointcut = this.getPointcut( candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass()); if (expressionPointcut == null) { return null; } // 依據切點信息生成對應的加強器 return new InstantiationModelAwarePointcutAdvisorImpl( expressionPointcut, candidateAdviceMethod, this, aspectInstanceFactory, declarationOrderInAspect, aspectName); }
上述實現首先對當前切面定義執行合法性校驗,若是切面配置合法則獲取目標方法上的切點註解定義,並封裝成 AspectJExpressionPointcut 對象。該過程位於 ReflectiveAspectJAdvisorFactory#getPointcut
方法中,實現比較簡單。
拿到切點註解定義以後,方法會依據切點的配置信息使用 InstantiationModelAwarePointcutAdvisorImpl 實現類建立對應的加強器。類 InstantiationModelAwarePointcutAdvisorImpl 的實例化過程除了初始化了一些基本屬性以外,主要是調用了 InstantiationModelAwarePointcutAdvisorImpl#instantiateAdvice
方法,依據加強類型對加強器實施相應的初始化操做:
private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) { Advice advice = this.aspectJAdvisorFactory.getAdvice( this.aspectJAdviceMethod, pointcut, this.aspectInstanceFactory, this.declarationOrder, this.aspectName); return (advice != null ? advice : EMPTY_ADVICE); } // org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvice public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) { // 獲取切面 class 對象,並校驗切面定義 Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); this.validate(candidateAspectClass); // 獲取方法的切點註解定義 AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod); if (aspectJAnnotation == null) { return null; } // If we get here, we know we have an AspectJ method. // Check that it's an AspectJ-annotated class if (!this.isAspect(candidateAspectClass)) { throw new AopConfigException("Advice must be declared inside an aspect type: " + "Offending method '" + candidateAdviceMethod + "' in class [" + candidateAspectClass.getName() + "]"); } AbstractAspectJAdvice springAdvice; // 依據切點註解類型使用對應的加強類進行封裝 switch (aspectJAnnotation.getAnnotationType()) { // @Pointcut case AtPointcut: if (logger.isDebugEnabled()) { logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'"); } return null; // @Around case AtAround: springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; // @Before case AtBefore: springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; // @After case AtAfter: springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; // @AfterReturning case AtAfterReturning: springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation(); if (StringUtils.hasText(afterReturningAnnotation.returning())) { springAdvice.setReturningName(afterReturningAnnotation.returning()); } break; // @AfterThrowing case AtAfterThrowing: springAdvice = new AspectJAfterThrowingAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation(); if (StringUtils.hasText(afterThrowingAnnotation.throwing())) { springAdvice.setThrowingName(afterThrowingAnnotation.throwing()); } break; default: throw new UnsupportedOperationException("Unsupported advice type on method: " + candidateAdviceMethod); } // Now to configure the advice... springAdvice.setAspectName(aspectName); springAdvice.setDeclarationOrder(declarationOrder); String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod); if (argNames != null) { springAdvice.setArgumentNamesFromStringArray(argNames); } springAdvice.calculateArgumentBindings(); return springAdvice; }
方法的總體執行流程如代碼註釋,邏輯比較清晰,Spring 會依據具體的加強註解類型,選擇相應的加強類對切點定義進行封裝。這裏咱們以 @Before
爲例說明一下加強的執行流程,AspectJMethodBeforeAdvice 加強類關聯註冊的處理器是 MethodBeforeAdviceInterceptor,當咱們調用一個被前置加強的目標方法時,MethodBeforeAdviceInterceptor#invoke
方法會被觸發:
public Object invoke(MethodInvocation mi) throws Throwable { // 執行加強方法 this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis()); // 執行目標方法 return mi.proceed(); }
這裏執行的加強方法就對應着 AspectJMethodBeforeAdvice#before
方法,該方法會依據切點配置將相應的參數綁定傳遞給咱們自定義的加強方法,並最終經過反射調用觸發執行。
上面分析了普通方法級別加強的處理過程,對於另一類加強(引介加強),方法 ReflectiveAspectJAdvisorFactory#getAdvisors
則使用專門的 DeclareParentsAdvisor 類建立對應的加強器:
// 3. 獲取全部引介加強定義 for (Field field : aspectClass.getDeclaredFields()) { // 建立引介加強器 Advisor advisor = this.getDeclareParentsAdvisor(field); if (advisor != null) { advisors.add(advisor); } } private Advisor getDeclareParentsAdvisor(Field introductionField) { // 獲取 @DeclareParents 註解定義 DeclareParents declareParents = introductionField.getAnnotation(DeclareParents.class); if (declareParents == null) { return null; } // 沒有指定默認的接口實現類 if (DeclareParents.class == declareParents.defaultImpl()) { throw new IllegalStateException("'defaultImpl' attribute must be set on DeclareParents"); } // 使用 DeclareParentsAdvisor 類型建立對應的引介加強器 return new DeclareParentsAdvisor( introductionField.getType(), declareParents.value(), declareParents.defaultImpl()); }
對於引介加強來講,Spring 會注入 DelegatePerTargetObjectIntroductionInterceptor 處理器對其進行專門的處理,思想上與前面分析前置加強大同小異,這裏再也不展開。
繼續回到 AbstractAdvisorAutoProxyCreator#findEligibleAdvisors
方法,上面的過程咱們分析了獲取全部類型加強器的過程,可是這些加強器不必定都適用於當前 bean 實例,咱們須要依據切點配置信息對其進行篩選。這一過程位於 AbstractAdvisorAutoProxyCreator#findAdvisorsThatCanApply
方法中:
protected List<Advisor> findAdvisorsThatCanApply( List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) { ProxyCreationContext.setCurrentProxiedBeanName(beanName); try { return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass); } finally { ProxyCreationContext.setCurrentProxiedBeanName(null); } } // org.springframework.aop.support.AopUtils#findAdvisorsThatCanApply public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) { // 沒有候選的加強器,直接返回 if (candidateAdvisors.isEmpty()) { return candidateAdvisors; } List<Advisor> eligibleAdvisors = new ArrayList<>(); // 1. 篩選引介加強器 for (Advisor candidate : candidateAdvisors) { if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) { eligibleAdvisors.add(candidate); } } // 表示是否含有引介加強 boolean hasIntroductions = !eligibleAdvisors.isEmpty(); // 2. 篩選其它類型的加強器 for (Advisor candidate : candidateAdvisors) { // 引介加強已經處理過,這裏直接跳過 if (candidate instanceof IntroductionAdvisor) { // already processed continue; } // 篩選其它類型的加強器 if (canApply(candidate, clazz, hasIntroductions)) { eligibleAdvisors.add(candidate); } } return eligibleAdvisors; }
方法首先會使用類過濾器(ClassFilter)篩選引介加強器,除了咱們手動註冊的類過濾器外,這裏默認還會使用 TypePatternClassFilter 類過濾器執行過濾操做。而後,方法會過濾篩選其它類型的加強器,這裏除了使用類過濾器外,考慮方法級別加強的定義形式,還會使用方法匹配器(MethodMatcher)進行篩選。若是加強器適用於當前 bean 類型,則將其加入到集合中用於下一步爲當前 bean 建立加強代理對象。若是沒有任何一個加強器適用於當前 bean 類型,則方法 AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean
最終會返回值爲 null 的 DO_NOT_PROXY
數組對象,表示當前 bean 不須要被加強。
完成了對於當前 bean 加強器的篩選,接下來咱們繼續回到 AbstractAutoProxyCreator#wrapIfNecessary
方法,看一下基於前面篩選出的加強器爲當前 bean 建立加強代理對象的過程,實現位於 AbstractAutoProxyCreator#createProxy
方法中:
protected Object createProxy(Class<?> beanClass, @Nullable String beanName, @Nullable Object[] specificInterceptors, TargetSource targetSource) { if (this.beanFactory instanceof ConfigurableListableBeanFactory) { AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass); } // ProxyFactory 用於爲目標 bean 實例建立代理對象 ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.copyFrom(this); // proxy-target-class = false,表示使用 JDK 原生動態代理 if (!proxyFactory.isProxyTargetClass()) { // 檢測當前 bean 是否應該基於類而非接口生成代理對象,即包含 preserveTargetClass=true 屬性 if (this.shouldProxyTargetClass(beanClass, beanName)) { proxyFactory.setProxyTargetClass(true); } // 若是是基於接口生成代理,則添加須要代理的接口到 ProxyFactory 中(除內置 callback 接口、語言內在接口,以及標記接口) else { this.evaluateProxyInterfaces(beanClass, proxyFactory); } } // 將攔截器封裝成 Advisor 對象 Advisor[] advisors = this.buildAdvisors(beanName, specificInterceptors); proxyFactory.addAdvisors(advisors); proxyFactory.setTargetSource(targetSource); // 模板方法,定製代理工廠 this.customizeProxyFactory(proxyFactory); // 設置代理工廠被配置以後是否還容許修改,默認爲 false,表示不容許修改 proxyFactory.setFrozen(this.freezeProxy); if (this.advisorsPreFiltered()) { proxyFactory.setPreFiltered(true); } // 基於 ProxyFactory 建立代理類 return proxyFactory.getProxy(this.getProxyClassLoader()); }
方法的執行流程如代碼註釋。下面咱們主要分析將攔截器封裝成 Advisor 對象的過程,以及基於 ProxyFactory 建立加強代理對象的過程。
Spring 定義了很是多的攔截器、加強器,以及加強方法等,這裏經過 AbstractAutoProxyCreator#buildAdvisors
方法統一將他們封裝成 Advisor 對象,從而簡化代理的建立過程。封裝的核心步驟由 DefaultAdvisorAdapterRegistry#wrap
方法實現:
public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException { // 已是 Advisor,則無需多作處理 if (adviceObject instanceof Advisor) { return (Advisor) adviceObject; } // 要求必須是 Advice 類型 if (!(adviceObject instanceof Advice)) { throw new UnknownAdviceTypeException(adviceObject); } Advice advice = (Advice) adviceObject; // 若是是 MethodInterceptor,則直接使用 DefaultPointcutAdvisor 進行包裝 if (advice instanceof MethodInterceptor) { // So well-known it doesn't even need an adapter. return new DefaultPointcutAdvisor(advice); } // 不然遍歷註冊的適配器,若是存在關聯的適配器則使用 DefaultPointcutAdvisor 進行包裝 for (AdvisorAdapter adapter : this.adapters) { // Check that it is supported. if (adapter.supportsAdvice(advice)) { return new DefaultPointcutAdvisor(advice); } } throw new UnknownAdviceTypeException(advice); }
接下來咱們重點分析一下經過代理工廠 ProxyFactory 建立加強代理對象的過程,實現位於 ProxyFactory#getProxy
方法中:
public Object getProxy(@Nullable ClassLoader classLoader) { return this.createAopProxy() // 1. 建立 AOP 代理 .getProxy(classLoader); // 2. 基於 AOP 代理建立目標類的加強代理對象 }
該方法的執行過程能夠拆分紅兩個步驟:
1,建立 AOP 代理,Spring 默認提供了兩種 AOP 代理實現,即 java 原生代理和 CGLib 代理;
2,基於 AOP 代理建立目標類的加強代理對象。
咱們首先來看一下步驟 1 的實現,位於 ProxyCreatorSupport#createAopProxy
方法中:
protected final synchronized AopProxy createAopProxy() { if (!this.active) { this.activate(); } return this.getAopProxyFactory().createAopProxy(this); } // org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { if (config.isOptimize() // 須要對代理策略進行優化 || config.isProxyTargetClass() // // 指定使用 CGLib 生成代理對象 || this.hasNoUserSuppliedProxyInterfaces(config)) // 當前類沒有接口定義,不得不使用 CGLib { 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."); } // 目標類是接口或代理類,使用 JDK 原生代理 if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); } // 使用 CGLib 動態代理 return new ObjenesisCglibAopProxy(config); } // 使用 JDK 原生動態代理 else { return new JdkDynamicAopProxy(config); } }
這部分代碼清晰說明了 Spring 在生成代理對象時如何在 java 原生代理和 CGLib 代理之間進行選擇,能夠歸納以下:
1,若是目標類實現了接口,則 Spring 默認會使用 java 原生代理。
2,若是目標類未實現接口,則 Spring 會使用 CGLib 生成代理。
3,若是目標類實現了接口,可是在配置時指定了 proxy-target-class=true,則使用 CGLib 生成代理。
下面分別對基於 java 原生代理和 CGLib 代理生成加強代理對象的過程進行分析。
首先來看一下基於 java 原生代理生成加強代理對象的過程,位於 JdkDynamicAopProxy 類中。Java 原生代理要求代理類實現 InvocationHandler 接口,並在 InvocationHandler#invoke
方法中實現代理加強邏輯。JdkDynamicAopProxy 正好實現了該接口,對應的 JdkDynamicAopProxy#invoke
方法實現以下:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object oldProxy = null; boolean setProxyContext = false; TargetSource targetSource = this.advised.targetSource; Object target = null; try { // 當前是 equals 方法,可是被代理類接口中未定義 equals 方法 if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) { return this.equals(args[0]); } // 當前是 hashCode 方法,可是被代理類接口中未定義 hashCode 方法 else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) { return this.hashCode(); } // 若是是 DecoratingProxy 中定義的方法(即 DecoratingProxy#getDecoratedClass),直接返回目標類對象 else if (method.getDeclaringClass() == DecoratingProxy.class) { return AopProxyUtils.ultimateTargetClass(this.advised); } else if (!this.advised.opaque // 容許被轉換成 Advised 類型 && method.getDeclaringClass().isInterface() // 接口類型 && method.getDeclaringClass().isAssignableFrom(Advised.class)) // 方法所在類是 Advised 類及其父類 { // 直接反射調用該方法 return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args); } // 結果值 Object retVal; // 指定內部間調用也須要代理 if (this.advised.exposeProxy) { // Make invocation available if necessary. oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } // Get as late as possible to minimize the time we "own" the target, in case it comes from a pool. target = targetSource.getTarget(); Class<?> targetClass = (target != null ? target.getClass() : null); // 獲取當前方法的攔截器鏈 List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); // Check whether we have any advice. If we don't, we can fallback on direct // reflective invocation of the target, and avoid creating a MethodInvocation. // 攔截器鏈爲空,則直接反射調用加強方法 if (chain.isEmpty()) { // We can skip creating a MethodInvocation: just invoke the target directly // Note that the final invoker must be an InvokerInterceptor so we know it does // nothing but a reflective operation on the target, and no hot swapping or fancy proxying. Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); } // 不然須要建立對應的 MethodInvocation,以鏈式調用攔截器方法和加強方法 else { MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // Proceed to the joinpoint through the interceptor chain. retVal = invocation.proceed(); } // 處理返回值 Class<?> returnType = method.getReturnType(); if (retVal != null && retVal == target && returnType != Object.class && returnType.isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) { // Special case: it returned "this" and the return type of the method is type-compatible. // Note that we can't help if the target sets a reference to itself in another returned object. retVal = proxy; } else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) { throw new AopInvocationException( "Null return value from advice does not match primitive return type for: " + method); } return retVal; } finally { if (target != null && !targetSource.isStatic()) { // Must have come from TargetSource. targetSource.releaseTarget(target); } if (setProxyContext) { // Restore old proxy. AopContext.setCurrentProxy(oldProxy); } } }
由上述方法實現,咱們能夠歸納出整個加強代理的執行過程,以下:
特殊處理 Object#equals
、Object#hashCode
、DecoratingProxy#getDecoratedClass
,以及 Advised 類及其父類中定義的方法;
若是配置了 expose-proxy 屬性,則記錄當前代理對象,以備在內部間調用時實施加強;
獲取當前方法的攔截器鏈;
若是沒有攔截器定義,則直接反射調用加強方法,不然先逐一執行攔截器方法,最後再應用加強方法;
處理返回值。
重點來看一下步驟 4 中應用攔截器方法的實現,位於 ReflectiveMethodInvocation#proceed
方法中:
public Object proceed() throws Throwable { // 若是全部的加強都執行完成,則執行加強方法 if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { return this.invokeJoinpoint(); } // 獲取下一個須要執行的攔截器 Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); // 動態攔截器,執行動態方法匹配 if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { // Evaluate dynamic method matcher here: static part will already have been evaluated and found to match. InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass()); // 動態匹配成功,執行對應的攔截方法 if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) { return dm.interceptor.invoke(this); } // 動態匹配失敗,忽略當前攔截器方法,繼續執行下一個攔截器 else { return this.proceed(); } } // 靜態攔截器,直接應用攔截方法 else { return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } }
攔截器方法的執行流程如上述代碼註釋,是一個遞歸調用的過程,並在最後應用加強方法。
完成了對於 AOP 代理對象 JdkDynamicAopProxy 的建立,最後來看一下獲取該對象的過程,實現位於 JdkDynamicAopProxy#getProxy
方法中:
public Object getProxy(@Nullable ClassLoader classLoader) { // 獲取須要被代理的接口集合 Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true); // 檢測是否在被代理接口中聲明瞭 equals 和 hashCode 方法 this.findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); // 基於 java 原生代理生成代理對象 return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); }
這裏的邏輯也就是 java 原生代理的模板代碼,若是對 java 代理比較熟悉的話,應該不難理解。
基於 CGLib 代理生成加強代理對象的過程位於 ObjenesisCglibAopProxy 類中,該類繼承自 CglibAopProxy 類。獲取 CGLib 代理類對象的方法定義在 CglibAopProxy 中,即 CglibAopProxy#getProxy
方法。該方法基於 CGLib 的 Enhancer 類建立代理對象,屬於 CGLib 的標準使用模式,由於有多個 callback 實現,因此這裏使用了 CallbackFilter 模式,依據場景選擇並應用對應的 callback 攔截器。
咱們重點關注 callback 的實現,位於 CglibAopProxy#getCallbacks
方法中。受制於 CGLib 在執行時一次只容許應用一個 callback 的約束,因此該方法依據參數配置實現了一組 callback,以覆蓋不一樣的場景。核心的 AOP callback 實現是 DynamicAdvisedInterceptor 類,它實現了 MethodInterceptor 接口,對應的 DynamicAdvisedInterceptor#intercept
方法實現以下:
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object oldProxy = null; boolean setProxyContext = false; Object target = null; TargetSource targetSource = this.advised.getTargetSource(); try { // 指定內部間調用也須要代理 if (this.advised.exposeProxy) { // Make invocation available if necessary. oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } // Get as late as possible to minimize the time we "own" the target, in case it comes from a pool... target = targetSource.getTarget(); Class<?> targetClass = (target != null ? target.getClass() : null); // 獲取當前方法的攔截器鏈 List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); // 結果值 Object retVal; // Check whether we only have one InvokerInterceptor: // that is, no real advice, but just reflective invocation of the target. // 攔截器鏈爲空,則直接反射調用加強方法 if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) { // We can skip creating a MethodInvocation: just invoke the target directly. // Note that the final invoker must be an InvokerInterceptor, so we know // it does nothing but a reflective operation on the target, and no hot swapping or fancy proxying. Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = methodProxy.invoke(target, argsToUse); } // 不然須要建立對應的 MethodInvocation,以鏈式調用攔截器方法和加強方法 else { retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed(); } // 處理返回值 retVal = processReturnType(proxy, target, method, retVal); return retVal; } finally { if (target != null && !targetSource.isStatic()) { targetSource.releaseTarget(target); } if (setProxyContext) { // Restore old proxy. AopContext.setCurrentProxy(oldProxy); } } }
能夠看出上述方法在實現流程上與前面介紹的 JdkDynamicAopProxy#invoke
方法是一致的,只是這裏是基於 CGLib 實現而已。
最後咱們對 Spring AOP 的運行機制進行一個總結。Spring AOP 的實現本質上是一個動態代理的過程,Spring 引入了 java 原生代理和 CGLib 代理,並依據場景選擇基於哪一種代理機制對目標對象進行加強。由前面對於 Spring IoC 實現的分析能夠了解到,Spring 容器在完成對 bean 對象的建立以後會執行初始化操做,而 AOP 初始化的過程就發生在 bean 的後置初始化階段,總體流程能夠歸納爲:
1,從容器中獲取全部的切面定義;
2,篩選適用於當前 bean 的加強器集合;
3,依據加強器集合基於動態代理機制生成相應的加強代理對象。
當咱們在調用一個被加強的方法時,相應的攔截器會依據鏈接點的方位在適當的位置觸發對應的加強定義,從而最終實現 AOP 中定義的各種加強語義。
特別聲明:本文素材來源於網絡,僅做爲分享學習之用,若有侵權,請聯繫刪除!