自定义Spring boot starter 属性注入不了?
分类:Java 浏览:66 时间:2020-03-08 19:03

按照官方文档来一步一步编写自定义starter,没有想到竟然失败了,properties注入失败,总是获取到null。反反复复检查都没有问题,WTF一个简单的问题搞的甚是蛋疼。

最近项目升级,准备将现有的项目统一使用spring boot开发,并拆分一些通用的类库。


首先改造的是Hessian自动装配的组件:使用Spring进行包扫描,再通过BeanDefinitionRegistryPostProcessor进行注册,之前版本是通过XML文件配置的。服务端只需要注入basePackages即可将接口实现类自动注册为Bean(Controller),客户端则需要注入basePackages和服务端的Url即可注册为Bean(Service)。


当我满怀激情开始改造的时候,问题来了:自定义Spring boot starter中注入的在配置文件中声明的base-packages取不到值,idea中在application.properties中输入是有提示的。翻阅官方文档,参考官方示例,最后可以确定我的配置和写法是没有问题。搞不定,只能先暂缓一下,老规矩,先处理其他的改造,等我睡一觉起来。


第二天想起来,这个自定义的starter还是有一点不一样的,因为它继承了BeanDefinitionRegistryPostProcessor,而BeanDefinitionRegistryPostProcessor严格意义来说并不是一个Bean,也就是说极有可能是在postProcessBeanDefinitionRegistry中开始扫描注册bean的时候其他的bean并没有初始化,所以properties注入不了。


查看Spring源码在AbstractApplicationContext类中的refresh方法:

@Override
public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // Prepare this context for refreshing.
      prepareRefresh();

      // Tell the subclass to refresh the internal bean factory.
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // Prepare the bean factory for use in this context.
      prepareBeanFactory(beanFactory);

      try {
         // Allows post-processing of the bean factory in context subclasses.
         postProcessBeanFactory(beanFactory);

         // Invoke factory processors registered as beans in the context.
         //  代码跟踪进去,会调用postProcessBeanDefinitionRegistry
         invokeBeanFactoryPostProcessors(beanFactory);

         // Register bean processors that intercept bean creation.
         registerBeanPostProcessors(beanFactory);

         // Initialize message source for this context.
         initMessageSource();

         // Initialize event multicaster for this context.
         initApplicationEventMulticaster();

         // Initialize other special beans in specific context subclasses.
         onRefresh();

         // Check for listener beans and register them.
         registerListeners();

         // Instantiate all remaining (non-lazy-init) singletons.
         //  问题找到 Bean的初始化(非懒加载)在此处
         finishBeanFactoryInitialization(beanFactory);

         // Last step: publish corresponding event.
         finishRefresh();
      }

      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }

         // Destroy already created singletons to avoid dangling resources.
         destroyBeans();

         // Reset 'active' flag.
         cancelRefresh(ex);

         // Propagate exception to caller.
         throw ex;
      }

      finally {
         // Reset common introspection caches in Spring's core, since we
         // might not ever need metadata for singleton beans anymore...
         resetCommonCaches();
      }
   }
}

可以看到invokeBeanFactoryPostProcessors方法是先于finishBeanFactoryInitialization方法执行的。


使用自定义starter的方式是行不通的了,只能手动注册BeanDefinitionRegistryPostProcessor,再使用@Bean进行注册。可以通过注入Environment获取配置文件中的值,此处如果使用@Value也是注入不了的,原因同上。

@Configuration
public class HessianConfig {

    @Bean
    public HessianServerScannerConfigurer hessianServerScannerConfigurer(Environment env) throws IOException {
        HessianServerScannerConfigurer hessianServerScannerConfigurer = new HessianServerScannerConfigurer();
        hessianServerScannerConfigurer.setBasePackages(Lists.newArrayList(env.getProperty("hessian.server.base-packages")));
        return hessianServerScannerConfigurer;
    }
}


最后附上,Hessian自动装配组件的源码,供参考:https://github.com/benjamin-coding/hessian-auto.git


Hessian 蛋疼集锦