动态代理机制

Spring AOP 使用动态代理技术在运行期织入增强的代码,它使用了两种代理机制:一种是基于 JDK 的动态代理技术;另一种是基于 CGLib 的动态代理。之所以需要两种代理机制,很大程度上是因为 JDK 本身只提供接口的代理,而不支持类的代理。在 Spring Boot 中可通过如下配置指定代理方式:

  1. # true采用CGLib动态代理,为false采用JDK动态代理
  2. spring.aop.proxy-target-class=false

1. JDK 动态代理

JDK 动态代理允许开发者在运行期创建接口的代理实例,底层通过反射实现,主要涉及 java.lang.reflect 包中的两个类:Proxy 和 InvocationHandler。其中,InvocationHandler 是一个接口,可以通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态地将横切逻辑和业务逻辑编织在一起。而 Proxy 利用 InvocationHandler 动态创建一个符合某一接口的实例,生成目标类的代理对象。

  1. public interface InvocationHandler {
  2. /**
  3. * proxy:最终生成的代理实例,一般不会用到
  4. * method:被代理目标实例的某个具体方法
  5. * args:被代理实例某个具体方法的入参,在方法反射调用时使用
  6. */
  7. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
  8. }

JDK 动态代理使用示例:

  1. public static void main(String[] args) {
  2. // 原始接口的实现类
  3. Bird eagle = new Eagle("老鹰");
  4. // 通过Proxy创建一个符合接口的代理实例
  5. Bird proxy = (Bird) Proxy.newProxyInstance(
  6. ClassLoader.getSystemClassLoader(),
  7. new Class[]{Bird.class},
  8. new BirdProxy(eagle));
  9. // 通过代理类去执行getName业务方法
  10. System.out.println(proxy.getName());
  11. }
  12. public class BirdProxy implements InvocationHandler {
  13. private final Bird bird;
  14. public BirdProxy(Bird bird) {
  15. this.bird = bird;
  16. }
  17. // 通过持有原实现类,在调用原实现类的逻辑前后可注入增强逻辑
  18. @Override
  19. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  20. System.out.println("Get Bird Name");
  21. return method.invoke(bird, args);
  22. }
  23. }

在使用 JDK 动态代理时,对于 InvocationHandler 的创建是最为核心的。在自定义的 InvocationHandler 中需要重写构造函数,将代理的对象传入,还需要重写 invoke 方法,此方法中实现了 AOP 增强的所有逻辑。

2. CGLIB 动态代理

使用 JDK 动态代理有一个限制,即它只能为接口创建代理实例。对于没有通过接口定义业务方法的类,我们可以通过 CGLib 动态创建代理实例。CGLib 采用动态字节码注入技术,可以为一个类创建子类,在子类中采用方法拦截的技术拦截所有父类方法的调用并顺势织入横切逻辑。但要注意,CGLib 不能对目标类中的 final 或 private 方法进行代理。CGLib 动态代理使用示例:

  1. public class CglibProxy implements MethodInterceptor {
  2. // CGLib增强类
  3. private Enhancer enhancer = new Enhancer();
  4. public Object getProxy(Class clazz) {
  5. // 设置需要创建子类的类
  6. enhancer.setSuperclass(clazz);
  7. enhancer.setCallback(this);
  8. // 通过字节码技术动态创建子类实例
  9. return enhancer.create();
  10. }
  11. // 拦截父类所有方法的调用
  12. @Override
  13. public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
  14. System.out.println("before work......");
  15. // 通过代理类调用父类中的方法
  16. Object result = methodProxy.invokeSuper(o, objects);
  17. System.out.println("after work......");
  18. return result;
  19. }
  20. public static void main(String[] args) {
  21. CglibProxy proxy = new CglibProxy();
  22. // 通过动态生成子类的方式创建代理类
  23. Eagle eagle = (Eagle) proxy.getProxy(Eagle.class);
  24. eagle.getName();
  25. }
  26. }

JDK 在创建代理对象时的性能高于 CGLib,但生成的代理对象的运行性能却比 CGLib 的低。如果是 singleton 的代理,则推荐使用 CGLib 动态代理。

