Springboot:关于一次bean注入失败的问题记录

1. 起因

某天同事反馈了一个错误日志,如下:

org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'xxxx' available

简单的翻译一下,系统想要调用xxxx这个bean,但却发现它没有注入进去。

因为前段时间一直在做代码迁移方面的工作,遇到了很多依赖冲突,或者bean注入失败等问题,当我看到这个错以为只是单纯的没注入bean,所以在我处理的时候,第一想法就是将这个bean注入进去应该就不会报错了,然而,现实总是事与愿违。

2.处理方式

先按照一般的处理方式:

@Configuration
public class TestConfig {

    @Bean
    public TestBean testBean(){
        //由于公司保密原因,这里使用TestBean替代
        return new TestBean();
    }
}

运行中,TestBean初始化报错,原因是它依赖的其他bean没有被注入,此时我还没意识到问题在哪,于是修改了一通,将TestBean的依赖项一股脑全部注入 :

@Configuration
public class TestConfig {


   @Bean
   @ConditionalOnMissingBean(ClientInfo.class)
   public TestClientInfo testClientInfo() {
      TestClientInfo clientInfo = new TestClientInfo();
      clientInfo.setAppName("app_name");
      clientInfo.setEmail("test@email.com");
      return clientInfo;
   }

   @Bean
   @ConditionalOnMissingBean(TestCacheService.class)
   public TestCacheService testCacheService() {
      DefaultTestCacheService defaultTestCacheService = new DefaultTestCacheService();
      defaultTestCacheService.setConfigServerVersion("1.0.0");
      defaultTestCacheService.init();
      return defaultTestCacheService;
   }

   @Bean
   @ConditionalOnMissingBean(TestExtraReadServiceClient.class)
   public TestExtraReadServiceClient testExtraReadServiceClient(
         @Autowired TestClientInfo clientInfo,
         @Autowired TestCacheService testCacheService
   ) {
      TestExtraReadServiceClient testExtraReadServiceClient = new TestExtraReadServiceClient();
      testExtraReadServiceClient.setClientInfo(clientInfo);
      testExtraReadServiceClient.setTestCacheService(testCacheService);
      testExtraReadServiceClient.setTestExtraReadService(ClientConfigFactory.createConsumerBean(TestExtraReadServiceClient.class));
      return testExtraReadServiceClient;
   }

   @Bean(name = "testReadServiceClient")
   @ConditionalOnMissingBean(TestReadServiceClient.class)
   public TestReadServiceClient testReadServiceClient(
         @Autowired TestClientInfo clientInfo,
         @Autowired TestCacheService testCacheService,
         @Autowired TestExtraReadServiceClient testExtraReadServiceClient
   ) {
      TestReadServiceClient testReadServiceClient = new TestReadServiceClient();
      testReadServiceClient.setTestClientInfo(clientInfo);
      testReadServiceClient.setTestCacheService(testCacheService);
      testReadServiceClient.setTestExtraReadServiceClient(testExtraReadServiceClient);
      testReadServiceClient.setTestReadService(ClientConfigFactory.createConsumerBean(TestReadService.class));
      return testReadServiceClient;
   }
}

我再次启动时,还是报错,缘由是这里面的某些bean还依赖了其他的,我需要将其他的依赖bean也注入进去,其他依赖项还可能依赖别的,所谓子子孙孙无穷尽也,我感觉到这个方法无法解决我的根本问题,只能从别的地方下手。

上述问题是由代码迁移导致,那么在原来地方肯定是没有问题,我应该去被迁移的项目中寻找注入的方式。

另一个项目中,我找到了我缺少的依赖,这个依赖里有我所需要的所有配置类。

我兴冲冲地将这个依赖加进去,去除掉我刚刚自己加的配置类,准备启动!

启动中问题又出现,这次错误是注入TestTagServiceClient时发现它依赖的TestTagWriteServiceClient这个bean没有注入!

具体代码中,TestTagWriteServiceClient确实是作为参数传进来的,

