Spring AOP 原理分析

155 阅读16分钟

理论知识

概念

AOP 利用了代理技术把『围绕』着目标代码的固定代码『抽取』出来,『封装』成固定的解决方案,哪里需要,套哪里。

术语

定义切面类 MyAspect 解释关键术语

/**
 * Create By IntelliJ IDEA
 * 使用 @Aspect 定义一个切面(切入点+通知)在哪个位置(切入点)做什么事情(通知)
 *
 * @author: zgz
 * @created: 2024/07/24
 */
@Aspect
@Component
public class MyAspect {

    /**
     * 切入点,代表需要在这个切入点附件执行通知。
     * 切点分为 execution 方式(用路径表达式指定哪些类织入切面)和 annotation 方式(可以指定被哪些注解修饰的代码织入切面)
     */
    @Pointcut("execution(public * com.zgz.proxy..*.TargetClass.test(..))")
    public void pointcut() {

    }

    /**
     * 在切入点执行的通知类型
     * <p>
     * 前置通知(@Before)           在我们执行目标方法之前运行。
     * 返回通知(@AfterReturning)   在我们的目标方法正常返回值后运行。
     * 异常通知(@AfterThrowing)    在我们的目标方法出现异常后运行。
     * 后置通知(@After)            在我们目标方法运行结束之后 ,不管有没有异常。
     * 环绕通知(@Around)           包括上面四种通知对应的所有位置。
     *
     * @param joinPoint joinPoint
     */
    @Before("pointcut()")
    public void onBefore(JoinPoint joinPoint) {
        System.out.println("onBefore:" + joinPoint.getSignature().getName() + "方法开始执行,参数:"
                + Arrays.asList(joinPoint.getArgs()));
    }

    @After("pointcut()")
    public void onAfter(JoinPoint joinPoint) {
        System.out.println("onAfter:" + joinPoint.getSignature().getName() + "方法执行结束,参数:"
                + Arrays.asList(joinPoint.getArgs()));
    }

    @AfterReturning(value = "pointcut()", returning = "result")
    public void afterReturning(JoinPoint joinPoint, Object result) {
        System.out.println("afterReturning:" + joinPoint.getSignature().getName() + "方法执行结束返回,参数:"
                + Arrays.asList(joinPoint.getArgs()) + ",返回值:" + result);
    }

    @AfterThrowing(value = "pointcut()", throwing = "exception")
    public void afterThrowing(JoinPoint joinPoint, Exception exception) {
        System.out.println("afterThrowing:" + joinPoint.getSignature().getName() + "方法执行出错,参数:"
                + Arrays.asList(joinPoint.getArgs()) + ",异常:" + exception);
    }

}

Spring5.x 执行顺序:

  • 正常情况:@Before —-> 目标方法 —-> @AfterReturning —-> @After
  • 异常情况:@Before —-> 目标方法 —-> @AfterThrowing —-> @After

关于@Around 这个集成注解,个人认为不如将具体的前置、后置逻辑代码分散到各自单独的注解中。具体使用参考:Spring 12: @Around环绕通知

代理技术

静态代理

编译或者类加载时进行切面的切入。JDK静态代理是通过直接编码创建的,编译时实现代理,编译完成后代理类是一个实际的 class 类

// 测试类
public class Client {

    public static void main(String[] args) {
        //创建目标对象(被代理对象)
        TeachDao teachDao = new TeachDao();

        //创建代理对象,同时将被代理对象传递给代理对象
        TeachDaoProxy teachDaoProxy = new TeachDaoProxy(teachDao);

        //通过代理对象的,调用被代理对象的方法
        //即:执行的是代理对象的方法,代理对象再去调用目标对象的方法
        teachDaoProxy.teach();
    }
}

public interface ITeacherDao {
    //授课的方法
    void teach();
}

// 目标类
public class TeachDao implements ITeacherDao {
    public void teach() {
        System.out.println("老师授课中。。。。");
    }
}

// 代理类
public class TeachDaoProxy implements ITeacherDao {
    //目标对象,通过接口来聚合
    private ITeacherDao target;