SpringBoot AOP 自动配置

在 Spring Boot 的自动配置中有如下配置:

  1. org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  2. org.springframework.boot.autoconfigure.aop.AopAutoConfiguration

当引入 spring-boot-starter-aop 依赖后,AopAutoConfiguration 自动配置的创建条件就满足了。在它的自动配置逻辑中,其核心是引入了 @EnableAspectJAutoProxy 注解,其内容如下:

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Import(AspectJAutoProxyRegistrar.class)
  5. public @interface EnableAspectJAutoProxy {
  6. /**
  7. * 动态代理方式采用CGLIB还是JDK,默认采用JDK动态代理,可通过spring.aop.proxy-target-class参数控制
  8. */
  9. boolean proxyTargetClass() default false;
  10. /**
  11. * 有时候目标对象内部的自我调用将无法实施切面中的增强
  12. * 当开启该属性后,内部方法调用可通过AopContext.currentProxy()获取当前代理类然后执行方法调用
  13. */
  14. boolean exposeProxy() default false;
  15. }

其中又通过 @Import 注解引入了 AspectJAutoProxyRegistrar 类,它是 ImportBeanDefinitionRegistrar 接口的实现类,最终其执行的核心逻辑如下:

  1. # 这里的clsAnnotationAwareAspectJAutoProxyCreator
  2. RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
  3. beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
  4. beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
  5. registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);

可以看到,最终会向容器中注册一个 Bean 定义,类型为 AnnotationAwareAspectJAutoProxyCreator,这个 Bean 就用来处理当前 Spring 上下文中所有 AspectJ 注解方法的类。

AnnotationAwareAspectJAutoProxyCreator

首先,看下这个类的层次结构:
image.png
在类的层级中,我们看到它实现了 BeanPostProcessor 接口,而实现 BeanPostProcessor 后,当 Spring 加载这个 Bean 时会在实例化后调用其 postProcessAfterInitialization 方法,而我们对于 AOP 逻辑的分析也由此开始。这个方法的具体实现在其父类 AbstractAutoProxyCreator 中,具体代码如下:

  1. @Override
  2. public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
  3. if (bean != null) {
  4. Object cacheKey = getCacheKey(bean.getClass(), beanName);
  5. if (this.earlyProxyReferences.remove(cacheKey) != bean) {
  6. // 如果这个bean需要被代理,则要进行封装
  7. return wrapIfNecessary(bean, beanName, cacheKey);
  8. }
  9. }
  10. return bean;
  11. }
  12. protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
  13. // 如果已经处理过
  14. if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
  15. return bean;
  16. }
  17. // 无须增强
  18. if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
  19. return bean;
  20. }
  21. // 给定的bean类是否代表一个基础设施类,基础设施类不应被代理
  22. // 或者配置了指定bean不需要自动代理
  23. if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
  24. this.advisedBeans.put(cacheKey, Boolean.FALSE);
  25. return bean;
  26. }
  27. // 如果存在增强方法则创建代理类
  28. // 对标记为@Aspect注解的类进行增强器的提取
  29. Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
  30. // 如果获取到了需要注入的增强逻辑则要针对增强创建代理
  31. if (specificInterceptors != DO_NOT_PROXY) {
  32. this.advisedBeans.put(cacheKey, Boolean.TRUE);
  33. // 创建代理
  34. Object proxy = createProxy(
  35. bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
  36. this.proxyTypes.put(cacheKey, proxy.getClass());
  37. return proxy;
  38. }
  39. this.advisedBeans.put(cacheKey, Boolean.FALSE);
  40. return bean;
  41. }

代理创建过程

