ShiroAutoConfiguration加入DefaultAdvisorAutoProxyCreator造成的二次代理

1、描述

某次Debug的时候,发现项目中某个Bean只要使用了AOP代理,就会出现两次代理的情况。

2、测试

  • 首先创建一个测试用的service,并加上使用AOP的注解(Transactional、Async、caching相关的注解都可以)
    在这里插入图片描述

  • 在这里插入图片描述

    • 在上图测试类中:
      • class2代表最终的代理类:TestService$$EnhancerBySpringCGLIB$$5e29e70b
      • class1代表class2的目标类,因为是多次代理,所以目标类还是一个代理类:TestService$$EnhancerBySpringCGLIB$$8cdcc677
      • class0才是最初代理的类,也就是TestService类。
  • 在这里插入图片描述

    • 其实debug testService bean 也能看出,有两个cglib的代理类。

    3、原因

  • 经过排查,发现在Shiro中配置了一个ProxyCreator[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传在这里插入图片描述

  • spirng aop config中,肯定有自己的ProxyCreator,但是项目中自己配置了Creator,并不会让spring自己的Creator失效,而是会出现两个ProxyCreator,所以一个Bean才会创建两次。

  • Spring自己的代理创建器经过各种委托,最终代码的实现在AopConfigUtils

    @Nullable
    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) {
                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;
    }
    

在这里插入图片描述

  • DefaultAdvisorAutoProxyCreator是配置Shiro时添加的代理创建器
  • AspectJAwareAdvisorAutoProxyCreator时spring自动添加的内部创建器

4、修复

原因找到了,那我们直接干掉Shiro中配置的代理创建器不就可以了。

删除Shiro的DefaultAdvisorAutoProxyCreator配置

此时启动项目,却报错了

在这里插入图片描述

goodsService无法作为GoodsService注入,因为goodsService是一个jdk动态代理类实现了:空?

给的建议是让我们强制使用cglib代理。

但是根据建议无论如何设置,都无效。依旧会报错。

新问题的原因

这时候另一个坑出现了,因为项目使用的是Shrio starter,在ShiroAnnotationProcessorAutoConfiguration自动配置类里,又发现了熟悉的创建器,DefaultAdvisorAutoProxyCreator

在这里插入图片描述

在这里插入图片描述

可以看到,super类直接new了DefaultAdvisorAutoProxyCreator。而且我们已经删除了自己配置的代理创建器,所以@ConditionalOnMissingBean注解就会生效,将DefaultAdvisorAutoProxyCreator放到容器中。

  1. 既然跟之前情况一样,有两个代理创建器,那为什么项目没有启来?
    • 第一次代理,使用的cglib(TestService并没有实现接口),cglib是通过代理类去继承目标类,然后重写其中目标类的方法,但是也会实现某些接口,比如Factory。
    • 第二次代理,代理的是第一次代理的代理类,发现第一次代理的代理类有接口,然后代理创建器就会用JDK的动态代理来实现,JDK动态代理是基于接口的方式,换句话来说就是代理类和目标类都实现同一个接口,那么代理类和目标类的方法名就一样了。但是此时的接口是cglib创建出来的,并没有我们想要的方法。所以此次的代理类会实现Factory,与目标类没有啥关系。但是这并不是注入失败的原因,失败的原因是JDK动态代理的类,相当于和目标类是 兄弟类,也就是代理类和目标类实现了共同的接口,所以将代理类注入给目标类肯定无法注入。
    • 相当于A类和B类共同实现了C类,此时我要A a = new B(),当然是不行的

5、最终解决

此时我们再排除Shiro的autoConfiguration就可以了

启动类的@SpringBootApplication注解中添加 exclude = {ShiroAnnotationProcessorAutoConfiguration.class}

6、备注

感觉对于AOP和Spring源码类理解的还是不到位。如有问题,请随时联系!

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值