关于springboot:记一次使用spring-javaconfig踩到的坑

前言

为了简化开发,我部门常常会封装一些通用的类库给业务研发应用,因为业务方的根包门路很常常和咱们部门我的项目的根包是不一样的,因而咱们会让业务方在应用咱们封装的包时,扫描一下咱们的根包,形如下

@ComponentScan(basePackages = {"com.aaa","com.bbb"})

不过这样就导致了业务方想应用咱们的类库,就必须晓得咱们的根包。这其实是一种间接的耦合。前面咱们就全面应用springboot的主动拆卸,让业务方无需晓得咱们的根包,也能够应用咱们的类库。然而在咱们封装的过程中,也遇到一些坑。本文就来复盘一次咱们应用spring javaconfig踩到的坑。本文次要是demo示例演示

demo示例

假如咱们封装了一个类库DemoService。示例如下

public class DemoService {

    private DemoProperties demoProperties;

    private String version;


    public DemoService(DemoProperties demoProperties) {
        this.demoProperties = demoProperties;
    }

    public String print(){
        return "version:" + version + ">>>>>>>>>>" + demoProperties;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }
}

DemoProperties 类如下

@ConfigurationProperties(prefix = "demo.hello")
public class DemoProperties {

    private String name;


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "DemoProperties{" +
                "name='" + name + '\'' +
                '}';
    }
}

有个针对DemoService的扩大后置处理器

public class DemoBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition helloService = beanFactory.getBeanDefinition("demoService");
        helloService.getPropertyValues().add("version","V1");

        System.out.println(">>>>>>>>>>>> demoService demoBeanFactoryPostProcessor");

    }
}

javaconfig配置如下

@Configuration
@EnableConfigurationProperties(DemoProperties.class)
public class DemoConfig implements InitializingBean {


    @Autowired
    private DemoProperties demoProperties;

    
    @Bean
    @ConditionalOnMissingBean
    public DemoService demoService(){
        return new DemoService(demoProperties);
    }


    @PostConstruct
    public void init(){
        System.out.println("模仿业务初始化。。。");
    }


    @Bean
    @ConditionalOnMissingBean
    public DemoBeanFactoryPostProcessor demoBeanFactoryPostProcessor(){
        return new DemoBeanFactoryPostProcessor();
    }


    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("xxxxxxxx:" + demoProperties);
    }
}

配置的application.properties如下

server.port=8086

demo.hello.name=zhangsan

启动类如下

@SpringBootApplication
public class DemoLoaderApplication implements ApplicationRunner {

    @Autowired
    private DemoService demoService;

    public static void main(String[] args) {
        SpringApplication.run(DemoLoaderApplication.class, args);
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println(demoService.print());
    }
}

以上就是残缺的示例demo,看完示例demo,能够答复如下问题

1、javaconfig中的

   @PostConstruct
    public void init(){
        System.out.println("模仿业务初始化。。。");
    }

是否会执行?

2、javaconfig中的

     @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("xxxxxxxx:" + demoProperties);
    }

是否会执行,如果会执行,demoProperties是否有值?

3、启动类中的

     @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println(demoService.print());
    }

打印的值是多少?

demoService的print办法如下

 public String print(){
        return "version:" + version + ">>>>>>>>>>" + demoProperties;
    }

咱们能够看下输入的后果

由输入的信息,咱们能够发现@PostConstruct没失效、afterPropertiesSet办法失效,由afterPropertiesSet打印的内容,咱们能够得出DemoProperties 依赖注入生效,即 @Autowired生效,由print()办法咱们能够得出DemoBeanFactoryPostProcessor失效了

排坑

答案就在我截图圈红的中央

@PostConstruct和@Autowired生效的起因是spring在进行ioc时,会先调用bean工厂的后置处理器进行beanFactory加强,spring会依据bean工厂的beanName去取beanFactory后置增强器,如果beanFactory后置增强器的bean此时还不存在,spring就会走doCreateBean进行创立,在创立的时候,会判断是否须要应用工厂办法进行实例化,咱们应用@Bean时,它采纳就是工厂办法。在通过工厂实例办法创立beanFactory后置增强器时,他会调用

org.springframework.beans.factory.support.SimpleInstantiationStrategy#instantiate(org.springframework.beans.factory.support.RootBeanDefinition, java.lang.String, org.springframework.beans.factory.BeanFactory, java.lang.Object, java.lang.reflect.Method, java.lang.Object...)

此时factoryBean就必须肯定得有值,否则会报错。而这个factoryBean就是示例中的DemoConfig 。这就意味着DemoConfig在DemoBeanFactoryPostProcessor在实例化前,就得先创立好。而此时

registerBeanPostProcessors还没执行到,意味着各种spring的bean后置处理器还没筹备好。比方解析@Autowired注解的AutowiredAnnotationBeanPostProcessor以及解析@PostConstruct注解的CommonAnnotationBeanPostProcessor都还没筹备好。因而@Autowired和@PostConstruct天然就不会失效

解决办法

办法一:DemoBeanFactoryPostProcessor的创立办法改为静态方法

    @Bean
    @ConditionalOnMissingBean
    public static DemoBeanFactoryPostProcessor demoBeanFactoryPostProcessor(){
        return new DemoBeanFactoryPostProcessor();
    }

因为是静态方法,他依赖就是类自身而非类实例对象,DemoConfig此时就会让失常的spring bean的生命周期来

办法二:DemoBeanFactoryPostProcessor独自应用一个配置类

示例

@Configuration
public class DemoBeanFactoryPostProcessorConfig {

    @Bean
    @ConditionalOnMissingBean
    public DemoBeanFactoryPostProcessor demoBeanFactoryPostProcessor(){
        return new DemoBeanFactoryPostProcessor();
    }

}

办法三:应用@Import注入

@Configuration
@EnableConfigurationProperties(DemoProperties.class)
@Import(DemoBeanFactoryPostProcessor.class)
public class DemoConfig implements InitializingBean {

    @Autowired
    private DemoProperties demoProperties;


    @Bean
    @ConditionalOnMissingBean
    public DemoService demoService(){
        return new DemoService(demoProperties);
    }

办法四:应用@Component + @ComponentScan


@Component
public class DemoBeanFactoryPostProcessor implements BeanFactoryPostProcessor {


    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition helloService = beanFactory.getBeanDefinition("demoService");
        helloService.getPropertyValues().add("version","V1");

        System.out.println(">>>>>>>>>>>> demoService demoBeanFactoryPostProcessor");

    }
}
@Configuration
@EnableConfigurationProperties(DemoProperties.class)
@ComponentScan(basePackageClasses = DemoBeanFactoryPostProcessor.class)
public class DemoConfig implements InitializingBean {

    @Autowired
    private DemoProperties demoProperties;


    @Bean
    @ConditionalOnMissingBean
    public DemoService demoService(){
        return new DemoService(demoProperties);
    }
}

总结

其实本文的解决思路就是对spring bean的创立过程要有肯定理解。其次咱们在利用spring的扩大点时候,咱们多应用spring自带的内置扩大对象,比方咱们在bean初始化时,要做一些扩大时,尽量应用InitializingBean而非应用@PostConstruct。这样能够防止当呈现下面示例的坑时,导致代码不执行而呈现bug,而这种bug往往暗藏比拟深。当然如果有自信不会呈现这种问题,用@PostConstruct也是能够,毕竟用注解的形式绝对也简洁一些

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理