Spring AOP 源碼解析:註解式切面增強機制
轉載自: zhenchao
IoC 和 AOP 被稱爲 Spring 兩大基礎模塊,支撐着上層擴展的實現和運行。雖然 AOP 同樣建立在 IoC 的實現基礎之上,但是作爲對 OOP(Object-Oriented Programing) 的補充,AOP(Aspect-Oriented Programming) 在程序設計領域擁有其不可替代的適用場景和地位。Spring AOP 作爲 AOP 思想的實現,被譽爲 Spring 框架的基礎模塊也算是實至名歸。Spring 在 1.0 版本的時候就引入了對 AOP 的支持,並且隨着版本的迭代逐漸提供了基於 XML 配置、註解,以及 schema 配置的使用方式,考慮到實際開發中使用註解配置的方式相對較多,所以本文主要分析註解式 AOP 的實現和運行機制。
註解式 AOP 示例
首先我們還是通過一個簡單的示例演示一下註解式 AOP 的具體使用。假設我們聲明瞭一個 IService 接口,並提供了相應的實現類 ServiceImpl,如下:
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 實現對方法調用的打點功能。首先我們需要定義一個切面:
@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 的那一行配置切入,即 <aop:aspectj-autoproxy/>
標籤。前面在分析 Spring IoC 實現的文章中,曾專門分析過 Spring 默認標籤和自定義標籤的解析過程。對於一個標籤而言,除了標籤的定義,還需要有對應的標籤的解析器,並在 Spring 啓動時將標籤及其解析器註冊到 Spring 容器中。標籤 <aop:aspectj-autoproxy />
的註冊過程由 AopNamespaceHandler#init
方法實現:
// 註冊 <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 過程所做的事情:
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 的增強器
我們首先來看一下篩選適用於當前 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
方法中。不過該方法實現比較冗長,但是邏輯卻很清晰,所以這裏主要概括一下其執行流程:
- 獲取所有類型 bean 實例對應的 beanName 集合;
- 過濾不是切面類型的 bean 對應的 beanName,即沒有被
@Aspect
註解,或包含以ajc$
開頭的字段,同時支持覆蓋BeanFactoryAspectJAdvisorsBuilder#isEligibleBean
方法擴展過濾規則; - 對於切面 bean 類型,獲取 bean 中定義的所有切點,併爲每個切點生成對應的增強器;
- 緩存解析得到的增強器,避免重複解析。
上述流程中我們重點看一下過程 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 創建增強代理對象
完成了對於當前 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 代理創建目標類的增強代理對象
}
該方法的執行過程可以拆分成兩個步驟:
- 創建 AOP 代理,Spring 默認提供了兩種 AOP 代理實現,即 java 原生代理和 CGLib 代理;
- 基於 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 代理之間進行選擇,可以概括如下:
- 如果目標類實現了接口,則 Spring 默認會使用 java 原生代理。
- 如果目標類未實現接口,則 Spring 會使用 CGLib 生成代理。
- 如果目標類實現了接口,但是在配置時指定了
proxy-target-class=true
,則使用 CGLib 生成代理。
下面分別對基於 java 原生代理和 CGLib 代理生成增強代理對象的過程進行分析。
基於 java 原生代理創建增強代理對象
首先來看一下基於 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 代理創建增強代理對象
基於 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 的後置初始化階段,整體流程可以概括爲:
- 從容器中獲取所有的切面定義;
- 篩選適用於當前 bean 的增強器集合;
- 依據增強器集合基於動態代理機制生成相應的增強代理對象。
當我們在調用一個被增強的方法時,相應的攔截器會依據連接點的方位在適當的位置觸發對應的增強定義,從而最終實現 AOP 中定義的各類增強語義。