Spring进阶 - Spring AOP实现原理(一)AOP切面实现原理
紧跟前文,前面我们学习了 AOP 基础,并进阶学习 IOC 初始化过程和 Bean 的生命周期等,而Spring AOP也是基于IOC的Bean加载来实现的。
本文介绍Spring AOP 原理解析,切面的实现过程。将切面类的所有切面方法根据使用的注解生成对应Advice,并将Advice连同切入点匹配器和切面类等信息一并封装到Advisor,为后续交给代理增强实现做准备的过程。
本文主要以阅读源码为主,与 IOC 容器初始化文章的源码阅读一样,本文也描述了源码阅读的流程,类似于地图,阅读源码迷路时看一下文字流程。
引言
我们应该从哪里入手看Spring AOP源码?和我们上文分析的IOC源码实现有什么关系呢?
前文聊到Spring AOP配置方式有 XML 和注解两种方式,XML核心配置如下:

可以看出AOP配置在Bean下,表明AOP是基于IOC容器加载来完成,这便是我们探索AOP的主要入口。
启动应用,初始化。
然后我们就能找到如下初始化的流程和aop对应的handler类,即parseCustomElement方法找到解析 aop:aspectj-autoproxy的handler(org.springframework.aop.config.AopNamespaceHandler)

其实你会发现,最重要的是知识点的关联关系,而不是知识点本身,单个知识点学一会谁学不会,前面的知识学好了,后续知识点不就是跑跑程序打个断点慢慢看的事了么?
入口找到,开始本文学习之旅。
上面的示例代码完整提供
初始化 Spring 项目,引入依赖 pom.xml , 最好是 java8 版本
▼xml复制代码<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>${aspectjweaver.version}</version> </dependency> </dependencies>
User 实体类
▼java复制代码public class User { /** * user's name. */ private String name; /** * user's age. */ private int age; /** * init. * * @param name name * @param age age */ public User(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
dao 层代码
▼java复制代码@Repository public class UserDaoImpl { /** * init. */ public UserDaoImpl() { } /** * mocked to find user list. * * @return user list */ public List<User> findUserList() { return Collections.singletonList(new User("fency", 18)); } }
Service 类
▼java复制代码@Service public class UserServiceImpl { /** * user dao impl. */ @Autowired private UserDaoImpl userDao; /** * init. */ public UserServiceImpl() { } /** * find user list. * * @return user list */ public List<User> findUserList() { return this.userDao.findUserList(); } /** * set dao. * * @param userDao user dao */ public void setUserDao(UserDaoImpl userDao) { this.userDao = userDao; } }
切面类
▼java复制代码package com.fency.springframework.aspect; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.reflect.MethodSignature; import java.lang.reflect.Method; public class LogAspect { /** * aspect for every methods under service package. */ public Object businessService(ProceedingJoinPoint pjp) throws Throwable { // get attribute through annotation Method method = ((MethodSignature) pjp.getSignature()).getMethod(); System.out.println("execute method: " + method.getName()); // continue to process return pjp.proceed(); } }
XML 配置
aspect.xml
▼xml复制代码<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 配置LogAspect切面 --> <bean id="logAspect" class="com.fency.springframework.aspect.LogAspect"/> <!-- 配置 AOP --> <aop:config> <!-- 定义切面 --> <aop:aspect id="logAspect" ref="logAspect"> <!-- 定义切入点 --> <aop:pointcut id="userServiceMethods" expression="execution(* com.fency.springframework.service.*.*(..))"/> <!-- 配置环绕通知 --> <aop:around method="businessService" pointcut-ref="userServiceMethods"/> </aop:aspect> </aop:config> </beans>
daos.xml
▼java复制代码<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 启用注解扫描 --> <context:component-scan base-package="com.fency.springframework.dao"/> <!-- 配置UserDaoImpl bean --> <bean id="userDao" class="com.fency.springframework.dao.UserDaoImpl"/> </beans>
services.xml
▼java复制代码<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 启用注解扫描 --> <context:component-scan base-package="com.fency.springframework.service"/> <!-- 配置UserServiceImpl bean,注入userDao依赖 --> <bean id="userService" class="com.fency.springframework.service.UserServiceImpl"> <property name="userDao" ref="userDao"/> </bean> </beans>
APP 启动类
▼java复制代码public class App { /** * main interfaces. * * @param args args */ public static void main(String[] args) { // create and configure beans ApplicationContext context = new FileSystemXmlApplicationContext("aspects.xml", "daos.xml", "services.xml"); // retrieve configured instance UserServiceImpl service = context.getBean("userService", UserServiceImpl.class); // use configured instance List<User> userList = service.findUserList(); // print info from beans userList.forEach(a -> System.out.println(a.getName() + "," + a.getAge())); } }
文字描述阅读源码流程(XML为例)
为了避免阅读源码时迷路,提前给大家提供地图,这是接下来我们要做的事情,现在看不懂是正常的,迷路了别忘记回来看这里。
阶段一 : 标签解析
- Spring 启动,IOC 容器从加载标签 XML 标签开始,XML 标签声明了多个自定义实例和 AOP 切面实例,IOC 容器加载所有的 XML 。
- 在 AopNamespaceHandler 中有一个 init()方法,我们发现每一个 aop 标签都有一个解析器,所以当你在 xml 使用 aop 这些标签,会被解析成 AOP 代理。
- config 配置标签的解析,初始化 ConfigBeanDefinitionParser 类,执行内部 parse 方法。
- aspect-autoproxy 标签解析,初始一个解析类,内部有 parse 方法,但是一直调用下去会发现 AOP的创建工作是交给 AnnotationAwareAspectJAutoProxyCreator 类完成
AnnotationAwareAspectJAutoProxyCreator 有两个重要的方法: **postProcessBeforeInstantiation** **(本文详细讲解)**和 postProcessAfterInstantiation(下一篇文章讲解)。到这里就挖到宝了。
阶段二:**postProcessBeforeInstantiation** 方法
-
方法内有两个重要的判断,判断是否给这个实例加 aop 的代理能力。
-
- 是否是 AOP 基础类?是基础类就不加 aop 能力。
- 是否跳过?跳过就不加 aop能力
-
如果实例是基础类,且要被跳过,那么它是 aop 切面类,把切面转成 Advisor 对象,内部会调用一个 getAdvisor() 的方法来转换。
-
在 getAdvisor() 方法又会调用 getPointcut() 方法获取表达式切点。最后转成出来一个 advisor 对象。
-
advisor 对象是 advice 的包装类,所以我们要获取 advice 信息,封装到 advisor 内部。
大费周章得到的 advisor 是什么?
advisor 约等于通知(Advice)和 切入点(Pointcut),一个 advisor 是 AOP 设计思想的两个术语的具体实现。一个切面类有多个通知,就有多个切点表达式,那么就有多个 advisor.
这就是**postProcessBeforeInstantiation** 方法做的事情,第一步的判断确保不会给 aop 切面类实例加 aop 代理能力,避免无限递归。后面的步骤是把 aop 切面实例转成一个 advisor 对象。
总结流程: 解析标签 -> 解析时调用 postProcessBeforeInstantiation 方法 -> 得到一个 advisor 对象。
有了Adisor, 注入到合适的位置并交给代理(cglib和jdk)实现了。这里是在 postProcessAfterInstantiation 执行,也就是下一篇文章讲解的部分。
接下来按照这个流程,看源码。
aop配置标签的解析
我们找到了AopNamespaceHandler,其实就是注册BeanDefinition的解析器BeanDefinitionParser,将aop:xxxxxx配置标签交给指定的parser来处理。
看一下 AopNamespaceHandler 的源码,将XML的aop:xxx配置标签交给Parser来处理。
▼java复制代码public class AopNamespaceHandler extends NamespaceHandlerSupport { /** * Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the * '{@code config}', '{@code spring-configured}', '{@code aspectj-autoproxy}' * and '{@code scoped-proxy}' tags. */ @Override public void init() { // In 2.0 XSD as well as in 2.5+ XSDs // 注册解析<aop:config> 配置 registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser()); // 注册解析<aop:aspectj-autoproxy> 配置 registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser()); registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator()); // Only in 2.0 XSD: moved to context namespace in 2.5+ registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser()); } }
我们来对 config配置标签和aspectj-autoproxy标签解析。
config配置标签的解析
<aop:config/>由ConfigBeanDefinitionParser这个类处理(java8才有这个类),作为parser类最重要的就是parse方法,源码:
▼java复制代码@Override @Nullable public BeanDefinition parse(Element element, ParserContext parserContext) { CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element)); parserContext.pushContainingComponent(compositeDef); configureAutoProxyCreator(parserContext, element); List<Element> childElts = DomUtils.getChildElements(element); for (Element elt: childElts) { String localName = parserContext.getDelegate().getLocalName(elt); if (POINTCUT.equals(localName)) { parsePointcut(elt, parserContext); } else if (ADVISOR.equals(localName)) { parseAdvisor(elt, parserContext); } else if (ASPECT.equals(localName)) { parseAspect(elt, parserContext); } } parserContext.popAndRegisterContainingComponent(); return null; }
打个断点看看

parseAspect的方法如下, 处理方法不难,我这里就不展开了
▼java复制代码private void parseAspect(Element aspectElement, ParserContext parserContext) { String aspectId = aspectElement.getAttribute(ID); String aspectName = aspectElement.getAttribute(REF); try { this.parseState.push(new AspectEntry(aspectId, aspectName)); List<BeanDefinition> beanDefinitions = new ArrayList<>(); List<BeanReference> beanReferences = new ArrayList<>(); List<Element> declareParents = DomUtils.getChildElementsByTagName(aspectElement, DECLARE_PARENTS); for (int i = METHOD_INDEX; i < declareParents.size(); i++) { Element declareParentsElement = declareParents.get(i); beanDefinitions.add(parseDeclareParents(declareParentsElement, parserContext)); } // We have to parse "advice" and all the advice kinds in one loop, to get the // ordering semantics right. NodeList nodeList = aspectElement.getChildNodes(); boolean adviceFoundAlready = false; for (int i = 0; i < nodeList.getLength(); i++) { Node node = nodeList.item(i); if (isAdviceNode(node, parserContext)) { if (!adviceFoundAlready) { adviceFoundAlready = true; if (!StringUtils.hasText(aspectName)) { parserContext.getReaderContext().error( "<aspect> tag needs aspect bean reference via 'ref' attribute when declaring advices.", aspectElement, this.parseState.snapshot()); return; } beanReferences.add(new RuntimeBeanReference(aspectName)); } AbstractBeanDefinition advisorDefinition = parseAdvice( aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences); beanDefinitions.add(advisorDefinition); } } AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition( aspectElement, aspectId, beanDefinitions, beanReferences, parserContext); parserContext.pushContainingComponent(aspectComponentDefinition); List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT); for (Element pointcutElement : pointcuts) { parsePointcut(pointcutElement, parserContext); } parserContext.popAndRegisterContainingComponent(); } finally { this.parseState.pop(); } }
aspectj-autoproxy配置标签的解析
这个部分仅仅是看调用关系,比较简单。
<aop:aspectj-autoproxy/>则由AspectJAutoProxyBeanDefinitionParser这个类处理的,我们看下parse 方法
▼java复制代码@Override @Nullable public BeanDefinition parse(Element element, ParserContext parserContext) { // 注册AspectJAnnotationAutoProxyCreator AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element); // 拓展BeanDefinition extendBeanDefinition(element, parserContext); return null; }
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary方法对应如下
▼java复制代码public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary( ParserContext parserContext, Element sourceElement) { BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary( parserContext.getRegistry(), parserContext.extractSource(sourceElement)); useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement); registerComponentIfNecessary(beanDefinition, parserContext); }
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary对应如下
▼java复制代码@Nullable public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary( BeanDefinitionRegistry registry, @Nullable Object source) { return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source); }
到这里,我们发现AOP的创建工作是交给AnnotationAwareAspectJAutoProxyCreator来完成的。
注解切面代理创建类(AnnotationAwareAspectJAutoProxyCreator)
AnnotationAwareAspectJAutoProxyCreator是如何工作的呢?这时候我们就要看AnnotationAwareAspectJAutoProxyCreator类结构关系了。
如下是类结构关系

它实现了两类接口:
- BeanFactoryAware属于Bean级生命周期接口方法
- InstantiationAwareBeanPostProcessor 和 BeanPostProcessor 这两个接口实现,一般称它们的实现类为“后处理器”,是容器级生命周期接口方法;
Bean级生命周期与容器级生命周期前文讲解过,没啥特殊的:
- Bean 级:Bean 实例化 -> 销毁的全过程
- 容器级:容器准备 -> Bean级生命周期 -> 容器关闭 全过程。简化步骤就是如此。
结合前文Spring Bean生命周期的流程,我们就可以定位到核心的初始化方法肯定在postProcessBeforeInstantiation和postProcessAfterInitialization中。

postProcessBeforeInstantiation
如下是上述类结构中postProcessBeforeInstantiation的方法,读者在自己看代码的时候建议打个断点看,可以方便理解

▼java复制代码@Override public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) { Object cacheKey = getCacheKey(beanClass, beanName); if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) { // 如果已经在缓存中,则忽略 if (this.advisedBeans.containsKey(cacheKey)) { return null; } // 是否是aop基础类?是否跳过? if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return null; } } // Create proxy here if we have a custom TargetSource. // Suppresses unnecessary default instantiation of the target bean: // The TargetSource will handle target instances in a custom fashion. TargetSource targetSource = getCustomTargetSource(beanClass, beanName); if (targetSource != null) { if (StringUtils.hasLength(beanName)) { this.targetSourcedBeans.add(beanName); } Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource); Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } return null; }
判断是否是aop基础类
postProcessBeforeInstantiation 方法内有一个步骤是 **isInfrastructureClass(beanClass),判断是否是aop基础类,如果是基础类那么不能被AOP代理,**源码:
简单理解,除了【业务目标对象】,其它【AOP框架自身组件】和【切面】都属于基础类,不应该被AOP代理。
▼java复制代码@Override protected boolean isInfrastructureClass(Class<?> beanClass) { // Previously we setProxyTargetClass(true) in the constructor, but that has too // broad an impact. Instead we now override isInfrastructureClass to avoid proxying // aspects. I'm not entirely happy with that as there is no good reason not // to advise aspects, except that it causes advice invocation to go through a // proxy, and if the aspect implements e.g the Ordered interface it will be // proxied by that interface and fail at runtime as the advice method is not // defined on the interface. We could potentially relax the restriction about // not advising aspects in the future. // 父类判断它是aop基础类 or 使用@Aspect注解 return (super.isInfrastructureClass(beanClass) || (this.aspectJAdvisorFactory != null && this.aspectJAdvisorFactory.isAspect(beanClass))); }
父类判断它是否是aop基础类的方法 super.isInfrastructureClass(beanClass), 本质上就是判断该类是否实现了Advice, Pointcut, Advisor或者AopInfrastructureBean接口。
▼java复制代码protected boolean isInfrastructureClass(Class<?> beanClass) { // 该类是否实现了Advice, Pointcut, Advisor或者AopInfrastructureBean接口 boolean retVal = Advice.class.isAssignableFrom(beanClass) || Pointcut.class.isAssignableFrom(beanClass) || Advisor.class.isAssignableFrom(beanClass) || AopInfrastructureBean.class.isAssignableFrom(beanClass); if (retVal && logger.isTraceEnabled()) { logger.trace("Did not attempt to auto-proxy infrastructure class [" + beanClass.getName() + "]"); } return retVal; }
是否应该跳过shouldSkip
postProcessBeforeInstantiation 方法内有一个步骤是 **shouldSkip(beanClass, beanName),**通过断点辅助,candidateAdvisors与xml配置的通知是对应的。 java 8 才能跑出来这个效果,java11 源码改了。
这一步的意义是:防止切面自己被自己通知,从而避免无限递归调用和潜在的循环依赖问题, 并且把切面转成 advisor 也是从这里开始。
当循环到 UserServiceImpl 时,这是一个需要 AOP 切面的Bean, 那么我们debug 可以看出要给 Bean 添加的候选通知。

▼java复制代码@Override protected boolean shouldSkip(Class<?> beanClass, String beanName) { // TODO: Consider optimization by caching the list of the aspect names List<Advisor> candidateAdvisors = findCandidateAdvisors(); for (Advisor advisor : candidateAdvisors) { if (advisor instanceof AspectJPointcutAdvisor && ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) { return true; } } return super.shouldSkip(beanClass, beanName); }
切面方法转成Advisor
这里不做剖析,如果感兴趣想深入研究切面方法怎么转成 Advisor, 这里提供java8源码。
findCandidateAdvisors方法如下:
▼java复制代码@Override protected List<Advisor> findCandidateAdvisors() { // 在父类中找到所有的advisor:基于xml配置的<aop:before/>生成的 List<Advisor> advisors = super.findCandidateAdvisors(); // 为bean Factory中AspectJ切面构建advistor:通过AspectJ注解的方式生成Advisor类 if (this.aspectJAdvisorsBuilder != null) { advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors()); } return advisors; }
在当前的bean Factory中通过AspectJ注解的方式生成Advisor类,buildAspectJAdvisors方法如下
这方法很长,本质上做的事情:用DCL双重锁的单例实现方式,拿到切面类里的切面方法,将其转换成advisor(并放入缓存中)。
▼java复制代码/** * Look for AspectJ-annotated aspect beans in the current bean factory, * and return to a list of Spring AOP Advisors representing them. * <p>Creates a Spring Advisor for each AspectJ advice method. * @return the list of {@link org.springframework.aop.Advisor} beans * @see #isEligibleBean */ public List<Advisor> buildAspectJAdvisors() { List<String> aspectNames = this.aspectBeanNames; if (aspectNames == null) { synchronized (this) { aspectNames = this.aspectBeanNames; if (aspectNames == null) { List<Advisor> advisors = new ArrayList<>(); aspectNames = new ArrayList<>(); String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Object.class, true, false); for (String beanName : beanNames) { if (!isEligibleBean(beanName)) { continue; } // We must be careful not to instantiate beans eagerly as in this case they // would be cached by the Spring container but would not have been weaved. Class<?> beanType = this.beanFactory.getType(beanName, false); if (beanType == null) { continue; } if (this.advisorFactory.isAspect(beanType)) { aspectNames.add(beanName); AspectMetadata amd = new AspectMetadata(beanType, beanName); if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) { MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName); List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory); // 单例加到advisorsCache, 非单例加到aspectFactoryCache if (this.beanFactory.isSingleton(beanName)) { this.advisorsCache.put(beanName, classAdvisors); } else { this.aspectFactoryCache.put(beanName, factory); } advisors.addAll(classAdvisors); } else { // Per target or per this. if (this.beanFactory.isSingleton(beanName)) { throw new IllegalArgumentException("Bean with name '" + beanName + "' is a singleton, but aspect instantiation model is not singleton"); } MetadataAwareAspectInstanceFactory factory = new PrototypeAspectInstanceFactory(this.beanFactory, beanName); this.aspectFactoryCache.put(beanName, factory); // advisorFactory工厂获取advisors advisors.addAll(this.advisorFactory.getAdvisors(factory)); } } } this.aspectBeanNames = aspectNames; return advisors; } } } if (aspectNames.isEmpty()) { return Collections.emptyList(); } List<Advisor> advisors = new ArrayList<>(); for (String aspectName : aspectNames) { List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName); if (cachedAdvisors != null) { advisors.addAll(cachedAdvisors); } else { MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName); advisors.addAll(this.advisorFactory.getAdvisors(factory)); } } return advisors; }
转换的成advisor的方法是:this.advisorFactory.getAdvisors,看getAdvisors源码
▼java复制代码@Override public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) { Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName(); 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<>(); for (Method method : getAdvisorMethods(aspectClass)) { // Prior to Spring Framework 5.2.7, advisors.size() was supplied as the declarationOrderInAspect // to getAdvisor(...) to represent the "current position" in the declared methods list. // However, since Java 7 the "current position" is not valid since the JDK no longer // returns declared methods in the order in which they are declared in the source code. // Thus, we now hard code the declarationOrderInAspect to 0 for all advice methods // discovered via reflection in order to support reliable advice ordering across JVM launches. // Specifically, a value of 0 aligns with the default value used in // AspectJPrecedenceComparator.getAspectDeclarationOrder(Advisor). Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName); if (advisor != null) { advisors.add(advisor); } } // If it's a per target aspect, emit the dummy instantiating aspect. if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) { Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory); advisors.add(0, instantiationAdvisor); } // Find introduction fields. for (Field field : aspectClass.getDeclaredFields()) { Advisor advisor = getDeclareParentsAdvisor(field); if (advisor != null) { advisors.add(advisor); } } return advisors; }
getAdvisor方法如下
▼java复制代码@Override @Nullable public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrderInAspect, String aspectName) { validate(aspectInstanceFactory.getAspectMetadata().getAspectClass()); AspectJExpressionPointcut expressionPointcut = getPointcut( candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass()); if (expressionPointcut == null) { return null; } // 封装成advisor return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod, this, aspectInstanceFactory, declarationOrderInAspect, aspectName); }
最后这里有一个重点 getPointcut() 方法,这是获取表达式的切点,详细看看
获取表达式的切点
切面类包含了切点,切面类转成 advisor 了,那么切点自然也是要转的。
获取表达式的切点的方法getPointcut如下:
▼java复制代码@Nullable private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) { AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod); if (aspectJAnnotation == null) { return null; } AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]); ajexp.setExpression(aspectJAnnotation.getPointcutExpression()); if (this.beanFactory != null) { ajexp.setBeanFactory(this.beanFactory); } return ajexp; }
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod的方法如下
▼java复制代码private static final Class<?>[] ASPECTJ_ANNOTATION_CLASSES = new Class<?>[] { Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class}; /** * Find and return the first AspectJ annotation on the given method * (there <i>should</i> only be one anyway...). */ @SuppressWarnings("unchecked") @Nullable protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) { for (Class<?> clazz : ASPECTJ_ANNOTATION_CLASSES) { AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) clazz); if (foundAnnotation != null) { return foundAnnotation; } } return null; }
findAnnotation方法如下
▼java复制代码@Nullable private static <A extends Annotation> AspectJAnnotation<A> findAnnotation(Method method, Class<A> toLookFor) { A result = AnnotationUtils.findAnnotation(method, toLookFor); if (result != null) { return new AspectJAnnotation<>(result); } else { return null; } }
AnnotationUtils.findAnnotation 获取注解方法如下
▼java复制代码/** * Find a single {@link Annotation} of {@code annotationType} on the supplied * {@link Method}, traversing its super methods (i.e. from superclasses and * interfaces) if the annotation is not <em>directly present</em> on the given * method itself. * <p>Correctly handles bridge {@link Method Methods} generated by the compiler. * <p>Meta-annotations will be searched if the annotation is not * <em>directly present</em> on the method. * <p>Annotations on methods are not inherited by default, so we need to handle * this explicitly. * @param method the method to look for annotations on * @param annotationType the annotation type to look for * @return the first matching annotation, or {@code null} if not found * @see #getAnnotation(Method, Class) */ @Nullable public static <A extends Annotation> A findAnnotation(Method method, @Nullable Class<A> annotationType) { if (annotationType == null) { return null; } // Shortcut: directly present on the element, with no merging needed? if (AnnotationFilter.PLAIN.matches(annotationType) || AnnotationsScanner.hasPlainJavaAnnotationsOnly(method)) { return method.getDeclaredAnnotation(annotationType); } // Exhaustive retrieval of merged annotations... return MergedAnnotations.from(method, SearchStrategy.TYPE_HIERARCHY, RepeatableContainers.none()) .get(annotationType).withNonMergedAttributes() .synthesize(MergedAnnotation::isPresent).orElse(null); }
封装成Advisor
注:Advisor 是 advice的包装器,包含了advice及其它信息
由InstantiationModelAwarePointcutAdvisorImpl构造完成
▼java复制代码public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut, Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) { this.declaredPointcut = declaredPointcut; this.declaringClass = aspectJAdviceMethod.getDeclaringClass(); this.methodName = aspectJAdviceMethod.getName(); this.parameterTypes = aspectJAdviceMethod.getParameterTypes(); this.aspectJAdviceMethod = aspectJAdviceMethod; this.aspectJAdvisorFactory = aspectJAdvisorFactory; this.aspectInstanceFactory = aspectInstanceFactory; this.declarationOrder = declarationOrder; this.aspectName = aspectName; if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) { // Static part of the pointcut is a lazy type. Pointcut preInstantiationPointcut = Pointcuts.union( aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut); // Make it dynamic: must mutate from pre-instantiation to post-instantiation state. // If it's not a dynamic pointcut, it may be optimized out // by the Spring AOP infrastructure after the first evaluation. this.pointcut = new PerTargetInstantiationModelPointcut( this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory); this.lazy = true; } else { // A singleton aspect. this.pointcut = this.declaredPointcut; this.lazy = false; this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut); } }
通过pointcut获取advice
▼java复制代码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); }
交给aspectJAdvisorFactory获取
▼java复制代码@Override @Nullable public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) { // 获取切面类 Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); 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 (!isAspect(candidateAspectClass)) { throw new AopConfigException("Advice must be declared inside an aspect type: " + "Offending method '" + candidateAdviceMethod + "' in class [" + candidateAspectClass.getName() + "]"); } if (logger.isDebugEnabled()) { logger.debug("Found AspectJ method: " + candidateAdviceMethod); } // 切面注解转换成advice AbstractAspectJAdvice springAdvice; switch (aspectJAnnotation.getAnnotationType()) { case AtPointcut: // AtPointcut忽略 if (logger.isDebugEnabled()) { logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'"); } return null; case AtAround: springAdvice = new AspectJAroundAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; case AtBefore: springAdvice = new AspectJMethodBeforeAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; case AtAfter: springAdvice = new AspectJAfterAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; case AtAfterReturning: springAdvice = new AspectJAfterReturningAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation(); if (StringUtils.hasText(afterReturningAnnotation.returning())) { springAdvice.setReturningName(afterReturningAnnotation.returning()); } break; 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); } // 最后将其它切面信息配置到advice springAdvice.setAspectName(aspectName); springAdvice.setDeclarationOrder(declarationOrder); String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod); if (argNames != null) { springAdvice.setArgumentNamesFromStringArray(argNames); } springAdvice.calculateArgumentBindings(); return springAdvice; }
小结
回头看,主要是处理使用了@Aspect注解的切面类,然后将切面类的所有切面方法根据使用的注解生成对应Advice,并将Advice连同切入点匹配器和切面类等信息一并封装到Advisor的过程。
advisor 有了,再加上源码的流程,其实到这里切面就已经实现了。
回顾一下你在写切面编程的时候,你需要定义 @Aspect, @Around , 切点表达式(Pointcut) 对吧,那就对应上源码了,@Aspect声明的了切面类实例,作用是源码中判断是否属于 aop 基础类,@Around , 切点表达式 对应为 advisor, 切面实现了。
下一篇文章学 advisor 的用法。

