问题
/*** 系统属性服务**/@Servicepublic class SystemConfigService {// 访问 db 的 mapperprivate final SystemConfigMapper systemConfigMapper;// 存放一些系统配置的缓存 mapprivate static Map<String, String>> SYS_CONF_CACHE = new HashMap<>()// 使用构造方法完成依赖注入public SystemConfigServiceImpl(SystemConfigMapper systemConfigMapper) {this.systemConfigMapper = systemConfigMapper;}// Bean 的初始化方法,捞取数据库中的数据,放入缓存的 map 中@PostConstructpublic void init() {// systemConfigMapper 访问 DB,捞取数据放入缓存的 map 中// SYS_CONF_CACHE.put(key, value);// ...}// 对外提供获得系统配置的 static 工具方法public static String getSystemConfig(String key) {return SYS_CONF_CACHE.get(key);}// 省略了从 DB 更新缓存的代码// ...}
看过了上面的代码后,很容易就理解了为什么会标题中的需求了。
SpringBoot 官方文档推荐做法
Constructor-based or setter-based DI? Since you can mix constructor-based and setter-based DI, it is a good rule of thumb to use constructors for mandatory dependencies and setter methods or configuration methods for optional dependencies. Note that use of the @Autowired annotation on a setter method can be used to make the property be a required dependency; however, constructor injection with programmatic validation of arguments is preferable.
尝试解决问题的一些方法
@Order 注解或者实现 org.springframework.core.Ordered
@AutoConfigureOrder/@AutoConfigureAfter/@AutoConfigureBefore 注解
@DependsOn 注解
@Service@DependsOn({"systemConfigService"})public class BizService {public BizService() {String xxValue = SystemConfigService.getSystemConfig("xxKey");// 可行}}
这样测试下来是可以是可以的,就是操作起来也太麻烦了,需要让每个每个依赖 SystemConfigService的 Bean 都改代码加上注解,那有没有一种默认就让 SystemConfigService 提前的方法?
Spring 中 Bean 创建的相关知识

ConfigurationClassPostProcessor 的介绍
BeanDefinitionRegistryPostProcessor 相关接口的介绍
在 BeanFactory 初始化之后调用,来定制和修改 BeanFactory 的内容
所有的 Bean 定义(BeanDefinition)已经保存加载到 beanFactory,但是 Bean 的实例还未创建
方法的入参是 ConfigurrableListableBeanFactory,意思是你可以调整 ConfigurrableListableBeanFactory 的配置
是 BeanFactoryPostProcessor 的子接口
在所有 Bean 定义(BeanDefinition)信息将要被加载,Bean 实例还未创建的时候加载
优先于 BeanFactoryPostProcessor 执行,利用 BeanDefinitionRegistryPostProcessor 可以给 Spring 容器中自定义添加 Bean
方法入参是 BeanDefinitionRegistry,意思是你可以调整 BeanDefinitionRegistry 的配置
在 Bean 实例化之后执行的
执行顺序在 BeanFactoryPostProcessor 之后
方法入参是 Object bean,意思是你可以调整 bean 的配置
最终答案
# 注册 ApplicationContextInitializerorg.springframework.context.ApplicationContextInitializer=com.antbank.demo.bootstrap.MyApplicationContextInitializer
注册 ApplicationContextInitializer 的目的其实是为了接下来注册 BeanDefinitionRegistryPostProcessor 到 Spring 中,我没有找到直接使用 spring.factories 来注册 BeanDefinitionRegistryPostProcessor 的方式,猜测是不支持的:
public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {@Overridepublic void initialize(ConfigurableApplicationContext applicationContext) {// 注意,如果你同时还使用了 spring cloud,这里需要做个判断,要不要在 spring cloud applicationContext 中做这个事// 通常 spring cloud 中的 bean 都和业务没关系,是需要跳过的applicationContext.addBeanFactoryPostProcessor(new MyBeanDefinitionRegistryPostProcessor());}}
除了使用 spring 提供的 SPI 来注册 ApplicationContextInitializer,你也可以用 SpringApplication.addInitializers 的方式直接在 main 方法中直接注册一个 ApplicationContextInitializer 结果都是可以的:
@SpringBootApplicationpublic class SpringBootDemoApplication {public static void main(String[] args) {SpringApplication application = new SpringApplication(SpringBootDemoApplication.class);// 通过 SpringApplication 注册 ApplicationContextInitializerapplication.addInitializers(new MyApplicationContextInitializer());application.run(args);}}
当然了,通过 Spring 的事件机制也可以做到注册 BeanDefinitionRegistryPostProcessor,选择实现合适的 ApplicationListener 事件,可以通过 ApplicationContextEvent 获得 ApplicationContext,即可注册 BeanDefinitionRegistryPostProcessor,这里就不多展开了。
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {// 手动注册一个 BeanDefinitionregistry.registerBeanDefinition("systemConfigService", new RootBeanDefinition(SystemConfigService.class));}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}}
当然你也可以使用一个类同时实现 ApplicationContextInitializer 和BeanDefinitionRegistryPostProcessor

最后说一句(别白嫖,求关注)
我的每一篇文章都是精心输出,如果这篇文章对你有所帮助,或者有所启发的话,帮忙点赞、在看、转发、收藏,你的支持就是我坚持下去的最大动力!
点击图片获取专栏





