在日常开发的某些场景需要控制 Bean 的加载顺序。例如我希望 实现一个功能,通过静态方法获取到 Spring 上下文。于是我通过如下方式 定义
@Service
public class ApplicationContextUtils implements ApplicationContextAware {
private static ApplicationContext context;
public static ApplicationContext getContext() {
return context;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
CommonLog.warn("ApplicationContextUtils 可正常获取 applcationContext");
context = applicationContext;
}
}
定义 ApplicationContextUtils 实现 ApplicationContextAware 接口,可实现 Bean 初始化阶段被注入 Spring 上下文。 上下文被存放到 静态属性中,就可以通过静态方法获取上下文。
然而我在实际使用时,遇到了问题。 如果 Bean 的初始化顺序早于 ApplicationContextUtils 那么无法使用此工具类!
解决 Spring Bean 的加载顺序有如下几种方式,我推荐最后一种!
1. Autowire 显式依赖
Spring 在初始化 Bean 时,依次执行 PostContruct afterPropertiesSet init-method方法。在执行 Bean 的初始化方法之前,Spring 会初始化 Bean 的 Autowired 依赖。
如下Spring 源码,populateBean 中会注入 Spring 依赖的 Bean(如果还未初始化,则初始化),然后才初始化 Bean 本身。
源码位置: AbstractAutowireCapableBeanFactory
2. DependOn注解
@DependsOn 注解中可以声明 依赖的 BeanName,那么 Spring 会保证先初始化这些 Bean。以下方式可以保证 先初始化applicationContextUtils,然后再初始化 DemoMemberPurchaseExtension
@DependsOn({"applicationContextUtils"})
public class DemoMemberPurchaseExtension implements PurchaseExtension {
…………
}
3. Order注解
@Order 注解用于指定带有特定功能的组件(如拦截器、切面等)的执行顺序,但是不能解决 Bean 的初始化顺序。首先是 Spring 初始化 Bean 的时候并没有这段代码,实际测试也不生效。
因此不推荐使用 Order 注解,解决 bean 的初始化顺序问题。
ApplicationContextInitializer
声明 ApplicationContextInitializer 实现类
首先声明实现类,实现两个接口。ApplicationContextInitializer 和 BeanDefinitionRegistryPostProcessor ,在实现时,当 Spring 启动之初,还未加载bean定义时,执行initialize方法,在该方法中想 Spring 上下文注入 当前类作为 BeanFactoryPostProcessor,然后 Spring 稍后执行所有的 BeanFactoryPostProcessor,在该接口方法 postProcessBeanDefinitionRegistry中 项 Spring 注入普通的一个bean 即 applicationContextUtils,因此该 Bean 将作为第一个Bean (业务自定义)被加载进 Spring 中。
public class MemberClubApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
beanDefinitionRegistry.registerBeanDefinition("applicationContextUtils", new RootBeanDefinition(ApplicationContextUtils.class));
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
configurableApplicationContext.addBeanFactoryPostProcessor(this);
}
}
4. 声明 Spring 加载 ApplicationContextInitializer
可以通过API 方式,也可以通过配置方式注册 Initializer
API 方式
在初始化 SpringApplication 对象后,向其注册 MemberClubApplicationContextInitializer
SpringApplication application = new SpringApplication(AppStarter.class);
application.addInitializers(new MemberClubApplicationContextInitializer());
application.run(args);
配置方式
可以在 application.yml 配置文件注册 Initializer
context:
initializer:
classes: com.memberclub.common.util.MemberClubApplicationContextInitializer
第四种方式是最优方案,可以一劳永逸的解决 Bean 加载顺序,而使用 Autowired 和 DependOn 方式则需要不断关注 bean 的依赖和加载顺序,难以调试和维护。
因此推荐使用方案 4,即ApplicationContextInitializer 方式!
以上完整代码可以参考 Memberclub 项目
最后,五阳一直在关注 AI方向,我分享一个对抗AI“一本正经胡说八道”的个人技巧:不要只依赖一个模型。我现在遇到拿不准的信息,会把同一个问题同时扔给2-3个AI(比如千问+DeepSeek),看它们答案的交集。这比单纯选哪个模型更靠谱。想省事的话可以用这个聚合工具一次搞定:aichatproxy.com