    //构造器
    public TeachDaoProxy(ITeacherDao target) {
        this.target = target;
    }

    public void teach() {
        System.out.println("代理 start...");
        target.teach();
        System.out.println("下一步操作,提交...");
    }
}

缺点:每个代理类只能为一个类服务,如果需要代理的类很多,那么就需要编写大量的代理类,比较繁琐

动态代理

运行时切入切面逻辑,分为 JDK 动态代理和 Cglib。Spring 默认使用 JDK 代理,SpringBoot 2.x版本默认使用 Cglib。

JDK 动态代理

利用反射机制,运行时动态创建代理类

代码示例

// 测试类
public class Client {
    public static void main(String[] args) {
        //创建目标对象
        ITeacherDao target = new TeachDao();

        //给目标对象,创建代理对象,可以强转为 ITeachDao
        ITeacherDao proxyInstance = (ITeacherDao) new ProxyFactory(target).getProxyInstance();

        //在内存中生成了代理对象
        System.out.println("proxyInstance="+proxyInstance.getClass());

        //通过代理对象,调用目标对象方法
        proxyInstance.teach();
    }
}

public interface ITeacherDao {
    //授课的方法
    void teach();
}

// 目标类
public class TeachDao implements ITeacherDao {
    public void teach() {
        System.out.println("老师授课中。。。。");
    }
}

// 创建代理的工厂类
public class ProxyFactory {
    //创建一个目标对象,Object
    private Object target;

    //构造器,对 Object 进行初始化
    public ProxyFactory(Object target) {
        this.target = target;
    }

    //给目标对象生成代理对象
    public Object getProxyInstance(){
        //参数1. ClassLoader loader :指定当前对象使用的类加载器,获取类加载器的方法固定
        //参数2. Class<?>[] interfaces: 目标对象实现的接口类型,使用泛型方法确认类型
        //参数3. InvocationHandler h : 事情处理,执行目标对象的方法时,会触发事情处理器方法, 会把当前执行 的目标对象方法作为参数传入
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("jdk 代理 start....");
                        //反射机制调用目标对象的方法
                        Object returnVal = method.invoke(target, args);
                        System.out.println("jdk 代理 提交....");
                        return returnVal;
                    }
                });
    }
}

核心是 InvocationHandler 每一个代理的(基于接口),实现方式

  1. 编写生成类实现InvocationHandler接口,覆盖 invoke 方法,实现切面逻辑。
  2. 通过 Proxy#newInstance() 静态方法来获取代理对象。

Cglib 动态代理

Cglib是一个第三方代码生成库,是对ASM字节码技术的二次封装,在运行时动态生成代理对象。直接操作字节码,效率高需要额外引入 jar

代码示例

// 测试类
public class Client {
    public static void main(String[] args) {
        //创建目标对象
        TeachDao target = new TeachDao();

        //获取到代理对象,并将目标对象传递给代理对象
        TeachDao proxyInstance = (TeachDao) new ProxyFactory(target).getProxyInstance();

        //执行代理对象方法,触发 intercept 方法,从而实现对目标方法的调用
        proxyInstance.teach();
    }
}

// 目标类
public class TeachDao {
    public void teach(){
        System.out.println("老师授课中,cglib 代理不需要实现接口");
    }
}

// 创建代理的工厂类
public class ProxyFactory implements MethodInterceptor {

    //维护一个目标对象
    private Object target;

    //构造器,传入一个被代理的对象
    public ProxyFactory(Object target) {
        this.target = target;
    }

    //返回一个代理对象:是 target 的代理对象
    public Object getProxyInstance(){
        //1. 创建一个工具类对象
        Enhancer enhancer = new Enhancer();
        //2. 设置父类
        enhancer.setSuperclass(target.getClass());
        //3. 设置回调函数
        enhancer.setCallback(this);
        //4. 创建子类对象,即代理对象
        return enhancer.create();
    }

    //重写 intercept 方法,会调用目标对象的方法
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("Cglib代理模式,start...");
        Object returnVal = method.invoke(target, args);
        System.out.println("Cglib代理模式,提交");
        return returnVal;
    }

}