1. createProxy

  1. // specificInterceptors为需要增强的逻辑
  2. // targetSource用来保存原对象
  3. protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
  4. @Nullable Object[] specificInterceptors, TargetSource targetSource) {
  5. if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
  6. AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
  7. }
  8. ProxyFactory proxyFactory = new ProxyFactory();
  9. // 获取当前类中相关属性
  10. proxyFactory.copyFrom(this);
  11. // 决定对于给定的bean是否应该使用targetClass而不是它的接口代理
  12. // 需要检查proxyTargeClass设置以及preserveTargetClass属性
  13. if (!proxyFactory.isProxyTargetClass()) {
  14. if (shouldProxyTargetClass(beanClass, beanName)) {
  15. // 直接对目标类进行代理
  16. proxyFactory.setProxyTargetClass(true);
  17. } else {
  18. // 添加代理接口
  19. evaluateProxyInterfaces(beanClass, proxyFactory);
  20. }
  21. }
  22. Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
  23. // 加入增强器
  24. proxyFactory.addAdvisors(advisors);
  25. // 设置要代理的类
  26. proxyFactory.setTargetSource(targetSource);
  27. // 定制代理
  28. customizeProxyFactory(proxyFactory);
  29. // 用来控制代理工厂被配置之后,是否还允许修改,默认为false,即在代理被配置后,不允许修改代理的配置
  30. proxyFactory.setFrozen(this.freezeProxy);
  31. if (advisorsPreFiltered()) {
  32. proxyFactory.setPreFiltered(true);
  33. }
  34. // 获取代理类,在这一步会进行代理类的创建
  35. return proxyFactory.getProxy(getProxyClassLoader());
  36. }

对于代理类的创建及处理,Spring 委托给 ProxyFactorγ 去处理,而在此函数中主要是对 ProxyFactory 的初始化操作,进而对真正的创建代理做准备,这些初始化操作包括如下内容:

  • 获取当前类中的属性。
  • 添加代理接口。
  • 封装 Advisor 并加入到 ProxyFactory 中。
  • 设置要代理的类。
  • 在 Spring 中还为子类提供定制函数 customizeProxyFactory,子类可对 ProxyFactory 做进一步封装
  • 进行获取代理操作。

ProxyFactory 内部是使用 JDK 或 CGLib 动态代理技术将增强应用到目标类中的。Spring 定义了 AopProxy 接口,并提供了两个实现类:CglibAopProxy、JdkDynamicAopProxy。前者使用 CGLib 动态代理技术创建代理,而后者使用 JDK 动态代理技术创建代理。在 Spring 4.0 版本还提供了 ObjenesisCglibAopProxy 实现类,它是 CglibAopProxy 的扩展,可以避免在创建代理类时调用构造函数。
image.png
此外,ProxyFactory 有几个常用的可配置属性:

  • target:代理的目标对象
  • proxyInterfaces:代理所要实现的接口,可以是多个接口。
  • interceptorNames:需要织入目标对象的 Bean 列表,采用 Bean 名称指定。这些 Bean 必须是实现了 MethodInterceptor 或 Advisor 的 Bean,配置中的顺序对应调用的顺序。
  • singleton:返回的代理是否是单实例,默认为单实例。
  • optimize:当设置为 true 时,强制使用 CGLib 动态代理。

2. createAopProxy

  1. protected final synchronized AopProxy createAopProxy() {
  2. if (!this.active) {
  3. activate();
  4. }
  5. // 创建代理
  6. return getAopProxyFactory().createAopProxy(this);
  7. }
  8. public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
  9. if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
  10. Class<?> targetClass = config.getTargetClass();
  11. if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
  12. return new JdkDynamicAopProxy(config);
  13. }
  14. return new ObjenesisCglibAopProxy(config);
  15. } else {
  16. return new JdkDynamicAopProxy(config);
  17. }
  18. }

Spring 是如何选取创建代理的方式呢?从 if 中的判断条件可以看到有 3个方面影响着 Spring 的判断。

  • optimize:用来控制通过 CGLIB 创建的代理是否使用激进的优化策略。除非完全了解 AOP 代理如何处理优化,否则不推荐用户使用这个设置。该属性仅用于 CGLIB 代理,对于 JDK 动态代理(默认)无效。

  • proxyTargetClass:这个属性为 true 时,目标类本身被代理而不是目标类的接口。如果这个属性值被设为 true,CGLIB 代理将被创建,可通过 spring.aop.proxy-target-class 属性进行设置。

  • hasNoUserSuppliedProxyInterfaces:是否存在代理接口。

