使用Spring配置文件设置系统属性

60

配置
Spring 2.5、Junit 4、Log4j
log4j文件位置由系统属性指定。

${log.location}

运行时,使用-D java选项设置系统属性。一切正常。

问题/所需:
在单元测试时,系统属性未设置,文件位置未解析。
应用程序使用Spring,希望简单配置Spring以设置系统属性。

更多信息:
要求仅限于配置。不能引入新的Java代码或IDE条目。理想情况下,Spring的某个属性配置实现可以处理此操作 - 我只是没有找到合适的组合。

这个想法接近,但需要添加Java代码:
Spring SystemPropertyInitializingBean

有任何帮助吗?赞赏任何想法。


相关问题及附加回答:https://dev59.com/omgu5IYBdhLWcg3wdG2o#41305482 - anre
4个回答

96

在评论中有一个关于如何在Spring 3中实现此功能的请求。

<bean id="systemPrereqs"
    class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <property name="targetObject" value="#{@systemProperties}" />
    <property name="targetMethod" value="putAll" />
    <property name="arguments">
        <!-- The new Properties -->
        <util:properties>
            <prop key="java.security.auth.login.config">/super/secret/jaas.conf</prop>
        </util:properties>
    </property>
</bean>

你会如何加载它?我只需要声明一个类型为MethodInvokingFactoryBean的@Autowired变量吗? - ziggy
3
如果我有一个需要这些系统属性设置的bean exampleBean,那么我可以使用 <bean id="exampleBean" class="ExampleBean" depends-on="systemPrereqs"/> 来确保在创建 exampleBean 之前它们被设置。请注意,该语句是针对Spring框架中的配置文件而言。 - Patrick
@Patrick 在log4j系统属性示例中还没有设置。如果使用-Dcatalina.base=target进行设置,则可以正常工作,但是在使用SpringJUnit4ClassRunner运行时是否需要特殊设置以便在log4j工具之前初始化?谢谢。 - Joseph Lust
非常有帮助,谢谢。特别是,我想使用属性文件设置一些系统属性,上述解决方案可以使用<util:properties location="classpath:my_env_var.properties" />。 - Géza
现在使用Spring 4,您可以使用MethodInvokingBean而不是MethodInvokingFactoryBean,以避免后者带来的类型确定和生命周期限制。 - Kolargol00
显示剩余2条评论

57

你可以通过两个MethodInvokingFactoryBeans的组合来实现这一点。

创建一个内部bean,访问System.getProperties,并创建一个外部bean,调用由内部bean获取的属性的putAll方法:

<bean
    class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <property
        name="targetObject">
        <!-- System.getProperties() -->
        <bean
            class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
            <property name="targetClass" value="java.lang.System" />
            <property name="targetMethod" value="getProperties" />
        </bean>
    </property>
    <property
        name="targetMethod"
        value="putAll" />
    <property
        name="arguments">
        <!-- The new Properties -->
        <util:properties>
            <prop
                key="my.key">myvalue</prop>
            <prop
                key="my.key2">myvalue2</prop>
            <prop
                key="my.key3">myvalue3</prop>

        </util:properties>
    </property>
</bean>

当然,您可以只使用一个bean并针对System.setProperties(),但这样会替换现有属性,这不是一个好主意。

无论如何,这是我的一个小测试方法:

public static void main(final String[] args) {

    new ClassPathXmlApplicationContext("classpath:beans.xml");

    System.out.println("my.key: "+System.getProperty("my.key"));
    System.out.println("my.key2: "+System.getProperty("my.key2"));
    System.out.println("my.key3: "+System.getProperty("my.key3"));

    // to test that we're not overwriting existing properties
    System.out.println("java.io.tmpdir: "+System.getProperty("java.io.tmpdir"));
}

这里是输出结果:

my.key: myvalue
my.key2: myvalue2
my.key3: myvalue3
java.io.tmpdir: C:\DOKUME~1\SEANFL~1\LOKALE~1\Temp\

1
使用Spring的depends-on属性。非常感谢您的答案...它是非常宝贵的... - Dave
8
Spring 3 简化了这个过程,不再需要第二个 MethodInvokingFactoryBean。你可以使用 SpEL,目标对象行变为 <property name="targetObject" value="#{@systemProperties}" />。 - Patrick
1
这需要使用util命名空间。是否可以改用更具可移植性的<map><entry ... />...</map>来代替? - Gray
@Gray 我不认为添加命名空间有什么问题,但是是的,你也可以使用你的方法。但是总的来说:所有仍在使用XML配置Spring的人都应该开始使用JavaConfig。这样更加自然。 - Sean Patrick Floyd
添加命名空间没有问题,但是你的答案没有提到它。<map>默认已经包含在其中了。 - Gray
显示剩余3条评论

11

Spring Batch有一个SystemPropertyInitializer类,可以更加简洁地设置系统属性。例如,可以使用它来强制JBoss日志记录使用slf4j(与Spring JPA一起):

<bean id="setupJBossLoggingProperty"
    class="org.springframework.batch.support.SystemPropertyInitializer"
    p:keyName="org.jboss.logging.provider" p:defaultValue="slf4j"/>

<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
    depends-on="setupJBossLoggingProperty"

记得添加“depends-on”属性,以强制先设置系统属性。


最终我无法让它正常工作。SystemPropertyInitializer 使用 afterPropertiesSet(),但显然在调用 <context:property-placeholder /> 之后才会被调用。 - Stewart

4

如果想使用更简洁的方法,请尝试以下代码:

<beans ... xmlns:p="http://www.springframework.org/schema/p" ...    
    <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean" 
        p:targetObject="#{@systemProperties}" p:targetMethod="setProperty"
        p:arguments="#{{'org.jboss.logging.provider','slf4j'}}"/>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接