Spring原型在运行时继承属性

时间:2013-08-16 16:24:59

标签: java spring dynamic properties prototype

创建在运行时加载属性集的服务的最佳方法是什么(bean传递“xyz”并加载xyz.properties)?在输入命令以启动服务之前,需要能够将这些属性文件放入类路径之外的文件夹中(编辑:这可能在程序运行时随时发生)。

我已经有一个系统要做这个,我们已经使用了一年多,但我正在迁移到spring以使代码更加模块化(通过DI更容易定制服务)并且更容易维护。我现在的创建环境然后用“this”将它传递给依赖项的方法似乎从IoC的角度来看是颠倒过来的。

有没有办法在没有硬编码属性文件名的情况下使用PropertyPlaceholderConfigurer?也许只是对变量的引用我传入其依赖项可以加载的服务的构造函数?到目前为止,看起来我将不得不创建一个服务并在没有任何配置的情况下注入其依赖项,然后为每个服务调用一个单独的加载方法来传递属性,但这看起来好像我不是真的使用spring。

USE CASE:该应用程序将客户端连接池连接到各种服务器,并将来自其他应用程序的请求转发到这些服务器。必须能够由非程序员添加新配置文件,而无需关闭或重新启动应用程序。配置文件将包括主机,端口和登录信息等基本内容,但也包括更复杂的内容,例如是否使用tcp / http,ssl / https(将确定要使用的客户端类型),以及超时和池最小/最大/等(需要默认值)。

3 个答案:

答案 0 :(得分:1)

我尝试PropertyPlaceholderConfigurer,坦率地说,我无法以某种方式绕过它。使用现有选项时很容易使用,但我无法扩展框架。

所以我的方法更简单:

  1. 创建一个注释@InjectConfig,它将配置键作为参数。

  2. 在您的bean / services中,使用此批注注释字段或公共设置器。

  3. 编写一个BeanPostProcessor,它从“配置提供程序”中获取选项,并将它们注入字段/设置器。

  4. 现在您只需要一个配置提供程序。将其注入后处理器并完成。

  5. 注意:我更喜欢注释setter,因为这意味着您可以轻松地从测试中配置服务(只需调用setter),而无需为238576配置文件提供智能名称。

    编辑如果你有很多配置,那么配置工厂可能是更好的选择:

    1. 创建一个描述配置包的密钥(我通常在这里使用枚举或新类型来防止打字错误)
    2. 在创建服务时(手动或通过Spring)将此密钥放入服务中
    3. 编写一个配置工厂,可以为配置密钥返回PropertiesMap
    4. 将此工厂注入您的服务
    5. 在服务的初始化代码中,使用密钥通过工厂查找配置。

      使用这种方法,您可以拥有一个虚拟工厂,它总是在测试中返回相同的东西,而且生产更复杂的工厂。

      然后可以通过弹簧配置真正的工厂,以便它知道在哪里查找配置文件。一种方法是为每个配置密钥注册java.io.File。现在您的问题(配置服务和加载配置)完全分开。

答案 1 :(得分:0)

PropertyPlaceholderConfigurer仅在应用程序上下文初始化时读取和初始化文件一次。所以很可能你不能在运行时配置它。 但你可以有变量。例如,对于我的情况,我有默认属性和用户特定属性。因此,PropertyPlaceholderConfigurer首先从类路径加载属性,然后尝试在定义的位置(用户主文件夹)中查找其他属性。我的用户属性文件存在,因此configurer加载它并覆盖属性。

以下是我的例子:

<bean id="config" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="ignoreResourceNotFound" value="true"/> <!-- do not throw exception if file not found -->
    <property name="locations">
        <list>
            <value>classpath:server.properties</value>
            <value>file:${user.home}/user.properties</value>
        </list>
    </property>
</bean>

我不确定这个答案是你真正需要的。但我想猜猜你的实际任务是什么。因此,如果您每次访问它们时都需要重新读取属性运行时,则必须像以前一样手动执行,因为spring应用程序上下文可帮助您配置应用程序初始配置。

答案 2 :(得分:0)

似乎最好的方法可能是使用包含主ApplicationContext的ServiceManager,然后让每个Service初始化自己的FileSystemXmlApplicationContext,并将主上下文作为父类,如下所示:

public class ServiceManager {

ApplicationContext appContext;
String APP_HOME = System.getProperty("user.home") + File.separator;

public void init() {
    //set main spring context
    appContext = new AnnotationConfigApplicationContext(AppConfig.class);
}

public void start(String serviceName) throws Exception {
    ApplicationContext serviceContext = new FileSystemXmlApplicationContext(
            new String[]{APP_HOME + serviceName + ".xml"}, //path to child ctx
            appContext); //reference to parent ctx to build hierarchy
    Service service = (Service) serviceContext.getBean("service");
    service.start();
}

}

ApplicationContext有点重复,但是现在内存相当便宜,这提供了完全分离的关注点。我仍然有共享日志记录和由父上下文管理的事件系统,现在每个服务都在其自己的配置中进行了简化。我使用两种服务构建了一个概念验证,到目前为止似乎工作正常。一旦我完成其他服务并完成测试,我将添加另一条评论。

参考: http://techo-ecco.com/blog/spring-application-context-hierarchy-and-contextsingletonbeanfactorylocator/