因此,总结下来就是:如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理实现 AOP。如果目标对象实现了接口,可以强制使用 CGLIB 实现 AOP。如果目标对象没有实现接口,必须采用 CGLIB 库,Spring 会自动在 JDK 动态代理和 CGLIB 之间转换。

2.1 JdkDynamicAopProxy

查看 JdkDynamicAopProxy 的 getProxy 方法:

  1. @Override
  2. public Object getProxy(@Nullable ClassLoader classLoader) {
  3. Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
  4. findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
  5. return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
  6. }

JdkDynamicAopProxy 实现了 lnvocationHandler 接口,在重写的 invoke 方法中实现了 AOP 的核心逻辑:

  1. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  2. Object oldProxy = null;
  3. boolean setProxyContext = false;
  4. TargetSource targetSource = this.advised.targetSource;
  5. Object target = null;
  6. try {
  7. ......
  8. Object retVal;
  9. // 有时候目标对象内部的自我调用将无法实施切面中的增强,需要通过此属性暴露代理类
  10. if (this.advised.exposeProxy) {
  11. oldProxy = AopContext.setCurrentProxy(proxy);
  12. setProxyContext = true;
  13. }
  14. target = targetSource.getTarget();
  15. Class<?> targetClass = (target != null ? target.getClass() : null);
  16. // 获取当前方法的拦截器链
  17. List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
  18. // 如果没有发现任何拦截器那么直接调用切点方法
  19. if (chain.isEmpty()) {
  20. Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
  21. retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
  22. } else {
  23. // 将拦截器封装在ReflectiveMethodInvocation,以便使用其proceed进行拦截器链的调用
  24. MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
  25. // 执行拦截器链
  26. retVal = invocation.proceed();
  27. }
  28. // 返回结果
  29. Class<?> returnType = method.getReturnType();
  30. if (retVal != null && retVal == target && returnType != Object.class && returnType.isInstance(proxy) &&
  31. !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
  32. retVal = proxy;
  33. }
  34. return retVal;
  35. }
  36. }

上面的函数中最主要的工作就是创建了一个拦截器链,并使用 ReflectiveMethodInvocation 类进行了拦截器链的封装,而在 ReflectiveMethodInvocation 类的 proceed 方法中实现了拦截器的逐一调用,下面分析在 proceed 方法中是如何实现前置增强在目标方法前调用,后置增强在目标方法后调用的逻辑。

  1. public Object proceed() throws Throwable {
  2. // 执行完所有增强后,执行原实现类目标方法
  3. if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
  4. return invokeJoinpoint();
  5. }
  6. // 获取下一个要执行的拦截器
  7. Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
  8. if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
  9. // 动态匹配
  10. InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
  11. Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
  12. if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
  13. return dm.interceptor.invoke(this);
  14. } else {
  15. // 不匹配则不执行当前拦截器,继续递归下一个
  16. return proceed();
  17. }
  18. } else {
  19. // 普通拦截器,直接调用拦截器
  20. return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
  21. }
  22. }

2.2 CglibAopProxy

