目 录CONTENT

文章目录

spring-AOP

FatFish1
2025-01-17 / 0 评论 / 0 点赞 / 102 阅读 / 0 字 / 正在检测是否收录...

基于springAOP+AspectJ的面向切面开发实例

AspectJ是Java社区里最完整最流行的AOP框架。在Spring2.0以上版本中,可以使用基于AspectJ注解或基于XML配置的AOP。

<aspectj-autoproxy>当Spring IOC容器侦测到bean配置文件中的<aspectj-autoproxy>元素时,会自动为与AspectJ切面匹配的bean创建代理

动态代理基础知识

http://www.chymfatfish.cn/archives/dynamicproxy

JDK动态代理和CGLib动态代理都是一些动态代理的底层代码开发框架,当然实际开发过程中,也不一定非要每次都手搓代码,用一些开发框架可以大大简化开发流程,例如AspectJ框架,就是开发代理的一个优秀框架,配合spring使用会大大简化开发流程

AspectJ基础知识

AspectJ框架的核心能力就是基于注解开发代理,避免每次都手搓代理创建的方法,将工作重心放在对业务代码的开发中

注解

AspectJ支持的五种注解包括:

  • @Before:前置通知,在方法执行之前执行

  • @After:后置通知,在方法执行之后执行

  • @AfterRunning:返回通知,在方法返回结果之后执行

  • @AfterThrowing:异常通知,在方法抛出异常之后执行

  • @Around:环绕通知,围绕着方法执行

基于springAOP+AspectJ开发实例

开发切面和通知

@Aspect
public class AspectJTest {
    // 切入点:execution表达式,代表任何包、任何类,返回值随意的test方法
    @Pointcut("execution(* *.test(..))")
    public void test(){
    }
    // 前置通知:test之前执行
    @Before("test()")
    public void beforeTest(){
        System.out.println("beforeTest");
    }
    // 后置通知:test之后执行
    @After("test()")
    public void afterTest(){
        System.out.println("afterTest");
    }
    // 环绕通知,代理替换test方法本身,p.proceed代表原来的test方法执行内容
    @Around("test()")
    public Object arountTest(ProceedingJoinPoint p){
        System.out.println("before1");
        Object o=null;
        try {
            o = p.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        System.out.println("after1");
        return o;
    }
}

execution表达式语法

其中写在@Pointcut里面的是execution表达式,语法包括:

execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)

  • 例如:execution(* com.sample.service.impl..*.*(..))

    • 第一个*:返回值,任意

    • com.sample.service.impl:包名

    • ..*:该包或任意子包下面的任意类,如果是.*就不包含子包了

    • .*(..):任意方法,入参随意

  • 例如:execution(* *To(..))代表任意包下,返回值任意的,以To结尾的方法

  • 例如:execution(* com..*.*Dao.find*(..)) 代表以com开头的子包下以Dao结尾的任意类,返回值任意的以find开头的任意方法

  • 例如:execution(* joke(String, ..)) 匹配目标类中joke()方法,该方法第一个入参为String,后面可以有任意个且类型不限的参数

开发连接点和接口

这里开发接口,就会触发jdk代理,不开发接口则触发cglib代理

public interface AopTest {
	public void test();
}

public class TestBean implements AopTest{

    private String testStr = "testStr";

    public String getTestStr() {
        return testStr;
    }

    public void setTestStr(String testStr) {
        this.testStr = testStr;
    }

    // 这个就是连接点test方法
    public void test(){
        System.out.println("test");
    }

}

注册bean

通过spring配置把切面和目标对象都注册到IOC容器中

<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-3.0.xsd
       http://www.springframework.org/schema/aop
       https://www.springframework.org/schema/aop/spring-aop.xsd">

    <aop:aspectj-autoproxy />

    <bean id="test" class="test.TestBean"/>
    <bean class="test.AspectJTest"/>

</beans>

调用目标方法

public static void main(String[] args) {
    ApplicationContext bf = new
    ClassPathXmlApplicationContext("aspectTest.xml");
    // 不开发接口
    TestBean bean=(TestBean) bf.getBean("test");
    // 开发接口要写接口类型转换,因为jdk代理的是接口,转不成实现类
    AopTest bean = (AopTest) bf.getBean(“test”);
    bean.test();
}

动态AOP自定义标签解析相关类

AOP+AspectJ的自定义标签也是通过spring提供的自定义标签解析能力来完成的

http://www.chymfatfish.cn/archives/springxml#%E8%87%AA%E5%AE%9A%E4%B9%89%E6%A0%87%E7%AD%BE%E8%A7%A3%E6%9E%90%E7%9B%B8%E5%85%B3%E7%B1%BB

按照spring自定义注解的思路,分析AOP标签的解析。

全文搜索aspectj-autoproxy,找到了NamespaceHandlerSupport的AOP实现AopNamespaceHandler。

AopNamespaceHandler

init

public void init() {
    // In 2.0 XSD as well as in 2.5+ XSDs
    registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
    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());
}

通过重写的init方法,可以得知,负责<aspectj-autoproxy /> 标签解析的类是AspectJAutoProxyBeanDefinitionParser

同样在,当前代码包下的resources/META-INF目录下找到了spring.handlersspring.schemas,在spring.schemas的配置中可以知道定义了aop相关标签的是spring-aop.xsd文件。

AspectJAutoProxyBeanDefinitionParser

parse

重点看parse方法的实现

public BeanDefinition parse(Element element, ParserContext parserContext) {
    AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
    extendBeanDefinition(element, parserContext);
    return null;
}

可以看到两个主体流程:

首先调用AopNamespaceUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary 解析并注册自定义的AOP标签,见AopNamespaceUtils部分

然后extendBeanDefinition是对子标签<aop:include/>的解析的解析

AopNamespaceUtils

void registerAspectJAnnotationAutoProxyCreatorIfNecessary

