乐趣区

关于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 也是能够,毕竟用注解的形式绝对也简洁一些

退出移动版