实现方式

  1. 编写生成类实现MethodInterceptor接口,覆盖intercept方法,实现切面逻辑。
  2. new一个Enhancer类来实现父子类的委托。
  3. 将实现了MethodInterceptor的类作为入参放入Enhancercallbacks属性里。
  4. 通过enhancer对象的#create()方法得到代理对象。

Spring 中的动态代理

Spring 中不可能直接去写 Cglib 或者 JDK 的动态代理,他是基于这俩做了封装。对外提供了org.springframework.aop.framework.ProxyFactory 代理工厂创建代理对象,会自动判断使用 Cglib 还是 JDK 动态代理。

ProxyFactory 使用的简化流程:

// new 一个 ProxyFactory 实例
ProxyFactory proxyFactory = new ProxyFactory();
// 拷贝代理配置
proxyFactory.copyFrom(this);
// 添加 Advisors
proxyFactory.addAdvisors(advisors);
// 设置 TargetSource
proxyFactory.setTargetSource(targetSource);
// 生成代理类
return proxyFactory.getProxy(classLoader);

回顾 AOP 使用方式

我们已经给出了一个切面类 MyAspect 下面创建一个目标类 TargetClass 包含需要被代理增强的方法

@Component
public class TargetClass {
    public String test(String value) {
        System.out.println("目标方法test被执行");
        if (!StringUtils.hasLength(value)) {
            throw new RuntimeException("value不能为空");
        }
        return value;
    }
}

pom 文件中需要引入的依赖

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

启动类

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableAsync
public class AlgorithmApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(AlgorithmApplication.class, args);
        TargetClass targetClass = context.getBean(TargetClass.class);
        targetClass.test("hello aop");
    }
}

执行结果

onBefore:test方法开始执行,参数:[hello aop]
目标方法test被执行
onAfter:test方法执行结束,参数:[hello aop]
afterReturning:test方法执行结束返回,参数:[hello aop],返回值:hello aop

AOP源码分析

@EnableAspectJAutoProxy

我们引入了 spring boot 开箱即用的 srtart:spring-boot-starter-aop 那么 spring boot 启动时是如何自动加载 aop 中的配置呢?

SpringBootApplication 注解

启动类上存在核心注解 @SpringBootApplication 点进去发现核心注解有三个

  • @SpringBootConfiguration: 标识此类为配置类,内部有 @Configuration 注解
  • @EnableAutoConfiguration:开启自动导入配置
  • @ComponentScan:包扫描
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
      @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

   Class<?>[] exclude() default {};

   String[] excludeName() default {};

   String[] scanBasePackages() default {};

   Class<?>[] scanBasePackageClasses() default {};
}
SpringBootConfiguration 注解
@Configuration
public @interface SpringBootConfiguration {

}
ComponentScan 注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {

   String[] value() default {};

   @AliasFor("value")
   String[] basePackages() default {};
  
   Class<?>[] basePackageClasses() default {};

   // 省略
}

使用xml配置的时候我们使用 <context:component-scan base-package=""></context:component-scan> 其中 base-package 指定扫描路径,路径下所有被@Component等注解标注的类都将被自动加载进入 IOC 容器

EnableAutoConfiguration 注解
@AutoConfigurationPackage // 自动导包
@Import(AutoConfigurationImportSelector.class) // 导入自动配置导入选择器
public @interface EnableAutoConfiguration {
   Class<?>[] exclude() default {};

   String[] excludeName() default {};
}
@AutoConfigurationPackage

@AutoConfigurationPackage 通过 debug org.springframework.boot.autoconfigure.AutoConfigurationPackages.Registrar#registerBeanDefinitions 发现,此注解会将主配置类(@SpringBootApplication 标注的类)所在的包下面所有的组件都扫描注冊到 spring 容器中

image.png

image.png

@Import(AutoConfigurationImportSelector.class)

AutoConfigurationImportSelector 开启自动配置类的导包的选择器,即有选择性的导入自动配置包