public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(ParserContext parserContext, Element sourceElement) {
    // 注册或升级AutoProxyCreator定义beanName为org.Springframework.aop.config.internalAutoProxyCreator的BeanDefinition
    BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
          parserContext.getRegistry(), parserContext.extractSource(sourceElement));
    // 对于proxy-target-class以及expose-proxy属性的处理
    useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
    // 注册组件并通知,便于监听器做进⼀步处理
    registerComponentIfNecessary(beanDefinition, parserContext);
}

看这个方法,整体也是三个步骤:

BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary

对于AOP的实现,基本上都是靠 AnnotationAwareAspectJAutoProxyCreator去完成 它可以根据@Point注解定义的切点来⾃动代理相匹 配的bean。

它的注册就是在这里完成

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

这里看起来还要继续跟踪下层方法

registerOrEscalateApcAsRequired

AnnotationAwareAspectJAutoProxyCreator类注册到spring中,该类是解析AOP的核心类,后面会看到它生效的地方

private static BeanDefinition registerOrEscalateApcAsRequired(
       Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");

    //如果已经存在了⾃动代理创建器且存在的⾃动代理创建器与现在的不⼀致,那么需要根据优先级来判断到底需要使⽤哪个
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
       BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
       if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
          int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
          int requiredPriority = findPriorityForClass(cls);
          if (currentPriority < requiredPriority) {
             //改变bean最重要的就是改变bean所对应的className属性
             apcDefinition.setBeanClassName(cls.getName());
          }
       }
       //如果已经存在⾃动代理创建器并且与将要创建的⼀致,那么⽆须再次创建
       return null;
    }
    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;
}

以上代码中实现了⾃动注册 AnnotationAwareAspectJAutoProxyCreator类的功能,同时这⾥还涉及了⼀个优先级的问题,如果已经存在了⾃动代理创建器,⽽且存在的⾃动代理创 建器与现在的不⼀致,那么需要根据优先级来判断 到底需要使⽤哪个

useClassProxyingIfNecessary

处理proxy-target-class以及expose-proxy属性

boolean proxyTargetClass = Boolean.parseBoolean(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
if (proxyTargetClass) {
    AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
  • proxy-target-classproxy-target-class属性值决定是基于接口的还是基于类的代理被创建。首先说明下proxy-target-class="true"proxy-target-class="false"的区别,为true则是基于类的代理将起作用(需要cglib库),为false或者省略这个属性,则标准的JDK 基于接口的代理将起作用。不加这个属性时

    • 如果被代理的⽬标对象实现了⾄少⼀个接⼝,则会使⽤JDK动态代理。所有该⽬标类型实现的接⼝都将被代理

    • 若该⽬标对象没有实现任何接⼝,则创建⼀个CGLIB代理

http://www.chymfatfish.cn/archives/springknowledge#aop%E7%9B%B8%E5%85%B3%E6%A0%87%E7%AD%BE
boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
if (exposeProxy) {
    AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
  • expose-proxy有时候⽬标对象内部的⾃我调⽤将⽆法实施切⾯中的增强,类似于事务失效。配置为true是为了解决这些问题的

这里看到这两个属性下面分别调用了两个force方法

public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
       BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
       definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
    }
}
public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) {
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
       BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
       definition.getPropertyValues().add("exposeProxy", Boolean.TRUE);
    }
}

这个两方法比较简单,实际上就是在BD中加属性,以供后面创建代理的时候使用

AspectJ代理创建的过程

前面根据AOP的parser把AnnotationAwareAspectJAutoProxyCreator类加载到spring环境中了,这个类就是spring-AOP体系的核心类

首先对创建流程做个总结,总体分为两个大部分:

  • 把@Aspect注解的切面类和其中@Pointcut、@Before、@After、@Around、@Throwing、@AfterReturning等几种切面方法加载成Advice

    • Advice有五类实现,分别对应@Before、@After、@Around、@Throwing、@AfterReturning五类注解

    • Advice的五类实现有两种分类,即实现MethodInterceptor的和没实现MethodInterceptor的

  • 执行JDK代理创建或CGLib代理创建

    • 以JDK代理为例,实际上就是走创建流程,实现核心的invoke等方法

    • invoke方法中执行的操作是

      • 为原始method找到Adivce的实现类,进一步封装,如果是实现MethodInterceptor的Advice,不需要封装,如果是没实现MethodInterceptor的,则进一步封装成Interceptor,封装后统一构造成处理链

      • 递归处理链中的每一个MethodInterceptor#invoke

AnnotationAwareAspectJAutoProxyCreator

血缘图

可见上层实现了BeanPostProcessor,也就是说它是一个可插入式的处理器

回顾可插入式处理器,就还记得其中有两个方法:分别在doCreateBean三步骤:创建bean实例、注入属性、初始化中第三步初始化的前面和后面执行

http://www.chymfatfish.cn/archives/spring-beansgetfromfactory#docreatebean---%E5%88%9D%E5%A7%8B%E5%8C%96bean%E7%9A%84%E6%A0%B8%E5%BF%83

其中AnnotationAwareAspectJAutoProxyCreator的父类AbstractAutoProxyCreator实现了初始化后处理器BeanPostProcessor#postProcessAfterInitialization ,也就是说其调用点在初始化步骤的后置扩展点

http://www.chymfatfish.cn/archives/spring-beansgetfromfactory#initializebean---%E5%88%9D%E5%A7%8B%E5%8C%96bean

核心成员变量

// bean当前正在被代理的缓存,有点类似于spring三级缓存的处理思路,创建同时加个临时缓存
private final Map<Object, Object> earlyProxyReferences = new ConcurrentHashMap<>(16);
// 也是一个代理bean的缓存,走postProcessBeforeInstantiation生成的targetSource代理类存在这
private final Set<String> targetSourcedBeans = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
// 代理bean的缓存
private final Map<Object, Boolean> advisedBeans = new ConcurrentHashMap<>(256);

这几个成员变量都继承自父类AbstractAutoProxyCreator,都是做缓存相关的,可见spring的体系中使用Map缓存是非常常见的事情,都是为了提升其加载的性能

