【问题标题】:Create ApplicationContext as Spring bean (by other application context)将 ApplicationContext 创建为 Spring bean(通过其他应用程序上下文)
【发布时间】:2010-07-21 20:47:12
【问题描述】:

我如何在其他应用程序上下文中将一个 ApplicationContext 定义为原型 spring bean。我还需要将当前上下文作为父级传递给新的应用程序上下文。

详情:

我有 Bean,它代表富客户端应用程序中的一个用户会话。此类管理应用程序上下文的生命周期,以及一些其他对象(如数据库连接)。此会话 bean 本身由特殊的“启动应用程序上下文”配置。

现在我想对这个会话 bean 进行单元测试,但是遇到了麻烦,因为在会话 bean 中创建了特定于会话的应用程序上下文,并且有很多依赖于“启动上下文”;

示例代码:

public class UserDBAminSession implements ApplicationContextAware, UserSession {
    ApplicationContext startupContext;
    ApplicationContext sessionContext;

    public void setApplicationContext(ApplicationContext startupContext) {...}

    public void start() {
        createSessionContext() ;
    }

    private void createSessionContext() {
        sessionContext = new ClassPathXmlApplicationContext("admin-session.xml", startupContext);
    }
}

出于测试目的,我希望使用类似这样的重复 createSessionContext 函数代码:

sessionContext = startupContext.getBean("adminContext", ApplicationContext.class);

然后我可以创建 startupContext 的模拟,返回一些存根。甚至 DI“会话上下文”在未来的某个时候会在春天变成 bean。但是,我不知道如何将父上下文参数传递给 ClassPathXmlApplicationContext 构造函数。我正在尝试这样的事情,但它似乎不起作用:

<bean id="adminContext" class="org.springframework.context.support.ClassPathXmlApplicationContext"
        scope="prototype" autowire="constructor">
    <constructor-arg type="java.lang.String">
        <value>admin-session.xml</value>
    </constructor-arg>
</bean>

我也在考虑在顶层创建应用程序上下文并通过 setter 传递它,但是:

  1. 这只是将问题移到更高级别,而不是解决。事实上它已经完成了(UserSession - 这是“顶级”)。
  2. 这打破了 RAII 模式。
  3. 这需要大量的代码重构。

或者制作特殊的“上下文工厂”对象,但它已经不是最好的代码了。

看起来很愚蠢,我不能从 IoC 框架本身进行 IoC 对象。可能是我误读了一些春季文档?

任何其他想法,如何对这个类进行单元测试?

【问题讨论】:

    标签: java spring


    【解决方案1】:

    使用FactoryBeanApplicationContextAware 接口。

    public class ChildApplicationContextFactoryBean implements FactoryBean, ApplicationContextAware {
    
        protected String[] configLocations;
    
        protected ApplicationContext applicationContext;
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext = applicationContext;
        }
    
        @Override
        public Object getObject() throws Exception {
            return new ClassPathXmlApplicationContext(configLocations, applicationContext);
        }
    
        @Override
        public Class getObjectType() {
            return ClassPathXmlApplicationContext.class;
        }
    
        @Override
        public boolean isSingleton() {
            return true;
        }
    
        public void setConfigLocations(String[] configLocations) {
            this.configLocations = configLocations;
        }
    
    }
    

    用法:

    <bean class="com.skg.ecg.portal.operation.transit.ChildApplicationContextFactoryBean">
        <property name="configLocations">
            <list>
                <value>applicationContext.xml</value>
            </list>
        </property>
    </bean>
    

    【讨论】:

      【解决方案2】:

      如果我的理解正确,您的要求是在手动控制的范围内管理一组 bean(在本例中为您的 RIA 会话)。

      Spring 支持scoped beans。你有基本的singletonprototype 作用域,webapps 也有requestsession 作用域。如果您的 RIA 会话实际上是 HTTP 会话,那么我建议您使用 session-scoped beans 而不是手动嵌套的应用程序上下文设计。

      如果您的会话与网络无关,那么您仍然可以选择定义自己的custom scope。这方面还有更多工作要做,但它是容器中已定义的扩展点,因此您处于安全的基础上。

      您最初认为应用程序上下文本身就是父上下文中的 bean 的想法可行,是的,但在这种情况下可能没有必要,只会增加复杂性。但是,如果您想进一步研究它,请查看SingletonBeanFactoryLocator,这是一个用于管理应用程序上下文层次结构的 Spring 基础结构类。它不会做你想要的特定工作,但它可能会给你一些想法。

      【讨论】:

      • 这不是网络应用程序。我知道范围 bean,但在这种情况下是不可接受的。每个子应用程序上下文都非常不同,甚至不是所有的 jar 文件都可能存在于用户安装中。 SingletonBeanFactoryLocator 似乎也不是我需要的东西。例如:用户进行一些数据库管理任务,并希望看到更改。然后我必须删除数据库管理上下文,并启动一般用户上下文。父上下文也不是微不足道的,必须在创建子之前配置(例如,我需要注入会话引用)。但我尝试在注销时重新启动完整的应用程序。
      猜你喜欢
      • 1970-01-01
      • 2013-04-12
      • 2012-12-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-03
      相关资源
      最近更新 更多