先看下 AutoConfigurationImportSelector 的UML图:

image.png 可以看到 AutoConfigurationImportSelector 实现了 ImportSelector 接口的selectImports 方法。

public interface ImportSelector {

   /**
    * 方法返回类的全类名数组(即需要导入到IOC容器中组件的全类名数组)
    * 包含一个`AnnotationMetadata`类型入参,通过这个参数我们可以获取到使用`ImportSelector`的类的全部注解信息。
    */
   String[] selectImports(AnnotationMetadata importingClassMetadata);
}

下面重点关注下 AutoConfigurationImportSelector 类的selectImports 方法。

public String[] selectImports(AnnotationMetadata annotationMetadata) {
   if (!isEnabled(annotationMetadata)) {
      return NO_IMPORTS;
   }
   AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
         .loadMetadata(this.beanClassLoader);
   AnnotationAttributes attributes = getAttributes(annotationMetadata);
   // 获取候选配置类
   List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
   
   configurations = removeDuplicates(configurations);
   Set<String> exclusions = getExclusions(annotationMetadata, attributes);
   checkExcludedClasses(configurations, exclusions);
   configurations.removeAll(exclusions);
   configurations = filter(configurations, autoConfigurationMetadata);
   fireAutoConfigurationImportEvents(configurations, exclusions);
   return StringUtils.toStringArray(configurations);
}

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
   
   // 只有loadFactoryNames方法要找到自动的配置类返回才不会报错
   List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
         getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
   
   // 在META-INF/spring.factories中找不到自动配置类。如果您使用的是自定义打包,请确保该文件是正确的。
   Assert.notEmpty(configurations,
         "No auto configuration classes found in META-INF/spring.factories. If you "
               + "are using a custom packaging, make sure that file is correct.");
   return configurations;
}

image.png

下面看下SpringFactoriesLoaderloadSpringFactories()方法和loadFactoryNames()方法

/**
 * 根据SpringBoot的启动生命流程,当需要加载自动配置类时,
 * 就会传入org.springframework.boot.autoconfigure.EnableAutoConfiguration参数,
 * 从map中查找key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的值,
 * 这些值通过反射将对象加到容器中,之后的作用就是用它们来做自动配置,这就是Springboot自动配置开始的地方
 */
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
   String factoryClassName = factoryClass.getName();
   return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
/**
 * 从“META-INF/spring.factories”中加载给定类型的工厂实现的完全限定类名放到map中
 */
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
	MultiValueMap<String, String> result = cache.get(classLoader);
	if (result != null) {
		return result;
	}

	try {
		// 获取 META-INF/spring.factories 枚举信息
		Enumeration<URL> urls = (classLoader != null ?
				classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
				ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));

		// 配置信息放到map里
		result = new LinkedMultiValueMap<>();
		while (urls.hasMoreElements()) {
			URL url = urls.nextElement();
			UrlResource resource = new UrlResource(url);
			Properties properties = PropertiesLoaderUtils.loadProperties(resource);
			for (Map.Entry<?, ?> entry : properties.entrySet()) {
				List<String> factoryClassNames = Arrays.asList(
						StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
				result.addAll((String) entry.getKey(), factoryClassNames);
			}
		}

		// 缓存
		cache.put(classLoader, result);
		return result;
	}
	catch (IOException ex) {
		throw new IllegalArgumentException("Unable to load factories from location [" +
				FACTORIES_RESOURCE_LOCATION + "]", ex);
	}
}

debug贴图

d2c9d36359a9bd8752671a102597a662.png

f5ebfa521149867486b97138cba285ca.png

image.png

这样我们的 AopAutoConfiguration 就加载到了 Spring IOC 容器中,下面看下 AopAutoConfiguration