// 负责寻找和加载Advisor的工具类
private BeanFactoryAdvisorRetrievalHelper advisorRetrievalHelper;

postProcessAfterInitialization

实现的BeanPostProcessor#postProcessAfterInitialization方法,继承自AbstractAutoProxyCreator

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
       Object cacheKey = getCacheKey(bean.getClass(), beanName);
       if (this.earlyProxyReferences.remove(cacheKey) != bean) {
          return wrapIfNecessary(bean, beanName, cacheKey);
       }
    }
    return bean;
}

看上去没啥东西,就是做了一个早期代理bean缓存的移除,然后其他逻辑在wrapIfNecessary中

这里可以再思考一下方法走完了回到doCreateBean的处理逻辑

// -----------------------------------doCreateBean-------------------------------
exposedObject = initializeBean(beanName, exposedObject, mbd);
……
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
	if (exposedObject == bean) {
		exposedObject = earlySingletonReference;
	}
……

在doCreateBean的这一部分处理,第一次获取bean肯定是找不到它,那么就直接返回到最外层doGetBean方法中,进入getSingleton方法。

// ------------------------doGetBean----------------------------
sharedInstance = getSingleton(beanName, () -> {
	try {
		return createBean(beanName, mbd, args);
	}
	……
})

在getSingleton方法中实际上是通过getObject方法间接触发了createBean,然后把刚刚创建的bean实例回传出来:

// ----------------------------getSingleton-------------------------
try {
	singletonObject = singletonFactory.getObject();
	newSingleton = true;
}

拿到singletonObject之后,因为发现是第一次获取的bean,这里会调用addSingleton加入缓存

// ----------------------------getSingleton-------------------------
if (newSingleton) {
	addSingleton(beanName, singletonObject);
}

在addSingleton方法中,存入三级缓存singletonObjects,因此最后代理类代替原始实例被存入三级缓存,后续拿bean拿到的就都是代理类了

wrapIfNecessary

创建AOP的骨架方法,也是AOP创建流程中两大方法的入口:获取切面、创建切面

if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
    return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
    return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

首先判断了三个条件:

  • 基于targetSourcedBeans判断是否是targetSource流程

  • 基于advisedBeans判断当前bean是否已走过代理流程,如果是不需要代理直接返回

  • 判断bean是spring自带的基础bean,或不需要进行代理

获取切面

然后调用getAdvicesAndAdvisorsForBean获取切面

Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);

这里拿到的最终是Advisor数组

Spring Aop在为目标bean获取需要进行代理的切面逻辑的时候最终得到的是Advisor,这里Advice表示的是每个切面逻辑中使用@Before、@After和@Around等需要织入的代理方法。因为每个代理方法都表示一个Advice,并且每个代理方法最终都会生成一个Advisor。

创建切面

拿到所有切面的对象后,调用createProxy创建切面

if (specificInterceptors != DO_NOT_PROXY) {
    this.advisedBeans.put(cacheKey, Boolean.TRUE);
    Object proxy = createProxy(
          bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
    this.proxyTypes.put(cacheKey, proxy.getClass());
    return proxy;
}

最后是处理缓存和返回

this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;

getAdvicesAndAdvisorsForBean

继承自AbstractAdvisorAutoProxyCreator

protected Object[] getAdvicesAndAdvisorsForBean(
       Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
    List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
    if (advisors.isEmpty()) {
       return DO_NOT_PROXY;
    }
    return advisors.toArray();
}

这里没啥东西,直接委托给findEligibleAdvisors处理

findEligibleAdvisors

是Advisor获取流程的框架方法。一共走了四个大步骤

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    // 1. 获取:将当前系统中所有的切面类的切面逻辑进行封装,从而得到目标Advisor
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    // 2. 判断:对获取到的所有Advisor进行判断,看其切面定义是否可以应用到当前bean,从而得到最终需要应用的Advisor
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    // 3. 扩展:提供的hook方法,用于对目标Advisor进行扩展
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
       // 4.排序:对需要代理的Advisor按照一定的规则进行排序
       eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}

总结一下,就是:

  • 获取:封装所有的切面类的切面逻辑,见findCandidateAdvisors方法,得到Advisor列表

  • 判断:对获取的Advisor列表进行判断,判断是否可以应用到当前bean,这里主要是调用到AopUtils#findAdvisorsThatCanApply点我跳转

  • 扩展:提供的hook方法,用于对模板Advisor扩展

  • 排序:对需要代理的Advisor按一定规则排序

其中比较核心的就是获取、判断两个步骤

根据其方法逻辑,每个bean都需要对应进行获取一轮,因此这里也是做了缓存的,除了首次获取代理类需要加载,后面只需要从缓存中取并且做判断即可

findCandidateAdvisors

也是两个大步骤,分别对应了两种代理写法

protected List<Advisor> findCandidateAdvisors() {
    // 1. 找Advisor的实现:找到系统中实现了Advisor接口的bean
    List<Advisor> advisors = super.findCandidateAdvisors();
    // 2. 封装@AspectJ注解类:找到系统中使用@Aspect标注的bean和bean中使用@Before,@After等标注的方法,将这些方法封装为一个个Advisor
    if (this.aspectJAdvisorsBuilder != null) {
       advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
    }
    return advisors;
}

这里可以看出来,要么Advisor是自己手写的实现类,要么是通过注解@AspectJ和各种注解方法写的,这两类分别找

首先调用到父类的实现找系统中实现了Advisor接口的bean

protected List<Advisor> findCandidateAdvisors() {
    Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
    return this.advisorRetrievalHelper.findAdvisorBeans();
}

父类这里直接判断成员变量advisorRetrievalHelper 非空,但是好像并没有找到这个东西是在哪创建的

其实它的加载隐藏在AbstractAdvisorAutoProxyCreator的继承关系中,它实现了BeanFactoryAware,还记得Aware系的作用,是为了让某个类具备感知的能力,因此AbstractAdvisorAutoProxyCreator能够感知到BeanFactory

Aware的填充在获取bean的阶段,通过Aware系列接口的统一前置处理器进行插入的

