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: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 中定义的各类增强语义。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
做 Java 开发的你,有编译过自己的 JDK 玩玩儿吗
我是风筝,公众号「古时的风筝」,一个不只有技术的技术公众号,一个在程序圈混迹多年,主业 Java,另外 Python、React 也玩儿的 6 的斜杠开发者。 Spring Cloud 系列文章已经完成,可以到 我的github 上查看系列完整内容。也可以在公众号内回复「pdf」获取我精心制作的 pdf 版完整教程。 你每天写的 Java 代码都需要 JDK 的支持,都要跑在 JVM 上,难道你就不好奇 JDK 长什么样子吗。好奇,就来编译并实现一个自己的 JDK 吧。 本次编译环境 macOS 10.12,编译的是 JDK 11 版本。 安装 OpenJDK 11 编译 OpenJDK 需要先在机器上安装 OpenJDK 10 或者 OpenJDK 11,作为 Boot JDK。 先安装 openJDK 11 编译需要,可以到 adoptopenjdk 网站去下载。 pkg 格式安装 进入页面 https://adoptopenjdk.net/index.html?variant=openjdk11&jvmVariant=hotspot 直接下载下载,然后双击就可以完成安装了...
- 下一篇
Proxy-Go v9.4 发布,专业的 HTTP(s)/SOCKS5/动态 IP 代理服务!
Proxy 是 golang 实现的高性能 http、https、websocket、tcp、udp、socks5 代理服务器,支持正向代理、反向代理、透明代理、内网穿透、TCP/UDP 端口映射、SSH 中转、TLS 加密传输、协议转换、DNS 防污染智能代理、前置 CDN/Nginx 反代、代理连接重定向、API动态调用上级代理、限速限连接数。提供全平台的命令行版本,友好易用的Windows&Linux&macOS控制面板,强大的安卓版。 更新内容 1.增加了判断流量上报对返回状态码,如果不是204则认为上报失败,会输出相关日志。 2.优化了流量上报的缓冲。 特色功能 链式代理,程序本身可以作为一级代理,如果设置了上级代理那么可以作为二级代理,乃至N级代理。 通讯加密,如果程序不是一级代理,而且上级代理也是本程序,那么可以加密和上级代理之间的通讯,采用底层tls高强度加密,安全无特征。 智能HTTP代理,HTTPS代理,SOCKS5代理,会自动判断访问的网站是否屏蔽,如果被屏蔽那么就会使用上级代理(前提是配置了上级代理)访问网站;如果访问的网站没有被屏蔽,为了加速访...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS6,CentOS7官方镜像安装Oracle11G
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- MySQL8.0.19开启GTID主从同步CentOS8
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Docker安装Oracle12C,快速搭建Oracle学习环境
- CentOS7设置SWAP分区,小内存服务器的救世主