原理概述
Spring容器启动过程中,借助BeanPostProcessor将@Pointcut指向的bean(目标对象)替换成动态代理生成的代理对象。代理类是目标类的子类,它内部持有一条方法拦截链List<MethodeInterceptor>(由通知注解标注的方法封装而成的),代理对象在执行原方法的过程中会递归执行拦截链,最终达到增强的效果。
原理讲解
请先仔细阅读Spring AOP的使用示例,在此基础上解释原理概述。
引入BeanPostProcessor
在Spring AOP的使用示例,可以看到引入@EnnableAspectJAutoAopProxy注解开启AOP功能,注解内部逻辑是:
- @EnnableAspectJAutoAopProxy通过@Import引入AspectJAutoProxyRegitstar类,它实现了ImportBeanDefinitionRegistar接口。
- AspectAutoProxyRegitstar通过registarBeanDefinitions()将AnnotationAwareAspectJAutoProxyCreator封装成RootBeanDefinition注册到Spring IOC容器。
- AnnotationAwareAspectJAutoProxyCreator的父类AbstractAutoProxyCreator实现了BeanPostProcessor的postProcessBeforeInstantiation()、postProcessAfterInitialization()。继承结构图如下:
结论:@EnnableAspectJAutoAopProxy最终会引入BeanPostProcessor的实现类AnnotationAwareAspectJAutoProxyCreator
代理类对象替换目标bean
了解BeanPostProcessor之后,我们知道:Spring在启动过程中会遍历执行所有BeanPostProcessor实现类的postProcessBeforeInstantiation()、postProcessAfterInitialization()。也就会执行AnnotationAwareAspectJAutoProxyCreator的上述两个方法。
重点关注postProcessAfterInitialization()的实现:
- 方法入口:
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是@Pointcut指定的目标对象。
- 先初始化一个ProxyFactory对象。
- ProxyFactory对象借助内部DefaultAopProxyFactory对象产生AopProxy实现类对象。
- @Pointcut注解定义切点类型是接口,则得到JdkDynamicAopProxy对象。
- @Pointcut注解定义切点类型是对象,则得到ObjenesisCglibAopProxy对象。
- PrxoyFactory再借助AopProxy实现类对象的getProxy(),通过动态代理生成代理对象。
- JdkDynamicAopProxy使用JDK动态代理。
- ObjenesisCglibAopProxy使用Cglib动态代理。
结论:Springr容器启动过程中会执行AnnotationAwareAspectJAutoProxyCreator的postProcessAfterInitialization(),根据不同的切点类型获取对应的AopProxy对象,调用getProxy(),通过jdk动态或cglib动态代理生成代理对象,替换原来的bean。
代理类是目标类的子类
上一章节介绍到,代理对象是通过AopProxy的getProxy()得到的,代码如下:
- 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);
}
- 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());
}
在动态代理示例中,我们知道
- JDK动态代理生成的代理对象实现了目标接口,内部持有InvocationHandler的实现类,在执行目标方法时,实际上执行InvocationHandler实现类的invoke(),并将目标方法作为invoke()的参数传入。所以我们需要在invoke()写增强逻辑,并调用目标方法。
- Cglib动态代理生成的代理对象继承自原类。内部持有MethodInterceptor的实现类,在执行目标方法时,实际上执行的是MethodInterceptor的intercept()。所以我们需要在intercept()写增强逻辑,调用目标方法。
结论:不管使用jdk动态代理还是cglib动态代理,生成的代理类都是目标类的子类。也正应如此,我们才只能使用 MyTestInterface bean = applicationContext.getBean(MyTestInterface.class)获取到代理类对象。
递归执行方法拦截链
上一章节的最后我们介绍了:
- JdkDynamicAopProxy使用jdk动态代理生成代理对象,代理对象执行目标方法时,实际执行的是InvocationHandler实现类的invoke()
- ObjenesisCglibAopProxy使用cglib动态代理生成代理对象,代理对象执行目标方法时,实际执行的是MethodIntercepto实现类的intercept()
所以,我们想要知道Aop增强逻辑的执行过程,我们需要明确
- 切点为接口,代理类内部持有的InvocationHandler实现类的invoke()具体实现
- 切点为类,代理类内部持有的MethodInterceptor实现类的intercept()具体实现
通过下图解释Aop增强逻辑的执行过程:
结论:不管切点类型是接口还是类,最终都会初始化一个ReflactiveMethodInvocation对象,通过proceed()递归执行拦截链List,而这个方法拦截链本质是由通知注解标注的方法封装而成的。
留给读者思考
Spring Aop失效的情况有哪些? Spring Aop如何做到先执行@Before标注的方法,再执行@After标注的方法? Spring Aop在以下情况能否生成代理对象的,如果能,是怎么生成的?
- 接口A内部有一个a(),定义一个AOP将@Pointcut指向a(),存在多个类实现接口A。
- 接口A内部有一个a(),定义多个AOP将@Pointcut指向a()。
- 接口A内部有b()和c(),定义两个AOP将@Pointcut分别指向b()、c()。
最后
如果这篇文章对你理解Spring AOP有所帮助,可以点赞鼓励一下。如果想了解Spring AOP的源码,请移步Spring AOP源码阅读。最后推荐读者可以阅读《Spring 技术内幕》,这本书对你理解Spring、Spring MVC有很大的帮助。