Spring AOP底层原理讲解(精简版)

729 阅读5分钟

原理概述

Spring容器启动过程中,借助BeanPostProcessor将@Pointcut指向的bean(目标对象)替换成动态代理生成的代理对象。代理类是目标类的子类,它内部持有一条方法拦截链List<MethodeInterceptor>(由通知注解标注的方法封装而成的),代理对象在执行原方法的过程中会递归执行拦截链,最终达到增强的效果。

原理讲解

请先仔细阅读Spring AOP的使用示例,在此基础上解释原理概述。

引入BeanPostProcessor

Spring AOP的使用示例,可以看到引入@EnnableAspectJAutoAopProxy注解开启AOP功能,注解内部逻辑是: image.png

  1. @EnnableAspectJAutoAopProxy通过@Import引入AspectJAutoProxyRegitstar类,它实现了ImportBeanDefinitionRegistar接口。
  2. AspectAutoProxyRegitstar通过registarBeanDefinitions()将AnnotationAwareAspectJAutoProxyCreator封装成RootBeanDefinition注册到Spring IOC容器。
  3. AnnotationAwareAspectJAutoProxyCreator的父类AbstractAutoProxyCreator实现了BeanPostProcessor的postProcessBeforeInstantiation()、postProcessAfterInitialization()。继承结构图如下: image.png

结论:@EnnableAspectJAutoAopProxy最终会引入BeanPostProcessor的实现类AnnotationAwareAspectJAutoProxyCreator

代理类对象替换目标bean

了解BeanPostProcessor之后,我们知道:Spring在启动过程中会遍历执行所有BeanPostProcessor实现类的postProcessBeforeInstantiation()、postProcessAfterInitialization()。也就会执行AnnotationAwareAspectJAutoProxyCreator的上述两个方法。

重点关注postProcessAfterInitialization()的实现:

  1. 方法入口:
    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;
	}
  1. 执行过程: image.png
    1. 前提bean是@Pointcut指定的目标对象。
    2. 先初始化一个ProxyFactory对象。
    3. ProxyFactory对象借助内部DefaultAopProxyFactory对象产生AopProxy实现类对象。
      • @Pointcut注解定义切点类型是接口,则得到JdkDynamicAopProxy对象。
      • @Pointcut注解定义切点类型是对象,则得到ObjenesisCglibAopProxy对象。
    4. PrxoyFactory再借助AopProxy实现类对象的getProxy(),通过动态代理生成代理对象。
      • JdkDynamicAopProxy使用JDK动态代理。
      • ObjenesisCglibAopProxy使用Cglib动态代理。

结论:Springr容器启动过程中会执行AnnotationAwareAspectJAutoProxyCreator的postProcessAfterInitialization(),根据不同的切点类型获取对应的AopProxy对象,调用getProxy(),通过jdk动态或cglib动态代理生成代理对象,替换原来的bean。

代理类是目标类的子类

上一章节介绍到,代理对象是通过AopProxy的getProxy()得到的,代码如下:

  1. JdkDynamicAopProxy使用JDK动态代理,生成的代理对象
    @Override
	public Object getProxy(@Nullable ClassLoader classLoader) {
		if (logger.isTraceEnabled()) {
			logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
		}
		Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
		findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
		return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
	}
  1. ObjenesisCglibAopProxy使用Cglib动态代理,生成代理对象
