代码之家  ›  专栏  ›  技术社区  ›  Rich Seller

将属性传递到Spring上下文

  •  8
  • Rich Seller  · 技术社区  · 16 年前

    我使用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配置来实现这一点吗?

    5 回复  |  直到 9 年前
        1
  •  1
  •   Robert Munteanu    16 年前

    更新 :

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

    1. 创建一个 ServiceResolver bean,它根据客户机输入处理您需要处理的任何事情;
    2. 将此bean声明为对相关服务的依赖关系;
    3. 在运行时,您可以根据需要更新/使用这个bean。

    这个 服务提供商 然后,就可以 init-method 或者在每次调用时,根据JNDI查找或环境变量确定返回到客户机的值。

    但在这之前,你可能想看看 configuration options 可用。你也可以:

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

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

    基本的想法是,你得到具有“原始”特性的豆子,例如 ${jdbcDriverClassName} 然后,您就可以解析它们并用所需的值替换它们。

        2
  •  13
  •   Tunaki    9 年前

    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
    
        3
  •  2
  •   Rich Seller    16 年前

    我现有的解决方案涉及到定义一个新的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;
    }
    
        4
  •  1
  •   skaffman    16 年前

    propertyplaceholderconfigurer可以从文件中获取属性,这是正确的,但如果找不到属性,则返回到使用系统属性。对于您的客户机应用程序来说,这听起来是一个可行的选项,只需在启动客户机时使用-d传递系统属性即可。

    javadoc

    配置人员还将检查 系统属性(例如“user.dir”)如果 它无法用解析占位符 任何指定的属性。这个 可通过以下方式定制 “系统属性模式”。

        5
  •  0
  •   Chin Huang    16 年前

    创建一个 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();