用户故事是这样的:
我们通过使用Spring Boot自动配置机制的库jar公开了一些API . API在初始化期间需要一些胶合逻辑,因此我们将它们放在某些 @Configuration 块的 @PostConstruct 块下并将它们注册为自动配置 .
用户代码基于 @SpringBootApplication ,而他们更喜欢使用 @ComponentScan 来定义bean而不是自动配置 .
所以问题来了,Spring总是首先尝试加载 @ComponentScan 定义的bean,然后是那些自动配置的bean . 因此,如果任何用户bean依赖于尚未初始化的API,那么肯定会失败 .
在混合自动配置和组件扫描的bean时,似乎无法定义bean顺序 . @Order, @AutoConfigureOrder, @AutoConfigureAfter, @AutoConfigureBefore 注释和Ordered接口仅适用于所有自动配置的bean .
当然,如果用户也为他们的bean使用自动配置,它将正常工作 . 但是从用户的角度来看, @ComponentScan 看起来更自然,更简单,特别是 @SpringBootApplication 暗示了当前java包的 @ComponentScan .
我们当前的解决方法是在很早的阶段就急切加载这些API初始化配置 . 对于Web上下文,它是 ServletContextInitializer.onStartup() ,对于其他通用上下文,它是 LoadTimeWeaverAware's @PostConstruct .
@Configuration
@ConditionalOnWebApplication
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
public class EagerInitWebContextInitializer implements ServletContextInitializer, PriorityOrdered
{
@Autowired
private ApplicationContext appContext;
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
String[] beanNames = appContext.getBeanNamesForAnnotation(EagerInitializer.class);
if (beanNames != null) {
// pre-load all eager initializers
for (String name : beanNames) {
appContext.getBean(name);
}
}
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
@Configuration
@ConditionalOnNotWebApplication
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
public class EagerInitGenericContextInitializer implements LoadTimeWeaverAware, PriorityOrdered
{
@Autowired
private ApplicationContext appContext;
@PostConstruct
protected void init() {
String[] beanNames = appContext.getBeanNamesForAnnotation(EagerInitializer.class);
if (beanNames != null) {
// pre-load all eager initializers
for (String name : beanNames) {
appContext.getBean(name);
}
}
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
@Override
public void setLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver) {
// does nothing
}
}
这实际上运作良好 . 但只是想知道有没有更好的方法来实现这一点,或者未来的Spring版本能否提供类似的系统方式?