如何在启动弹簧时选择性加载bean

时间:2015-04-30 05:30:14

标签: java spring spring-mvc

我的问题是如何在Web应用程序启动时有选择地加载spring bean。

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

问题, 我试图覆盖customizeContextorg.springframework.web.context.ContextLoaderListener的方法,从上下文中删除那些未使用的bean。 (我知道这不是一个好主意,删除加载的bean而不是阻止它们在第一个位置加载。那是因为我无法弄清楚如何实现它)但是,我得到了java.lang.IllegalStateException: BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext

经过一番调查后,我得知BeanFactory不能在这里使用,但我有点卡住,不知道如何实现这个功能。有人可以帮我解决这个问题吗?要么在开始时停止将bean加载到Spring中,要么在Spring启动时从bean中移除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");
}

3 个答案:

答案 0 :(得分:5)

不是在加载所有bean之后尝试配置BeanFactory,而是应该有 groups bean并仅加载与实际运行的服务相关的

遗留方法是让中间XML文件包含其他文件的导入,这些文件包含我在bean上面调用 groups 的内容,并在主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>

您只需要启用相关的配置文件,可以是programmaticaly:

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

或甚至作为JVM属性:

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

参考:Spring参考手册中的第Environment Abstraction章。

答案 1 :(得分:2)

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

我们通过在方法customizeContext中向ConfigurableWebApplicationContext添加BeanFactoryPostProcessor来找到一种方法。它似乎解决了我们的问题。

代码更改为:

protected void customizeContext(ServletContext servletContext, ConfigurableWebApplicationContext applicationContext) {
    super.customizeContext(servletContext, applicationContext);
    applicationContext.addBeanFactoryPostProcessor(new BootProcessor());

}

类BootProcessor实现BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory clbf) throws BeansException {
        BeanDefinitionRegistry beanDefinitionRegistry = (BeanDefinitionRegistry) clbf;
        beanDefinitionRegistry.removeBeanDefinition("testerBean");


    }

}

答案 2 :(得分:0)

使用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?.");
        }
    }
}

如果您不使用类路径扫描,则可以将其注入main方法中:

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