ShiroAutoConfiguration加入DefaultAdvisorAutoProxyCreator造成的二次代理

本文探讨了在项目中遇到的AOP代理异常,即一个Bean被创建两次,原因是Shiro配置的ProxyCreator与自定义冲突。通过排查和修复,作者发现删除Shiro的DefaultAdvisorAutoProxyCreator并排除相关配置后,问题得以解决。关键在于理解Spring AOP代理的原理和不同创建器的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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源码类理解的还是不到位。如有问题,请随时联系!

### 关于 Apache Shiro Framework Version 和 Error Code 目前并未发现有关 `Apache Shiro` 的具体版本号为 `710` 或错误码 `shiro710` 的官方记录或文档说明。通常情况下,Shiro 的版本命名遵循语义化版本控制 (Semantic Versioning),例如 `1.x.x` 系列[^1]。 #### 如何确认当前使用的 Apache Shiro 版本? 如果需要查询正在使用的 Shiro 版本,可以参考以下方法: 1. **通过 Maven 依赖管理工具检查** 如果项目基于 Maven 构建,则可以在项目的 `pom.xml` 文件中查找 `<dependency>` 中关于 Shiro 的定义部分: ```xml <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.9.1</version> <!-- 当前最新稳定版 --> </dependency> ``` 2. **通过程序动态获取版本信息** 可以利用 Java 编程方式读取 Shiro 库中的元数据来确定其版本号: ```java import org.apache.shiro.version.Version; public class ShiroVersionChecker { public static void main(String[] args) { String shiroVersion = Version.getVersion(); System.out.println("Current Apache Shiro version is: " + shiroVersion); } } ``` 3. **手动查阅配置文件** 若项目集成了特定的环境参数设置(如上下文中指定),则可能涉及自定义加载逻辑。例如,在 Web 应用场景下可通过 XML 配置调整默认行为[^2]: ```xml <context-param> <param-name>shiroEnvironmentClass</param-name> <param-value>org.apache.shiro.web.env.IniWebEnvironment</param-value> </context-param> ``` 对于提到的所谓 “error code shiro710”,这并非标准术语或者已知概念。建议重新审视实际遇到的问题描述并提供更详尽背景以便进一步诊断。 --- ### 示例代码片段展示如何集成 Spring Boot 和 Shiro 以下是基于 Spring Boot 实现的一个简单示例,用于演示两者之间的基本协作关系[^5]: ```java import org.apache.shiro.spring.boot.autoconfigure.ShiroAutoConfiguration; import org.springframework.context.annotation.Configuration; @Configuration public class ShiroConfig extends ShiroAutoConfiguration { @Bean public Realm myRealm() { return new MyCustomRealm(); // 自定义领域类 } } ``` 同时还需要确保引入必要的 Starter POM 插件支持: ```xml <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-starter</artifactId> <version>1.8.0</version> </dependency> ``` ---
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值