Spring AOP 源碼解析:注解式切面增強機制
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 使用的一個簡單示例,并沒有覆寫所有的特性。對于 Spring AOP 特性的介紹不屬于本文的範疇,不過我們還是會在下面分析源碼的過程中進行針對性的介紹。
注解式 AOP 實作機制
下面從啟用注解式 AOP 的那一行配置切入,即 标簽。前面在分析 Spring IoC 實作的文章中,曾專門分析過 Spring 預設标簽和自定義标簽的解析過程。對于一個标簽而言,除了标簽的定義,還需要有對應的标簽的解析器,并在 Spring 啟動時将标簽及其解析器注冊到 Spring 容器中。标簽 的注冊過程由 AopNamespaceHandler#init 方法實作:
// 注冊 标簽及其解析器
this.registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
AspectJAutoProxyBeanDefinitionParser 類是标簽 的解析器,該類實作了 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;
該方法做了兩件事情:注冊标簽解析器和處理 子标簽。本文我們重點來看标簽解析器的注冊過程,即 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,這一步主要是用來解析标簽 的 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 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 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 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 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 findAdvisorsThatCanApply(List 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 方法中:
// 擷取需要被代理的接口集合
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 中定義的各類增強語義。
原文位址
https://my.oschina.net/wangzhenchao/blog/4279608