如何在春季开始时选择性加载豆子



我的问题是如何在web应用程序启动时实现选择性加载springbean。

背景我们的应用程序是基于J2EE和Spring的。我们在不同的托管服务器上运行相同的web应用程序。在其中一些托管服务器上,我们只运行web服务,但在其他服务器上,还需要运行报告、调度程序等服务。所有这些服务都在spring配置xml文件中配置为spring-bean。因此,我们希望在使用web服务启动服务器时禁用一些未使用的bean。

问题,我试图覆盖org.springframework.web.context.ContextLoaderListener中的customizeContext方法,以从上下文中删除那些未使用的bean。(我知道移除加载的bean而不是从一开始就停止加载它们不是一个好主意。那是因为我也不知道如何实现它。)然而,我得到了java.lang.IllegalStateException: BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext

经过一番调查,我发现BeanFactory不能在这里使用,但我有点卡住了,不知道如何实现这个功能。有人能帮我摆脱困境吗?无论是在启动时停止将bean加载到Spring中,还是在Spring刚启动时将bean从中移除,都对我有效

以下是我重写方法customizeContext的代码。

@Override
protected void customizeContext(ServletContext servletContext, ConfigurableWebApplicationContext applicationContext) {
super.customizeContext(servletContext, applicationContext);
ConfigurableListableBeanFactory configurableListableBeanFactory = applicationContext.getBeanFactory();
BeanDefinitionRegistry beanDefinitionRegistry = (BeanDefinitionRegistry) configurableListableBeanFactory;
beanDefinitionRegistry.removeBeanDefinition("testBean");
}

您不应该在加载所有bean后尝试配置BeanFactory,而应该拥有bean的,并且只加载与实际运行的服务相关的

传统的方法是让中间XML文件包含导入,而其他文件包含我所称的beans的上述,并在主XML文件中导入正确的文件。摘自Spring参考手册:依赖于系统环境变量和包含${placeholder}令牌的XML语句的组合,这些令牌根据环境变量的值解析为正确的配置文件路径

但现在选择的工具应该是配置文件。您将bean放在与您的服务相对应的不同配置文件中

@Bean
@Profile("production")
public DataSource productionDataSource() throws Exception {

或XML

<beans profile="production">
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
</beans>

你只需要启用相关的配置文件,可以通过程序:

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("dev");

甚至作为JVM属性:

-Dspring.profiles.active="profile1,profile2"

参考文献:Spring参考手册中的"环境摘要"一章。

感谢Serge和其他人的建议。我们目前使用的是3.0.5,因此无法在我们的项目中真正使用3.1配置文件功能。

我们找到了一种方法,在方法customizeContext()中的ConfigurableWebApplicationContext中添加一个BeanFactoryPostProcessor。它似乎解决了我们的问题。

代码更改为:

protected void customizeContext(ServletContext servletContext, ConfigurableWebApplicationContext applicationContext) {
super.customizeContext(servletContext, applicationContext);
applicationContext.addBeanFactoryPostProcessor(new BootProcessor());
}
class BootProcessor implements BeanFactoryPostProcessor{
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory clbf) throws BeansException {
BeanDefinitionRegistry beanDefinitionRegistry = (BeanDefinitionRegistry) clbf;
beanDefinitionRegistry.removeBeanDefinition("testerBean");
}           
}

使用Spring Boot(使用2.1.x进行了测试,但应该适用于所有版本),很简单,将此类放在类路径扫描范围内:

@Component
public class BeanKiller implements BeanFactoryPostProcessor
{    
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
{
try {
DefaultListableBeanFactory factory = (DefaultListableBeanFactory) beanFactory;
factory.removeBeanDefinition("problematicBeanNameHere");
} catch (NoSuchBeanDefinitionException e) {
throw new IllegalStateException("Couldn't remove the problematicBeanNameHere, maybe it changed name?.");
}
}
}

如果你不使用类路径扫描,你可以在主方法中注入它:

public static void main(String[] args)
{
SpringApplicationBuilder builder = new SpringApplicationBuilder(YourApplication.class);
builder.initializers(context -> context.addBeanFactoryPostProcessor(new ZuulRefreshRoutesListenerKiller()));
builder.run(args);
}

最新更新