/**
 * Auto-configuration for Spring's AOP support. Equivalent to enabling EnableAspectJAutoProxy in your configuration.
 * The configuration will not be activated if spring.aop.auto=false. 
 * The proxyTargetClass attribute will be true, by default, 
 * but can be overridden by specifying spring.aop.proxy-target-class=false.
 *
 * Spring AOP支持的自动配置。相当于在配置中启用EnableAspectJAutoProxy。
 * 如果spring.aop.auto=false,配置将不被激活。默认情况下,proxyTargetClass属性将为true,
 * 但可以通过指定spring.aop.proxy-target-class=false来覆盖。
 * @author Dave Syer
 * @author Josh Long
 * @see EnableAspectJAutoProxy
 */
@Configuration
// 当 IOC 容器中存在 EnableAspectJAutoProxy, Aspect, Advice,AnnotatedElement 类时,才会将 AopAutoConfiguration 加载到 IOC 容器
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class,AnnotatedElement.class })
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {

   
	@Configuration
	@EnableAspectJAutoProxy(proxyTargetClass = false)
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false)
	public static class JdkDynamicAutoProxyConfiguration {

	}

	@Configuration
   // 将 proxyTargetClass 属性设置为 true,意味着使用 CGLIB 代理。
	@EnableAspectJAutoProxy(proxyTargetClass = true)
   // 这个条件注解确保只有当 spring.aop.proxy-target-class 属性的值为 true 或者该属性不存在时(matchIfMissing = true),这个配置类才会被激活
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)
	public static class CglibAutoProxyConfiguration {

	}
}

继续往下跟

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
// 通过@Import导入了AspectJAutoProxyRegistrarAspectJ自动代理注册器
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

	boolean proxyTargetClass() default false;

	boolean exposeProxy() default false;
}

/**
 * 通过实现 ImportBeanDefinitionRegistrar 手动往 IOC 容器注册 Bean
 */
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

	@Override
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

		// 往IOC容器里注册了一个类型为AnnotationAwareAspectJAutoProxyCreator(注解驱动的AspectJ自动代理创建器)的Bean
		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

		AnnotationAttributes enableAspectJAutoProxy =
				AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
		if (enableAspectJAutoProxy != null) {
			if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
				AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
			}
			if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
				AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
			}
		}
	}

}

/**
 * 处理和配置 Spring AOP 相关 bean 的工具类
 */
public abstract class AopConfigUtils {
	public static final String AUTO_PROXY_CREATOR_BEAN_NAME = "org.springframework.aop.config.internalAutoProxyCreator";

	@Nullable
	public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
		return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
	}

	@Nullable
	public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry,
			@Nullable Object source) {

		return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
	}

	@Nullable
	private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry,
			@Nullable Object source) {

		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");

		// 判断容器中是否有该 bean
		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) {
					apcDefinition.setBeanClassName(cls.getName());
				}
			}
			return null;
		}

		// 通过 RootBeanDefinition 手动注册
		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;
	}
}

至此,@EnableAspectJAutoProxy注解往 IOC 容器中注册了类型为AnnotationAwareAspectJAutoProxyCreator的Bean,Bean名称为org.springframework.aop.config.internalAutoProxyCreator

小结
  1. spring boot 项目启动,通过启动类上的@SpringBootApplication注解加载@EnableAutoConfiguration注解
  2. 通过@EnableAutoConfiguration加载@Import(AutoConfigurationImportSelector.class) 导入自动加载选择器
  3. 执行 AutoConfigurationImportSelector 选择器的 selectImports() 方法,加载ClassPath下的META-INF/spring.factories文件来动态的注入AutoConfiguration类(这里用 AopAutoConfiguration举例)
  4. 当pom文件中引入spring-aopaspectjweaver两个模块后,EnableAspectJAutoProxy.class, Aspect.class, Advice.class, AnnotatedElement.class这几个类就存在了,即AopAutoConfiguration类开始加载,进而加载了AnnotationAwareAspectJAutoProxyCreator bean

贴个知识点:@Conditional 根据不同的条件,来进行不同的条件判断,如果满足指定的条件,那么配置类里边的配置才会生效。