public Object getProxy(@Nullable ClassLoader classLoader) {
		if (logger.isTraceEnabled()) {
			logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());
		}

		try {
			Class<?> rootClass = this.advised.getTargetClass();
			Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");

			Class<?> proxySuperClass = rootClass;
			if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
				proxySuperClass = rootClass.getSuperclass();
				Class<?>[] additionalInterfaces = rootClass.getInterfaces();
				for (Class<?> additionalInterface : additionalInterfaces) {
					this.advised.addInterface(additionalInterface);
				}
			}

			// Validate the class, writing log messages as necessary.
			validateClassIfNecessary(proxySuperClass, classLoader);

			// Configure CGLIB Enhancer...
			Enhancer enhancer = createEnhancer();
			if (classLoader != null) {
				enhancer.setClassLoader(classLoader);
				if (classLoader instanceof SmartClassLoader &&
						((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
					enhancer.setUseCache(false);
				}
			}
			enhancer.setSuperclass(proxySuperClass);
			enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
			enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
			enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));

			Callback[] callbacks = getCallbacks(rootClass);
			Class<?>[] types = new Class<?>[callbacks.length];
			for (int x = 0; x < types.length; x++) {
				types[x] = callbacks[x].getClass();
			}
			// fixedInterceptorMap only populated at this point, after getCallbacks call above
			enhancer.setCallbackFilter(new ProxyCallbackFilter(
					this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
			enhancer.setCallbackTypes(types);

			// Generate the proxy class and create a proxy instance.
			return createProxyClassAndInstance(enhancer, callbacks);
		}
		catch (CodeGenerationException | IllegalArgumentException ex) {
			throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
					": Common causes of this problem include using a final class or a non-visible class",
					ex);
		}
		catch (Throwable ex) {
			// TargetSource.getTarget() failed
			throw new AopConfigException("Unexpected AOP exception", ex);
		}
	}
    protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
		enhancer.setInterceptDuringConstruction(false);
		enhancer.setCallbacks(callbacks);
		return (this.constructorArgs != null && this.constructorArgTypes != null ?
				enhancer.create(this.constructorArgTypes, this.constructorArgs) :
				enhancer.create());
	}

动态代理示例中,我们知道

  1. JDK动态代理生成的代理对象实现了目标接口,内部持有InvocationHandler的实现类,在执行目标方法时,实际上执行InvocationHandler实现类的invoke(),并将目标方法作为invoke()的参数传入。所以我们需要在invoke()写增强逻辑,并调用目标方法。
  2. Cglib动态代理生成的代理对象继承自原类。内部持有MethodInterceptor的实现类,在执行目标方法时,实际上执行的是MethodInterceptor的intercept()。所以我们需要在intercept()写增强逻辑,调用目标方法。

结论:不管使用jdk动态代理还是cglib动态代理,生成的代理类都是目标类的子类。也正应如此,我们才只能使用 MyTestInterface bean = applicationContext.getBean(MyTestInterface.class)获取到代理类对象。

递归执行方法拦截链

上一章节的最后我们介绍了:

  1. JdkDynamicAopProxy使用jdk动态代理生成代理对象,代理对象执行目标方法时,实际执行的是InvocationHandler实现类的invoke()
  2. ObjenesisCglibAopProxy使用cglib动态代理生成代理对象,代理对象执行目标方法时,实际执行的是MethodIntercepto实现类的intercept()

所以,我们想要知道Aop增强逻辑的执行过程,我们需要明确

  1. 切点为接口,代理类内部持有的InvocationHandler实现类的invoke()具体实现
  2. 切点为类,代理类内部持有的MethodInterceptor实现类的intercept()具体实现

通过下图解释Aop增强逻辑的执行过程:

image.png 结论:不管切点类型是接口还是类,最终都会初始化一个ReflactiveMethodInvocation对象,通过proceed()递归执行拦截链List,而这个方法拦截链本质是由通知注解标注的方法封装而成的。

留给读者思考

Spring Aop失效的情况有哪些? Spring Aop如何做到先执行@Before标注的方法,再执行@After标注的方法? Spring Aop在以下情况能否生成代理对象的,如果能,是怎么生成的?

  1. 接口A内部有一个a(),定义一个AOP将@Pointcut指向a(),存在多个类实现接口A。
  2. 接口A内部有一个a(),定义多个AOP将@Pointcut指向a()。
  3. 接口A内部有b()和c(),定义两个AOP将@Pointcut分别指向b()、c()。

最后

如果这篇文章对你理解Spring AOP有所帮助,可以点赞鼓励一下。如果想了解Spring AOP的源码,请移步Spring AOP源码阅读。最后推荐读者可以阅读《Spring 技术内幕》,这本书对你理解Spring、Spring MVC有很大的帮助。