@Bean
    @ConditionalOnMissingBean({TestTagServiceClient.class})
    public TestTagServiceClient testTagServiceClient(TagRecordServiceClient tagRecordServiceClient,
                                                   TestTagWriteServiceClient testTagWriteServiceClient,
                                                   TestDataServiceClient testDataServiceClient,
                                                   TestExtraReadServiceClient testExtraReadServiceClient){
        TestTagServiceClient bean = new TestTagServiceClient();
        bean.setTagRecordServiceClient(tagRecordServiceClient);
        bean.setTestDataServiceClient(testDataServiceClient);
        bean.setTestExtraReadServiceClient(testExtraReadServiceClient);
        bean.setTestTagWriteServiceClient(testTagWriteServiceClient);
        return bean ;
    }

在这个配置类中有@AutoConfigureAfter({TestUserDataAutoConfiguration.class,TestUserTagAutoConfiguration.class})这样的注解,意思是在TestUserDataAutoConfiguration和TestUserTagAutoConfiguration之后才加载,这个注解是为了保证配置类的加载顺序,不然某些依赖的配置未加载就会出现我现在遇到的问题。

那么接下来就要去寻找TestTagWriteServiceClient是在什么时候注入的。

答案在TestUserTagAutoConfiguration这个配置类中。

@Configuration
@AutoConfigureAfter(TestAutoConfiguration.class)
@ConditionalOnProperty(value = Constants.ENABLE_USER_TAG, havingValue = "true")
public class TestUserTagAutoConfiguration {
    @Autowired
    private TestProperties properties;

    /**
     * Assemble {@link TestTagWriteService} service consumer
     */
    @Bean
    @ConditionalOnMissingBean({TestTagWriteService.class})
    public TestTagWriteService testTagWriteService() {
        return (TestTagWriteService) ServiceFactory.consumer()
                .service(TestTagWriteService.class)
                .group(properties.getGroup())
                .version(properties.getVersion())
                .subscribe();
    }

    /**
     * Assemble {@link TestTagWriteServiceClient} bean
     */
    @Bean
    @ConditionalOnMissingBean
    public TestTagWriteServiceClient testTagWriteServiceClient(ClientInfo clientInfo,
                                                             TestTagWriteService testTagWriteService) {
        TestTagWriteServiceClient bean = new TestTagWriteServiceClient();
        bean.setClientInfo(clientInfo);
        bean.setTestTagWriteService(testTagWriteService);
        return bean;
    }

}

这里我需要看看注入TestTagServiceClient和TestTagWriteServiceClient的先后顺序。我依次打上断点,再次启动! 

断点先进入了TestTagServiceClient的注入,我意识到他们可能注入顺序有误,但是我无法修改依赖中配置项,那么我就需要自己手动注入TestTagWriteServiceClient,并保证在TestTagServiceClient之前,我修改了我的配置类:

@Configuration
public class TestConfig {

    /**
     * Assemble {@link TestTagWriteService} service consumer
     */
    @Bean
    public TestTagWriteService testTagWriteService() {
        return (TestTagWriteService) ServiceFactory.consumer()
                .service(TestTagWriteService.class)
                .group("group")
                .version("version")
                .subscribe();
    }

    /**
     * Assemble {@link TestTagWriteServiceClient} bean
     */
    @Bean
    public TestTagWriteServiceClient testTagWriteServiceClient(TestTagWriteService testTagWriteService) {
        ClientInfo info = new ClientInfo();
        info.setAppName("app_name");
        info.setEmail("test@email.com");
        TestTagWriteServiceClient bean = new TestTagWriteServiceClient();
        bean.setClientInfo(info);
        bean.setTestTagWriteService(testTagWriteService);
        return bean;
    }
}

怀着满怀期待的心情再次启动,这次启动成功了,调用uicReadServiceClient时没有出现错误,问题解决。

3.总结

在日常开发中,会遇到各种各样的问题,这次遇到的问题由于我还有别的事情,足足耽搁了三天才解决,此间我尝试过很多方法,以及找同事帮忙看,最后都无功而返。

类似这样bean注入的问题,需要从源头开始看,静下心来去研究源码,观察它是怎么初始化,怎么注入进系统中,如果正规手段无法解决,需要使用一些特殊的手段,按照自己的所学去理解它,这样才能在以后的开发中披坚执锐。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值