http://www.chymfatfish.cn/archives/applicationcontext#%E6%AD%A5%E9%AA%A4%E4%B8%89%E3%80%81%E9%85%8D%E7%BD%AEaware%E5%88%9D%E5%A7%8B%E5%8C%96%E6%97%B6%E5%89%8D%E7%BD%AE%E5%A4%84%E7%90%86%E5%99%A8

因此作为Aware接口,AbstractAdvisorAutoProxyCreator的实现类在加载的时候会调用setBeanFactory方法

public void setBeanFactory(BeanFactory beanFactory) {
    super.setBeanFactory(beanFactory);
    if (!(beanFactory instanceof ConfigurableListableBeanFactory)) {
       throw new IllegalArgumentException(
             "AdvisorAutoProxyCreator requires a ConfigurableListableBeanFactory: " + beanFactory);
    }
    initBeanFactory((ConfigurableListableBeanFactory) beanFactory);
}
// AnnotationAwareAspectJAutoProxyCreator中的实现
protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
	super.initBeanFactory(beanFactory);
	if (this.aspectJAdvisorFactory == null) {
		this.aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory(beanFactory);
	}
	this.aspectJAdvisorsBuilder =
			new BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory);
}
// 父类实现
protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    this.advisorRetrievalHelper = new BeanFactoryAdvisorRetrievalHelperAdapter(beanFactory);
}

可以看到这时advisorRetrievalHelper 属性被加载了,子类实现中还额外加载了aspectJAdvisorFactory 、aspectJAdvisorsBuilder

然后把找Advisor的流程委托给BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans点我跳转

而基于@AspectJ注解的方法委托给BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors点我跳转

createProxy

createProxy方法是一套spring提供的通过ProxyFactory创建代理流程的标准写法,不仅spring自己使用,日常开发也可以使用,可以学习这套写法

ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);

开始有一个ProxyFactory的创建。这里调用ProxyFactory的空参构造,自动调用到父类ProxyCreatorSupport的空参构造。

public ProxyCreatorSupport() {
    this.aopProxyFactory = new DefaultAopProxyFactory();
}

可知这里构造了一个DefaultAopProxyFactory,到这时获取的ProxyFactory里面就承载了一个DefaultAopProxyFactory

然后是copyFrom方法配置了部分成员变量

public void copyFrom(ProxyConfig other) {
    Assert.notNull(other, "Other ProxyConfig object must not be null");
    this.proxyTargetClass = other.proxyTargetClass;
    this.optimize = other.optimize;
    this.exposeProxy = other.exposeProxy;
    this.frozen = other.frozen;
    this.opaque = other.opaque;
}

这几个变量都是用来确定使用jdk代理还是cglib代理的,后面在DefaultAopProxyFactory判断jdk代理还是cglib代理

if (proxyFactory.isProxyTargetClass()) {
    // Explicit handling of JDK proxy targets and lambdas (for introduction advice scenarios)
    if (Proxy.isProxyClass(beanClass) || ClassUtils.isLambdaClass(beanClass)) {
       // Must allow for introductions; can't just set interfaces to the proxy's interfaces only.
       for (Class<?> ifc : beanClass.getInterfaces()) {
          proxyFactory.addInterface(ifc);
       }
    }
}
else {
    // No proxyTargetClass flag enforced, let's apply our default checks...
    if (shouldProxyTargetClass(beanClass, beanName)) {
       proxyFactory.setProxyTargetClass(true);
    }
    else {
       evaluateProxyInterfaces(beanClass, proxyFactory);
    }
}

然后这里是评估proxyTargetClass属性,后面判断jdk代理还是cglib代理,后面在DefaultAopProxyFactory判断jdk代理还是cglib代理

Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);

这一块是基于之前获取到的advisor创建增强器

return proxyFactory.getProxy(classLoader);

然后就到了最核心的部分:通过ProxyFactory获取代理,参考getProxy方法

BeanFactoryAdvisorRetrievalHelper - 找Advisor实现类

findAdvisorBeans

String[] advisorNames = this.cachedAdvisorBeanNames;
if (advisorNames == null) {
    // Do not initialize FactoryBeans here: We need to leave all regular beans
    // uninitialized to let the auto-proxy creator apply to them!
    advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
          this.beanFactory, Advisor.class, true, false);
    this.cachedAdvisorBeanNames = advisorNames;
}

首先证实了上面的推断,确实是使用的缓存模式

如果缓存中不存在,委托BeanFactoryUtils#beanNamesForTypeIncludingAncestors 方法获取当前BeanFactory中所有实现了Advisor接口的bean ,点我跳转

