有时,我们会碰到一个 bean 在使用时,却没有被 spring 容器加载。
例如下面的 PropertiesHelper 类,我们在另一个类 FooService 中使用了静态方法 PropertiesHelper.getProperty()
来获取配置值,却报了空指针。
@Component
public class PropertiesHelper implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext appContext) throws BeansException {
applicationContext = appContext;
}
/**
* 静态方法:获取某个属性值
*/
public static String getProperty(String key) {
return applicationContext.getEnvironment().getProperty(key);
}
}
碰到这种问题,通常的做法就是在 FooService 加载前,让 PropertiesHelper 优先加载,比如使用 @DependOn 或者 在 FooService 中 @Autowired 自动注入 PropertiesHelper。
有一种一劳永逸的方法:
在 SpringBoot 里面,如果碰到这种情况,我们有一种屡试不爽的解决方案,那就是直接在启动类 Application 中注入 PropertiesHelper 的依赖。形如:
@SpringBootApplication
public class Application {
// 让这个类优先加载
@Autowired
private PropertiesHelper propertiesHelper;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
原理:启动类中注入的 bean 为什么会优先加载?
SpringBoot 工程在启动时,会刷新 spring 容器,然后从启动类 Application 开始,递归扫描出所有的 BeanDefinition 注册到容器中。
SpringBoot 是以启动类 Application 为起点,将所有的 bean definition 全部扫描出来。这是一个递归操作,直到所有的 configration class 处理完为止。
所以,如果我们在启动类 Application 中注入一个 bean 的话,这个 bean 会被优先扫描出来,最后会被优先加载。
补充:
普通的 bean 被扫描出来后,最后也会被包装成一个 org.springframework.context.annotation.ConfigurationClass
。
也就是说, @Component、@Service 标记的 bean 也会被包装成一个 ConfigurationClass。
最后还会扫描这些类里面是否有定义过 bean ,比如: 在 @Service 类中,通过 @Bean 定义的 bean ,也会被合法的扫描出来,最终作为一个 ConfigurationClass 进行处理。