实用指南:Spring进阶 - Spring AOP实现原理(一)AOP切面实现原理

紧跟前文,前面我们学习了 AOP 基础,并进阶学习 IOC 初始化过程和 Bean 的生命周期等,而Spring AOP也是基于IOC的Bean加载来实现的。

本文介绍Spring AOP 原理解析,切面的实现过程。将切面类的所有切面方法根据使用的注解生成对应Advice,并将Advice连同切入点匹配器和切面类等信息一并封装到Advisor,为后续交给代理增强实现做准备的过程。

本文主要以阅读源码为主,与 IOC 容器初始化文章的源码阅读一样,本文也描述了源码阅读的流程,类似于地图,阅读源码迷路时看一下文字流程。

引言

我们应该从哪里入手看Spring AOP源码?和我们上文分析的IOC源码实现有什么关系呢?

前文聊到Spring AOP配置方式有 XML 和注解两种方式,XML核心配置如下:

img

可以看出AOP配置在Bean下,表明AOP是基于IOC容器加载来完成,这便是我们探索AOP的主要入口。

启动应用,初始化。

然后我们就能找到如下初始化的流程和aop对应的handler类,即parseCustomElement方法找到解析 aop:aspectj-autoproxy的handler(org.springframework.aop.config.AopNamespaceHandler)

img

其实你会发现,最重要的是知识点的关联关系,而不是知识点本身,单个知识点学一会谁学不会,前面的知识学好了,后续知识点不就是跑跑程序打个断点慢慢看的事了么?

入口找到,开始本文学习之旅。

上面的示例代码完整提供

初始化 Spring 项目,引入依赖 pom.xml , 最好是 java8 版本

<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 实体类

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 层代码

@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 类

@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;
  }
  }

切面类

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 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

<?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

<?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 启动类

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为例)

为了避免阅读源码时迷路,提前给大家提供地图,这是接下来我们要做的事情,现在看不懂是正常的,迷路了别忘记回来看这里。

阶段一 : 标签解析

  1. Spring 启动,从加载标签 XML 标签开始,XML 标签声明了自定义实例和 AOP 切面实例,一同被加载。
  2. 在 AopNamespaceHandler 中有一个 init()方法,我们发现每一个 aop 标签都有一个解析器,所以当你在 xml 使用 aop 这些标签,会被解析成 AOP 代理。
  3. config 配置标签的解析,初始化 ConfigBeanDefinitionParser 类,执行内部 parse 方法。
  4. aspect-autoproxy 标签解析,初始一个解析类,内部有 parse 方法,但是一直调用下去会发现 AOP的创建工作是交给 AnnotationAwareAspectJAutoProxyCreator 类完成

AnnotationAwareAspectJAutoProxyCreator 有两个重要的方法: **postProcessBeforeInstantiation** **(本文详细讲解)**和 postProcessAfterInstantiation(下一篇文章讲解)。到这里就挖到宝了

阶段二:**postProcessBeforeInstantiation**方法

  1. 方法内有两个重要的判断,判断是否给这个实例加 aop 的代理能力。

    1. 是否是 AOP 基础类?是基础类就不加 aop 能力。
    2. 是否跳过?跳过就不加 aop能力
  2. 如果实例是基础类,且要被跳过,那么它是 aop 切面类,把切面转成 Advisor 对象,内部会调用一个 getAdvisor() 的方法来转换。

  3. 在 getAdvisor() 方法又会调用 getPointcut() 方法获取表达式切点。最后转成出来一个 advisor 对象。

  4. advisor 对象是 advice 的包装类,所以我们要获取 advice 信息,封装到 advisor 内部。

大费周章得到的 advisor 是什么?
advisor 约等于通知(Advice)和 切入点(Pointcut),一个 advisor 是 AOP 设计思想的两个术语的具体实现。

这就是**postProcessBeforeInstantiation** 方法做的事情,第一步的判断确保不会给 aop 切面类实例加 aop 代理能力,避免无限递归。后面的步骤是把 aop 切面实例转成一个 advisor 对象。

总结流程: 解析标签 -> 解析时调用 postProcessBeforeInstantiation 方法 -> 得到一个 advisor 对象。

有了Adisor, 注入到合适的位置并交给代理(cglib和jdk)实现了。这里是在 postProcessAfterInstantiation 执行,也就是下一篇文章讲解的部分。

接下来按照这个流程,看源码。

aop配置标签的解析

我们找到了AopNamespaceHandler,其实就是注册BeanDefinition的解析器BeanDefinitionParser,将aop:xxxxxx配置标签交给指定的parser来处理。

看一下 AopNamespaceHandler 的源码,将XML的aop:xxx配置标签交给Parser来处理。

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方法,源码:

@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;
  }

打个断点看看

img

parseAspect的方法如下, 处理方法不难,我这里就不展开了

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 方法

@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 注册AspectJAnnotationAutoProxyCreator
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
// 拓展BeanDefinition
extendBeanDefinition(element, parserContext);
return null;
}

AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary方法对应如下

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对应如下

@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}

到这里,我们发现AOP的创建工作是交给AnnotationAwareAspectJAutoProxyCreator来完成的。

注解切面代理创建类(AnnotationAwareAspectJAutoProxyCreator)

AnnotationAwareAspectJAutoProxyCreator是如何工作的呢?这时候我们就要看AnnotationAwareAspectJAutoProxyCreator类结构关系了。

如下是类结构关系

img

它实现了两类接口:

  • BeanFactoryAware属于Bean级生命周期接口方法
  • InstantiationAwareBeanPostProcessor 和 BeanPostProcessor 这两个接口实现,一般称它们的实现类为“后处理器”,是容器级生命周期接口方法

Bean级生命周期与容器级生命周期前文讲解过,没啥特殊的:

  • Bean 级:Bean 实例化 -> 销毁的全过程
  • 容器级:容器准备 -> Bean级生命周期 -> 容器关闭 全过程。简化步骤就是如此。

结合前文Spring Bean生命周期的流程,我们就可以定位到核心的初始化方法肯定在postProcessBeforeInstantiation和postProcessAfterInitialization中。

img

postProcessBeforeInstantiation

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

img

@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代理。

@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接口。

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 添加的候选通知。

img

@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方法如下:

@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(并放入缓存中)。

/**
* 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源码

@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方法如下

@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如下:

@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的方法如下

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方法如下

@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 获取注解方法如下

/**
* 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构造完成

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

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获取

@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 有了,下一篇文章学 advisor 的用法。

参考文章

https://pdai.tech/md/spring/spring-x-framework-aop-source-2.html

posted on 2025-11-02 12:24  slgkaifa  阅读(5)  评论(0)    收藏  举报

导航