注解作用
@ConditionalOnClass当类路径中存在指定的类时,该条件满足。
@ConditionalOnMissingClass当类路径中不存在指定的类时,该条件满足。
@ConditionalOnBean当依赖注入(DI)容器中存在指定类型的 Bean 时,该条件满足。
@ConditionalOnMissingBean当依赖注入(DI)容器中不存在指定类型的 Bean 时,该条件满足。
@ConditionalOnSingleCandidate当依赖注入(DI)容器中该类型的 Bean 只有一个或者标记了 @Primary 的只有一个时,该条件满足。
@ConditionalOnExpression当指定的 Spring Expression Language (SpEL) 表达式的结果为 true 时,该条件满足。
@ConditionalOnProperty当指定的属性设置或者值与条件匹配时,该条件满足。
@ConditionalOnResource当指定的文件存在于类路径上时,该条件满足。
@ConditionalOnJndi当指定的 JNDI 资源存在于 JNDI 名称上时,该条件满足。
@ConditionalOnJava当指定的 Java 版本存在时,该条件满足。
@ConditionalOnWebApplication当应用程序是 Web 应用时,该条件满足。
@ConditionalOnNotWebApplication当应用程序不是 Web 应用时,该条件满足。

AOP代理对象生成过程

通过上面的分析我们定位到了类 AnnotationAwareAspectJAutoProxyCreator,先看下此类的层级关系。

image.png

public interface BeanPostProcessor {

	/**
	 * bean 初始化前执行此方法
	 */
	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	/**
	 * bean 初始化之后执行此方法
	 */
	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

}

public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {

	/**
	 * bean实例化前调用该方法
	 */
	@Nullable
	default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
		return null;
	}

	/**
	 * bean实例化后调用该方法
	 */
	default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
		return true;
	}

	/**
	 * bean属性赋值后调用该方法,并且 postProcessAfterInstantiation 方法必须返回true
	 */
	@Nullable
	default PropertyValues postProcessPropertyValues(
			PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {

		return pvs;
	}

}

BeanPostProcessor 接口和 InstantiationAwareBeanPostProcessor 接口包含bean初始化、实例前后进行自定义操作的方法,通过分析我们将断点打在org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessBeforeInstantiationorg.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization

image.png

通过debug发现 postProcessBeforeInstantiation 方法返回了 null,我们先不关注具体bean 的前置处理内部逻辑,跳转到 postProcessAfterInitialization 方法。

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
   if (bean != null) {
      Object cacheKey = getCacheKey(bean.getClass(), beanName);
      if (!this.earlyProxyReferences.contains(cacheKey)) {
         // 如果bean有资格被代理,包装这个bean。重点关注
         return wrapIfNecessary(bean, beanName, cacheKey);
      }
   }
   return bean;
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
   // 一些校验先跳过
   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;
   }

   // Create proxy if we have advice.
   // 获取该bean对应的通知方法集合,对应我们在MyAspect中定义的通知方法
   // getAdvicesAndAdvisorsForBean 包含逻辑:
   //  1. 获取所有的通知方法(切面里定义的各个方法);
   //  2. 通过切点表达式判断这些通知方法是否可为当前Bean所用;
   //  3. 如果有符合的通知方法,则对它们进行排序(排序规则不同版本Spring有所不同,上面已经提及过)。
   Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
   
   if (specificInterceptors != DO_NOT_PROXY) {
      this.advisedBeans.put(cacheKey, Boolean.TRUE);

      // 通知方法不为空,创建目标bean的代理
      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;
}

image.png

然后我们进入createProxy 方法

image.png

68213607eb1bdb33689b5fb4c662ef47.png 标注

  • ①代理后的对象
  • ②被代理的对象
  • ③代理对象的通知方法集合

后续执行目标类的目标方法时,即:targetClass.test("hello aop"); 从IOC容器中获取的目标类即为代理后的对象。执行目标方法时,代理对象会执行相应的通知方法链。

生成拦截器链MethodInterceptor

targetClass.test("hello aop"); 打上断点 image.png

step into 跳转到了CglibAopProxyintercept方法中

image.png 然后调用getInterceptorsAndDynamicInterceptionAdvice方法

8138829d6aeceff5fea9aeff9d5d4ada.png

进入org.springframework.aop.framework.DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice方法

