【问题标题】:Passing properties to a Spring context将属性传递给 Spring 上下文
【发布时间】:2009-07-06 09:42:37
【问题描述】:

我正在使用 Spring 处理对某个远程服务器的 RMI 调用。构造应用程序上下文并从客户端获取远程调用的 bean 很简单:

ApplicationContext context = new ApplicationContext("classpath:context.xml");

MyService myService = (MyService ) context.getBean( "myService " );

但是,我没有看到将属性传递到配置中的简单方法。例如,如果我想在客户端运行时确定远程服务器的主机名。

理想情况下,我应该在 Spring 上下文中有一个这样的条目:

<bean id="myService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
  <property name="serviceUrl" value="rmi://${webServer.host}:80/MyService"/>
  <property name="serviceInterface" value="com.foo.MyService"/>
</bean>

并将属性作为参数从客户端传递给上下文。

我可以在上下文中使用 PropertyPlaceholderConfigurer 来替换这些属性,但据我所知,这仅适用于从文件中读取的属性。

我有一个解决这个问题的实现(作为答案添加),但我正在寻找一个标准的 Spring 实现以避免自己滚动。是否有另一个 Spring 配置器(或其他任何东西)来帮助初始化配置,或者我最好查看 java config 来实现这一点?

【问题讨论】:

  • 您还会在哪里存储您的属性?
  • 客户端可以连接到任何服务器,例如用户可以在文本字段中输入主机名。所以不是在编译时确定的。

标签: java spring rmi


【解决方案1】:

http://forum.springsource.org/showthread.php?t=71815

TestClass.java

package com.spring.ioc;

public class TestClass {

    private String first;
    private String second;

    public String getFirst() {
        return first;
    }

    public void setFirst(String first) {
        this.first = first;
    }

    public String getSecond() {
        return second;
    }

    public void setSecond(String second) {
        this.second = second;
    }
}

SpringStart.java

package com.spring;

import java.util.Properties;

import com.spring.ioc.TestClass;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;

public class SpringStart {
    public static void main(String[] args) throws Exception {
    PropertyPlaceholderConfigurer configurer = new PropertyPlaceholderConfigurer();
    Properties properties = new Properties();
    properties.setProperty("first.prop", "first value");
    properties.setProperty("second.prop", "second value");
    configurer.setProperties(properties);

    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
    context.addBeanFactoryPostProcessor(configurer);

    context.setConfigLocation("spring-config.xml");
    context.refresh();

    TestClass testClass = (TestClass)context.getBean("testBean");
    System.out.println(testClass.getFirst());
    System.out.println(testClass.getSecond());
    }
}

spring-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

    <bean id="testBean" class="com.spring.ioc.TestClass">
        <property name="first" value="${first.prop}"/>
        <property name="second" value="${second.prop}"/>
    </bean>

</beans>

输出:

first value
second value

【讨论】:

    【解决方案2】:

    我现有的解决方案涉及定义一个新的 MapAwareApplicationContext,它将 Map 作为额外的构造函数参数。

    public MapAwareApplicationContext(final URL[] configURLs,
        final String[] newConfigLocations,
        final Map<String, String> additionalProperties) {
        super(null);
    
        //standard constructor content here
    
        this.map = new HashMap<String, String>(additionalProperties);
    
        refresh();
    }
    

    它会覆盖 postProcessBeanFactory() 以添加到 MapAwareProcessor:

    protected void postProcessBeanFactory(
        final ConfigurableListableBeanFactory beanFactory) {
        beanFactory.addBeanPostProcessor(new MapAwareProcessor(this.map));
        beanFactory.ignoreDependencyInterface(MapAware.class);
    }
    

    MapAwareProcessor 实现 postProcessBeforeInitialization() 以将地图注入任何实现 MapAware 接口的类型:

    public Object postProcessBeforeInitialization(final Object bean, 
            final String beanName) {
        if (this.map != null && bean instanceof MapAware) {
            ((MapAware) bean).setMap(this.map);
        }
    
        return bean;
    }
    

    然后我在我的配置中添加一个新的 bean 来声明一个 MapAwarePropertyPlaceholderConfigurer:

    <bean id="propertyConfigurer"
      class="com.hsbc.r2ds.spring.MapAwarePropertyPlaceholderConfigurer"/>
    

    配置器实现了 MapAware,所以它会像上面那样被注入 Map。然后它实现 resolvePlaceholder() 来解析地图中的属性,或者委托给父配置器:

    protected String resolvePlaceholder(final String placeholder, 
            final Properties props, final int systemPropertiesMode) {
        String propVal = null;
        if (this.map != null) {
            propVal = this.map.get(placeholder);
        }
        if (propVal == null) {
            propVal = super.resolvePlaceholder(placeholder, props);
        }
        return propVal;
    }
    

    【讨论】:

    • 这就是我的观点,这似乎不需要太多努力就可以实现,当然比这更省力
    • 或者你可以使用 BeanFactoryPostProcessor:springindepth.com/book/…
    【解决方案3】:

    更新

    根据问题更新,我的建议是:

    1. 创建一个ServiceResolver bean,它可以根据客户端输入处理您需要处理的任何内容;
    2. 将此 bean 声明为相关服务的依赖项;
    3. 在运行时,您可以按照自己认为合适的方式更新/使用此 bean。

    然后ServiceResolver 可以在init-method 或每次调用上确定返回给客户端的值,例如基于JNDI 查找或环境变量。

    但在此之前,您可能需要查看可用的configuration options。您可以:

    • 添加在编译时不必存在的属性文件;
    • 从 JNDI 中查找值;
    • 从 System.properties 中获取值。

    如果您需要从自定义位置查找属性,请查看org.springframework.beans.factory.config.BeanFactoryPostProcessor 以及org.springframework.beans.factory.config.PropertyPlaceholderConfigurer 的实现方式。

    基本思想是你得到具有“原始”属性的 bean,例如${jdbcDriverClassName} 然后您可以解决它们并将它们替换为所需的值。

    【讨论】:

    • 谢谢,我现有的实现就是这样做的,我会更新我的问题以反映这一点。我希望有一个标准的 Spring 实现,这样我就可以避免自己滚动。
    【解决方案4】:

    PropertyPlaceholderConfigurer 可以从文件中获取属性,这是真的,但如果它找不到它们,它会回退到使用系统属性。这听起来像是您的客户端应用程序的一个可行选项,只需在启动客户端时使用 -D 传递系统属性。

    来自javadoc

    配置器也会检查 系统属性(例如“user.dir”)如果 它无法解析占位符 任何指定的属性。这 可以通过定制 “系统属性模式”。

    【讨论】:

      【解决方案5】:

      创建一个RmiProxyFactoryBean 实例并直接在您的代码中配置serviceUrl 属性:

      String serverHost = "www.example.com";
      
      RmiProxyFactoryBean factory = new RmiProxyFactoryBean();
      factory.setServiceUrl("rmi://" + serverHost + ":80/MyService");
      factory.setServiceInterface(MyService.class);
      try {
          factory.afterPropertiesSet();
      } catch (Exception e) {
          throw new RuntimeException(
                  "Problem initializing myService factory", e);
      }
      MyService myService = (MyService) factory.getObject();
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-04-30
        • 2012-02-14
        • 2022-09-29
        • 1970-01-01
        • 2015-03-18
        • 1970-01-01
        • 2020-07-15
        • 2020-06-25
        相关资源
        最近更新 更多