for (String name : advisorNames) {
    // 是否合格,是一个留给开发者自行实现的方法,不实现默认为true
    if (isEligibleBean(name)) {
       if (this.beanFactory.isCurrentlyInCreation(name)) {
          if (logger.isTraceEnabled()) {
             logger.trace("Skipping currently created advisor '" + name + "'");
          }
       }
       else {
          // 说明创建好了,通过getBean取出bean,存入advisors
          try {
             advisors.add(this.beanFactory.getBean(name, Advisor.class));
          }
          catch (BeanCreationException ex) {
            ……
    }
}

然后方法是一个for循环,遍历刚刚找到的所有实现了Advisor接口的bean

首先调用isCurrentlyInCreation过滤+判断当前bean还在创建中,如果不在创建中了,则执行getBean方法加载bean,然后添加到结果advisors中

BeanFactoryAspectJAdvisorsBuilder - 找@AspectJ注解类

buildAspectJAdvisors

String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
       this.beanFactory, Object.class, true, false);
for (String beanName : beanNames) {
    ……
}

跟BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans一样,都是基于 BeanFactoryUtils#beanNamesForTypeIncludingAncestors 方法做的,不同的是,这里找所有Object的实现类,即全部bean的beanName

找到全部beanName后,开始遍历,然后看到下面这块核心代码:

// 这个if判断bean是否被@Aspect注解
if (this.advisorFactory.isAspect(beanType)) {
    ……
    MetadataAwareAspectInstanceFactory factory =
           new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
    List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
    ……

首先委托AspectJAdvisorFactory#getAdvisors执行真正的封装流程,详情参考ReflectiveAspectJAdvisorFactory

if (this.beanFactory.isSingleton(beanName)) {
    this.advisorsCache.put(beanName, classAdvisors);
}
else {
    this.aspectFactoryCache.put(beanName, factory);
}
advisors.addAll(classAdvisors);

最后缓存的时候看下是单例,如果切面类是singleton类型,则将解析得到的Advisor进行缓存,否则将当前的factory进行缓存,以便再次获取时可以通过factory直接获取

ReflectiveAspectJAdvisorFactory

核心成员变量

// 过滤掉使用了Pointcut注解的方法

private static final MethodFilter adviceMethodFilter = ReflectionUtils.USER_DECLARED_METHODS
                   .and(method -> (AnnotationUtils.getAnnotation(method, Pointcut.class) == null));

这个过滤器用于过滤掉使用@Pointcut注解的方法,后面会用到

getAdvisors

Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
validate(aspectClass);

这里先调用validate方法过滤掉perflow或percflowbelow切点

核心步骤1 获取切点方法封装成advisor

for (Method method : getAdvisorMethods(aspectClass)) {
    ……
    Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);

找Advisor方法参考getAdvisorMethods,封装参考getAdvisor

核心步骤2 增加同步实例化增强器

if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
    Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
    advisors.add(0, instantiationAdvisor);
}

核心步骤3 获取DeclareParents注解

for (Field field : aspectClass.getDeclaredFields()) {
    Advisor advisor = getDeclareParentsAdvisor(field);
    if (advisor != null) {
       advisors.add(advisor);
    }
}

getAdvisorMethods

private List<Method> getAdvisorMethods(Class<?> aspectClass) {
    List<Method> methods = new ArrayList<>();
    ReflectionUtils.doWithMethods(aspectClass, methods::add, adviceMethodFilter);
    if (methods.size() > 1) {
       methods.sort(adviceMethodComparator);
    }
    return methods;
}

主体是反射,通过ReflectionUtils#doWithMethods 反射获取的所有方法,这里就用到了方法过滤器adviceMethodFilter

getAdvisor

validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());

首先过滤掉perflow或percflowbelow切点

AspectJExpressionPointcut expressionPointcut = getPointcut(
       candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());

到这里已经是方法级了,这里是用于找当前方法中所有的切点的方法入口,封装成AspectJExpressionPointcut。参考getPointcut方法

然后根据切点信息Advisor生成增强,实际上是把自己又封装了一次

return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
       this, aspectInstanceFactory, declarationOrderInAspect, aspectName);

参考InstantiationModelAwarePointcutAdvisorImpl,它的重点实际上就是它的构造函数

getPointcut

首先获取方法上的注解

AspectJAnnotation<?> aspectJAnnotation =
       AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);

参考AbstractAspectJAdvisorFactory#findAspectJAnnotationOnMethod 方法,见AbstractAspectJAdvisorFactory

然后进行封装并提取表达式

AspectJExpressionPointcut ajexp =
       new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
ajexp.setExpression(aspectJAnnotation.getPointcutExpression());

getAdvice

通过InstantiationModelAwarePointcutAdvisorImpl的构造函数调入

该方法最核心的功能就是按照切点注解的不同,生成不同的增强器。

switch (aspectJAnnotation.getAnnotationType()) {
    case 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都有一个公共父类AbstractAspectJAdvice,且实现了MethodInterceptor方法,每个切面方法注解分别对应着它的实现类,例如@Before对应着AspectJMethodBeforeAdvice,@After对应着AspectJAfterAdvice等

切面的具体实现流程就被封装在了这些Advice中,具体可以参考后面AbstractAspectJAdvice

这里只是先构造,具体用到可以参考后面JdkDynamicAopProxy和CGLib代理的实现部分,以JdkDynamicAopProxy为例

AbstractAspectJAdvisorFactory

成员变量

private static final Class<?>[] ASPECTJ_ANNOTATION_CLASSES = new Class<?>[] {
		Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class};

一个Class[]数组,封装了AspectJ框架中支持的所有注解

findAspectJAnnotationOnMethod

for (Class<?> clazz : ASPECTJ_ANNOTATION_CLASSES) {
    AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) clazz);
    if (foundAnnotation != null) {
       return foundAnnotation;
    }
}

这里可以看到是在遍历上面存好的注解Class,调用findAnnotation方法找所有的注解

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,找到的结果封装成AspectJAnnotation

InstantiationModelAwarePointcutAdvisorImpl - 封装切点信息增强

成员变量

// 切点方法增强获取的Advice
private Advice instantiatedAdvice;

构造函数

懒加载实例暂略,非懒加载单例的流程最核心是这几个变量的赋值

else {
    // A singleton aspect.
    this.pointcut = this.declaredPointcut;
    this.lazy = false;
    this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
}

其中instantiateAdvice负责最核心的根据注解中的信息初始化对应的增强器

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

这里可以看到传进来的就是前面ReflectiveAspectJAdvisorFactory#getPointcut 方法获取的切面的封装,然后这里传进来又调用回去,到ReflectiveAspectJAdvisorFactory#getAdvice方法,参考ReflectiveAspectJAdvisorFactory

而看过ReflectiveAspectJAdvisorFactory#getAdvice 方法就可以知道,InstantiationModelAwarePointcutAdvisorImpl是Advice的封装,如果是@Pointcut类型的,advice属性就是null,如果是五种切面方法对应的,advice属性就有值,这就是@Pointcut和五种切面方法的差异所在

BeanFactoryUtils

beanNamesForTypeIncludingAncestors