查看 CglibAopProxy 的 getProxy 方法,该方法实现了 Enhancer 的创建及接口封装。

  1. public Object getProxy(@Nullable ClassLoader classLoader) {
  2. try {
  3. Class<?> rootClass = this.advised.getTargetClass();
  4. Class<?> proxySuperClass = rootClass;
  5. ......
  6. // 验证 Class
  7. validateClassIfNecessary(proxySuperClass, classLoader);
  8. // 创建及配置CGLIB Enhancer
  9. Enhancer enhancer = createEnhancer();
  10. if (classLoader != null) {
  11. enhancer.setClassLoader(classLoader);
  12. if (classLoader instanceof SmartClassLoader && ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
  13. enhancer.setUseCache(false);
  14. }
  15. }
  16. enhancer.setSuperclass(proxySuperClass);
  17. enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
  18. enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
  19. enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));
  20. // 设置拦截器
  21. Callback[] callbacks = getCallbacks(rootClass);
  22. Class<?>[] types = new Class<?>[callbacks.length];
  23. for (int x = 0; x < types.length; x++) {
  24. types[x] = callbacks[x].getClass();
  25. }
  26. // 生成代理类,以及创建代理实例
  27. return createProxyClassAndInstance(enhancer, callbacks);
  28. }
  29. ......
  30. }
  31. protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
  32. enhancer.setInterceptDuringConstruction(false);
  33. enhancer.setCallbacks(callbacks);
  34. return (this.constructorArgs != null && this.constructorArgTypes != null ?
  35. enhancer.create(this.constructorArgTypes, this.constructorArgs) :
  36. enhancer.create());
  37. }

以上函数完整地阐述了一个创建 Spring 中的 Enhancer 的过程,其中最重要的是通过 getCallbacks 方法设置拦截器链。具体代码如下:

  1. private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
  2. // 对于 expose-proxy 属性的处理
  3. boolean exposeProxy = this.advised.isExposeProxy();
  4. boolean isFrozen = this.advised.isFrozen();
  5. boolean isStatic = this.advised.getTargetSource().isStatic();
  6. // 将拦截器链封装在 DynamicAdvisedInterceptor 中
  7. Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
  8. ......
  9. Callback[] mainCallbacks = new Callback[] {
  10. // 将拦截器链加入 Callback 中
  11. aopInterceptor,
  12. targetInterceptor, new SerializableNoOp(), targetDispatcher, this.advisedDispatcher,
  13. new EqualsInterceptor(this.advised), new HashCodeInterceptor(this.advised)};
  14. Callback[] callbacks = mainCallbacks;
  15. ......
  16. return callbacks;
  17. }

在 getCallbacks 中 Spring 考虑了很多情况,但是对于我们来说,只需要理解最常用的就可以了,比如将 advised 属性封装在 DynamicAdvisedInterceptor 并加入 callbacks 数组中,这么做的目的是什么呢?

我们知道 CGLIB 中对于方法的拦截是通过将自定义的拦截器(实现 Methodlnterceptor 接口)加入 Callback 中并在调用代理时直接激活拦截器中的 intercept 方法来实现的,那么在 getCallbacks 中正是实现了这样一个目的,DynamicAdvisedInterceptor 继承自 Methodlnterceptor,加入 Callback 中后,在再次调用代理时会直接调用 DynamicAdvisedInterceptor 中的 intercept 方法。因此,对于 CGLIB 方式实现的代理,其核心逻辑必然在 DynamicAdvisedInterceptor 中的 intercept 中。

  1. public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
  2. Object oldProxy = null;
  3. boolean setProxyContext = false;
  4. Object target = null;
  5. TargetSource targetSource = this.advised.getTargetSource();
  6. try {
  7. if (this.advised.exposeProxy) {
  8. oldProxy = AopContext.setCurrentProxy(proxy);
  9. setProxyContext = true;
  10. }
  11. target = targetSource.getTarget();
  12. Class<?> targetClass = (target != null ? target.getClass() : null);
  13. // 获取拦截器链
  14. List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
  15. Object retVal;
  16. if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
  17. // 如果拦截器链为空则直接激活原方法
  18. Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
  19. retVal = methodProxy.invoke(target, argsToUse);
  20. } else {
  21. // 进入拦截器链并通过process调用
  22. retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
  23. }
  24. retVal = processReturnType(proxy, target, method, retVal);
  25. return retVal;
  26. }
  27. }

上述的实现与 JDK 方式实现代理中的 invoke 方法大同小异,都是首先构造链,然后封装此链进行串联调用,稍有些区别就是在 JDK 中直接构造 ReflectiveMethodlnvocation,而在 CGLIB 中使用 CglibMethodInvocation。CglibMethodInvocation 继承自 ReflectiveMethodlnvocation,但是其 proceed 方法并没有重写,还是一个递归调用逻辑。