7de1ec3c3ad6da609d4c5bd392b2c18c.png

该方法主要逻辑:遍历代理对象的通知集合然后判断是否可以应用于当前调用的目标方法,如果可以,则将该通知转换为 MethodInterceptor(直接转换,或者通过适配器转换),并添加到interceptorList集合中。

继续往下走,发现生成了拦截器链 chain

39da85afe9c48de1c255842fb3824639.png

ExposeInvocationInterceptor,是默认的拦截器,查看MethodInterceptor关系

image.png

链式调用

发现代码走到逻辑 retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed(); 处,创建了CglibMethodInvocation对象,并调用 proceed() 方法。

image.png

核心逻辑 proceed 方法

public Object proceed() throws Throwable {
   //	索引初始值为-1
   if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
      return invokeJoinpoint();
   }

   // 每次递归都会对 index++,得到后面需要调用的advice
   Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
   if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
      // Evaluate dynamic method matcher here: static part will already have
      // been evaluated and found to match.
      InterceptorAndDynamicMethodMatcher dm =
            (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
      if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
         return dm.interceptor.invoke(this);
      }
      else {
         // Dynamic matching failed.
         // Skip this interceptor and invoke the next in the chain.
         return proceed();
      }
   }
   else {
      // 调用 advice 的 invoke,参数是当前对象,传进去继续调用当前对象的 process 实现递归
      return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
   }
}

MethodBeforeAdviceInterceptor 为例子,看下前置拦截的实现,先执行切入的前置逻辑,再继续调用 proceed 方法进行下一个Interceptor的调用

public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
    private MethodBeforeAdvice advice;
    
    public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
        Assert.notNull(advice, "Advice must not be null");
        this.advice = advice;
    }
    
    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        // 执行前置逻辑
        this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
        // mi是传入的那个对象,然后又执行了 proceed, 继续递归
        return mi.proceed();
     }
}

贴下debug截图 第一个拦截器: d7c8ec8c09c2faacc42de50acdc0ebb1.png

step into 取出了第二个拦截器

1721885779837_63C7A5FA-1106-44d3-AA56-43D76FE7C79B.png

step into 取出第三个拦截器

1721885949189_36728968-61D9-46c7-A8BA-230B03200D28.png

step into 取出第四个拦截器

1721886132474_FC3413C9-38A1-4139-AE90-AA42C628E8F3.png

step into 取出第五个拦截器

MethodBeforeAdviceInterceptorinvoke方法第一行调用了通知方法before,此时控制台打印内容为:

onBefore:test方法开始执行,参数:[hello aop]

然后执行 invokeJoinpoint 方法,该方法内部通过反射调用了目标方法(这里为TargetClass的test方法)

image.png

目标方法执行没有异常,调用org.springframework.aop.aspectj.AspectJAfterAdvice#invoke 内的finally逻辑

image.png 执行后,控制台输出

image.png

然后若目标方法执行存在异常,会执行org.springframework.aop.aspectj.AspectJAfterThrowingAdvice#invoke 内部的catch逻辑

image.png

此时控制台打印

onBefore:test方法开始执行,参数:[]
目标方法test被执行
onAfter:test方法执行结束,参数:[]
afterThrowing:test方法执行出错,参数:[],异常:java.lang.RuntimeException: value不能为空
Exception in thread "main" java.lang.RuntimeException: value不能为空
	at com.zgz.proxy.aop.TargetClass.test(TargetClass.java:17)
	at com.zgz.proxy.aop.TargetClass$$FastClassBySpringCGLIB$$4b4eee6d.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:746)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
	at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:52)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
	at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:47)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
	at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:52)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
	at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:62)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
	at com.zgz.proxy.aop.TargetClass$$EnhancerBySpringCGLIB$$e6a65a82.test(<generated>)
	at com.zgz.AlgorithmApplication.main(AlgorithmApplication.java:24)

然后方法执行结束出栈后,整个AOP的拦截器链调用随之结束了

image.png

image.png

最后贴一张大佬总结的流程图(重要)

image.png

总结

spring aop 源码学习笔记 参考链接