public static String[] beanNamesForTypeIncludingAncestors(
       ListableBeanFactory lbf, Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
    Assert.notNull(lbf, "ListableBeanFactory must not be null");
    String[] result = lbf.getBeanNamesForType(type, includeNonSingletons, allowEagerInit);
    if (lbf instanceof HierarchicalBeanFactory) {
       HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
       if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
          String[] parentResult = beanNamesForTypeIncludingAncestors(
                (ListableBeanFactory) hbf.getParentBeanFactory(), type, includeNonSingletons, allowEagerInit);
          result = mergeNamesWithParent(result, parentResult, hbf);
       }
    }
    return result;
}

这个方法粗略一看,首先入参比较多,lbf是传入的BeanFactory,type是要获取的bean的类型

因此这里实质上是执行了

String[] result = lbf.getBeanNamesForType(type, includeNonSingletons, allowEagerInit);

BeanFactory#getBeanNamesForType方法

参考DefaultListableBeanFactory的实现,就可以知道,这里确实只是获取的beanName,并没有开始执行bean加载

ProxyFactory

成员变量

private AopProxyFactory aopProxyFactory;

AOP代理工厂,它的初始化在构造函数:

public ProxyCreatorSupport() {
    this.aopProxyFactory = new DefaultAopProxyFactory();
}

getProxy

public Object getProxy(@Nullable ClassLoader classLoader) {
    return createAopProxy().getProxy(classLoader);
}

这里首先构造AopProxy,而AopProxy有两个实现,具体看proxy-target-class属性中选择了哪一种,分别对应了代理部分的jdk代理和CGLib代理,参考createAopProxy

构造好后调用AopProxy#getProxy 方法,而两类代理的实现分别是:

  • JDK代理:参考JdkDynamicAopProxy#getProxy点我跳转

  • CGLib代理:参考CglibAopProxy#getProxy 方法,暂不看了

createAopProxy

先看createAopProxy,方法继承自ProxyCreatorSupport

protected final synchronized AopProxy createAopProxy() {
    if (!this.active) {
       activate();
    }
    return getAopProxyFactory().createAopProxy(this);
}

继续看getAopProxyFactory

public AopProxyFactory getAopProxyFactory() {
    return this.aopProxyFactory;
}

这里取的是属性aopProxyFactory,即DefaultAopProxyFactory,因为AbstractAutoProxyCreator#createProxy 调用到这里时是走的无参构造,继续跟进createAopProxy方法

DefaultAopProxyFactory

createAopProxy

还记得AbstractAutoProxyCreator#createProxy 方法

// ------------------------------AbstractAutoProxyCreator#createProxy---------------------
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);

这里proxy封装了前面ReflectiveAspectJAdvisorFactory#getAdvice方法(点我跳转)找到的所有Advice

然后这个proxyFactory作为入参进createAopProxy方法

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    if (!NativeDetector.inNativeImage() &&
          (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
       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.");
       }
       if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) || ClassUtils.isLambdaClass(targetClass)) {
          return new JdkDynamicAopProxy(config);
       }
       return new ObjenesisCglibAopProxy(config);
    }
    else {
       return new JdkDynamicAopProxy(config);
    }
}

可以看到这里主要是做判断选择对应的代理模式,方法中已经详尽给出了spring在选择AOP代理的时候使用jdk代理还是cglib代理的方案,主要有三个方面:

  • config.isOptimize()⽤来控制通过CGLIB创建的代理是否使⽤激进的优化策略。除⾮完全了解AOP代理如何处理优化,否则不推荐⽤户使⽤这个设置。⽬前这个属性仅⽤于CGLIB代理,对于JDK动态代理(默认代理)⽆效

  • config.isProxyTargetClass()这个属性为true时,⽬标类本⾝被代理而不是目标类的接口。如果这个属性值被设为true,CGLIB代理将被创建,设置⽅式为<aop:aspectj-autoproxy- proxy-target-class="true"/>

  • hasNoUserSuppliedProxyInterfaces(config):是否存在代理接口

其中proxyTargetClass主要就是判断需要代理它本身还是代理它的接口,设置点就在ReflectiveAspectJAdvisorFactory#getAdvice方法(点我跳转

这里CGLib和JDK代理可以参考:

http://www.chymfatfish.cn/archives/dynamicproxy#%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E7%BC%96%E7%A0%81%E6%80%9D%E8%B7%AF

AbstractAspectJAdvice - 切面信息增强

通过几个切面增强的案例来分析其具体流程

AbstractAspectJAdvice有五个实现类,根据其实现接口,分成了两类:

  • 实现了MethodInterceptor的:AsepctJAfterAdvice、AspectJAfterThrowingAdvice,这些都直接实现invoke方法,都不需要继续封装了

  • 没实现MethodInterceptor的:AspectJMethodBeforeAdvice、AspectJAfterReturningAdvice、AspectJAroundAdvice,这些没有实现invoke方法,还需要继续封装

封装的逻辑还得看是JDK还是CGLib代理,以JDK为例,参考JdkDynamicAopProxy#invoke方法,点我跳转

公共构造函数

public AbstractAspectJAdvice(
       Method aspectJAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aspectInstanceFactory) {
    Assert.notNull(aspectJAdviceMethod, "Advice method must not be null");
    this.declaringClass = aspectJAdviceMethod.getDeclaringClass();
    this.methodName = aspectJAdviceMethod.getName();
    this.parameterTypes = aspectJAdviceMethod.getParameterTypes();
    // 存储切面方法
    this.aspectJAdviceMethod = aspectJAdviceMethod;
    this.pointcut = pointcut;
    this.aspectInstanceFactory = aspectInstanceFactory;
}

这个构造函数被多个实现调用,重点需要关注的切面方法的存储

this.aspectJAdviceMethod = aspectJAdviceMethod;

看这个构造函数的调用点,从ReflectiveAspectJAdvisorFactory#getAdvice 一路往上找到ReflectiveAspectJAdvisorFactory#getAdvisors

public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
    Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
    ……
    for (Method method : getAdvisorMethods(aspectClass)) {
	if (method.equals(ClassUtils.getMostSpecificMethod(method, aspectClass))) {
		……
		Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);

可知这里就是代理类中每一个AspectJ注解对应的方法,而这些实现类就是围绕这个方法做的操作

invokeAdviceMethod/invokeAdviceMethodWithGivenArgs

protected Object invokeAdviceMethod(JoinPoint jp, @Nullable JoinPointMatch jpMatch,
       @Nullable Object returnValue, @Nullable Throwable t) throws Throwable {
    return invokeAdviceMethodWithGivenArgs(argBinding(jp, jpMatch, returnValue, t));
}
protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
    Object[] actualArgs = args;
    if (this.aspectJAdviceMethod.getParameterCount() == 0) {
       actualArgs = null;
    }
    try {
       ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
       return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
    }
    catch (IllegalArgumentException ex) {
       throw new AopInvocationException("Mismatch on arguments to advice method [" +
             this.aspectJAdviceMethod + "]; pointcut expression [" +
             this.pointcut.getPointcutExpression() + "]", ex);
    }
    catch (InvocationTargetException ex) {
       throw ex.getTargetException();
    }
}

这两个连起来看,其实就是基于给定参数调用切面方法,核心在:

ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);

AspectJMethodBeforeAdvice - @Before切面增强

实现了before方法

public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
    invokeAdviceMethod(getJoinPointMatch(), null, null);
}

即直接调用切面方法

AspectJAfterAdvice - @After切面增强

public Object invoke(MethodInvocation mi) throws Throwable {
    try {
       return mi.proceed();
    }
    finally {
       invokeAdviceMethod(getJoinPointMatch(), null, null);
    }
}

实现的invoke方法,即先调用原始方法,再调用切面方法

AspectJAfterThrowingAdvice - @Throwing切面增强

public Object invoke(MethodInvocation mi) throws Throwable {
    try {
       return mi.proceed();
    }
    catch (Throwable ex) {
       if (shouldInvokeOnThrowing(ex)) {
          invokeAdviceMethod(getJoinPointMatch(), null, ex);
       }
       throw ex;
    }
}

也是实现invoke方法,这个就好理解一些

JdkDynamicAopProxy

http://www.chymfatfish.cn/archives/dynamicproxy#jdk%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86

封装了JDK动态代理实现,JDK动态代理的编程思路主要有三步:

代理类实现函数式接口InvocationHandler,在整个创建过程中,对于InvocationHandler的创建是最为核⼼的,在⾃定义的InvocationHandler中需要重写3个函数

  • 构造函数,将代理的对象传⼊。

  • invoke⽅法,此⽅法中实现了AOP增强的所有逻辑。

  • getProxy⽅法,此⽅法千篇⼀律,但是必不可少

因此在AOP的代理创建过程中,JdkDynamicAopProxy一定也遵循了这三个步骤。通过代码印证

成员变量

// 切面增强封装好的ProxyFactory,在AbstractAutoProxyCreator#createProxy中封装好一路传进来
private final AdvisedSupport advised;
// 被aop代理的对象
private final Class<?>[] proxiedInterfaces;

构造函数

public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
    Assert.notNull(config, "AdvisedSupport must not be null");
    if (config.getAdvisorCount() == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
       throw new AopConfigException("No advisors and no TargetSource specified");
    }
    this.advised = config;
    this.proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
    findDefinedEqualsAndHashCodeMethods(this.proxiedInterfaces);
}

主要是配置两个核心参数:advised和proxiedInterfaces

getProxy

public Object getProxy(@Nullable ClassLoader classLoader) {
    if (logger.isTraceEnabled()) {
       logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
    }
    return Proxy.newProxyInstance(determineClassLoader(classLoader), this.proxiedInterfaces, this);
}

这里就符合JDK动态代理实现的几个步骤了

其实到这里spring构造AOP的流程就结束了,但是根据代理的逻辑,真正执行的时候还是要调用invoke方法,因此继续看invoke到底实现了什么

invoke

invoke就是最终实现代理执行的地方了,虽然此方法不会在AOP创建过程中调用,但是真正方法执行的时候就会调用到这里

前面一些基本校验不看,直接看核心部分

// 获取当前方法拦截器
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
	// 如果没有拦截器,直接调用切点方法
	Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
	retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
} else{
	// 将拦截器封装在ReflectiveMethodInvocation中,生成连接器链
    MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
    // 执行拦截器链
    retVal = invocation.proceed();
}

首先获取当前方法的所有拦截器,参考AdvisedSupport#getInterceptorsAndDynamicInterceptionAdvice点我跳转,而AdvisedSupport的实现就是前面看过的ProxyFactory

这里实际上对应的是在AOP编程中,切面可以有多个方法适配,即既可以前处理,又可以后处理,异常处理等等,因此这里生成的代理类的invoke方法做的事情实际就是,为这个方法找到所有可以匹配的切面方法,构造成拦截器链,然后依次执行

执行拦截器方法可以参考MethodInvocation#proceed 方法,点我跳转

AdvisedSupport

getInterceptorsAndDynamicInterceptionAdvice

if (cached == null) {
    cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
          this, method, targetClass);
    this.methodCache.put(cacheKey, cached);
}
return cached;

核心在这里,取完存到缓存

AdvisorChainFactory advisorChainFactory = new DefaultAdvisorChainFactory();

这个成员变量也是使用的硬编码初始化,继续跟进getInterceptorsAndDynamicInterceptionAdvice方法

DefaultAdvisorChainFactory

getInterceptorsAndDynamicInterceptionAdvice

AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();

// ----------------GlobalAdvisorAdapterRegistry#getInstance------------------------
public static AdvisorAdapterRegistry getInstance() {
	return instance;
}

// --------------DefaultAdvisorAdapterRegistry#DefaultAdvisorAdapterRegistry------------------
public DefaultAdvisorAdapterRegistry() {
	registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
	registerAdvisorAdapter(new AfterReturningAdviceAdapter());
	registerAdvisorAdapter(new ThrowsAdviceAdapter());
}

首先获取注册机,可以看到这个注册机里面封装了三类Adapter,正好对应了前面没实现MethodInterceptor的三类,可知这里一定是有封装逻辑

Advisor[] advisors = config.getAdvisors();

这里拿到所有支持的Advisor切面方法,然后开始遍历

for (Advisor advisor : advisors) {
    if (advisor instanceof PointcutAdvisor) {
       ……
       for (MethodInterceptor interceptor : interceptors) {
	       interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
       }
    }
    else if (advisor instanceof IntroductionAdvisor) {
      ……
    }
    else {
       Interceptor[] interceptors = registry.getInterceptors(advisor);
       interceptorList.addAll(Arrays.asList(interceptors));
    }
}

这里遍历的内容就是判断切面方法类型,是Pointcut,还是introduction,都不是,剩下就是五类切面方法

如果是Pointcut方法,封装成InterceptorAndDynamicMethodMatcher,看它的类名,应该是个匹配器,专门用于匹配是否符合当前切面的,调用点在执行处理链的地方,参考ReflectiveMethodInvocation#proceed点我跳转

IntroductionAdvisor暂时不开

如果是五类切面方法,向下跟进DefaultAdvisorAdapterRegistry#getInterceptors 方法,点我跳转

DefaultAdvisorAdapterRegistry

构造方法

public DefaultAdvisorAdapterRegistry() {
	registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
	registerAdvisorAdapter(new AfterReturningAdviceAdapter());
	registerAdvisorAdapter(new ThrowsAdviceAdapter());
}

构造方法就是加载了三类注册器

public void registerAdvisorAdapter(AdvisorAdapter adapter) {
    this.adapters.add(adapter);
}

getInterceptors

public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
    List<MethodInterceptor> interceptors = new ArrayList<>(3);
    Advice advice = advisor.getAdvice();
    // 如果属于MethodInterceptor方法,即AsepctJAfterAdvice、AspectJAfterThrowingAdvice,不做处理,直接添加
    if (advice instanceof MethodInterceptor) {
       interceptors.add((MethodInterceptor) advice);
    }
    // 剩下三类,遍历注册器,封装interceptor
    for (AdvisorAdapter adapter : this.adapters) {
       if (adapter.supportsAdvice(advice)) {
          interceptors.add(adapter.getInterceptor(advisor));
       }
    }
    if (interceptors.isEmpty()) {
       throw new UnknownAdviceTypeException(advisor.getAdvice());
    }
    return interceptors.toArray(new MethodInterceptor[0]);
}

这里可以看到,对于AsepctJAfterAdvice、AspectJAfterThrowingAdvice,都不需要再封装了

而对于AspectJMethodBeforeAdvice、AspectJAfterReturningAdvice、AspectJAroundAdvice这三类,要封装成Interceptor

封装的目的就是,通过Interceptor适配统一的invoke方法,从而在JdkDynamicAopProxy#invoke方法中调用处理链的invoke方法

这里参考一个@Before注解对应的MethodBeforeAdviceAdapter

MethodBeforeAdviceAdapter

getInterceptor

public MethodInterceptor getInterceptor(Advisor advisor) {
    MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
    return new MethodBeforeAdviceInterceptor(advice);
}

这里比较简单,就是把advisor封装成MethodBeforeAdviceInterceptor

这个advisor显然就是前面找到的AbstractAspectJAdvice的实现类AspectJMethodBeforeAdvice

而封装的目的其实就是包装invoke方法,方便JdkDynamicAopProxy#invoke 方法调用

public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {
    ……
    @Override
    @Nullable
    public Object invoke(MethodInvocation mi) throws Throwable {
       this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
       return mi.proceed();
    }
}

封装的invoke方法也很简单,先执行advice的before方法,然后zhi执行原方法

到这里封装责任链的逻辑就OK了

ReflectiveMethodInvocation

构造函数

protected ReflectiveMethodInvocation(
       Object proxy, @Nullable Object target, Method method, @Nullable Object[] arguments,
       @Nullable Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {
    this.proxy = proxy;
    this.target = target;
    this.targetClass = targetClass;
    this.method = BridgeMethodResolver.findBridgedMethod(method);
    this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments);
    this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
}

先看构造函数,在JdkDynamicAopProxy#invoke中调用,这里比较核心的入参,target是执行对象,method是原始方法,arguments是参数,而interceptorsAndDynamicMethodMatchers就是在JdkDynamicAopProxy#invoke 中构造的处理链

proceed

是一个递归调用处理链的流程

public Object proceed() throws Throwable {
    // 判断递归结束条件
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
       return invokeJoinpoint();
    }
    // 取责任链
    Object interceptorOrInterceptionAdvice =
          this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    // 判断是Pointcut生成的切点方法
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
       ……
    }
    // 是五类切面方法
    else {
       ……
       return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

以@Before为例,发现是最后一个else流程,走到MethodBeforeAdviceAdapter#invoke流程,先执行advice#before方法,然后再递归调用ReflectiveMethodInvocation#proceed方法

AopUtils

findAdvisorsThatCanApply

核心方法是调用到canApply,提供了两种:判断有Introduction切面和没有后Introduction切面的

if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
	eligibleAdvisors.add(candidate);
}
……
if (canApply(candidate, clazz, hasIntroductions)) {
	eligibleAdvisors.add(candidate);
}

canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions)

return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
……
return canApply(pca.getPointcut(), targetClass, hasIntroductions);
……
else {
	return true;
}

这里分成IntroductionAdvisor、pointCut、普通切面处理。

IntroductionAdvisor的直接获取ClassFilter调用matches方法,pointCut类型要获取到pointCut再调用canApply判断,如果是切面直接返回true

canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions)

if (!pc.getClassFilter().matches(targetClass)) {
	return false;
}

获取Advisor中的ClassFilter属性并调用matches方法校验

MethodMatcher methodMatcher = pc.getMethodMatcher();
……
methodMatcher.matches(method, targetClass))

获取Advisor中的methodMatcher,调用matches方法校验

经过两步校验,可以确定是否能应用于当前类了。

有一个非常典型的应用就是对事务切面的校验,见事务部分补链接

0

评论区