9

我正在使用 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 来实现这一点?

4

5 回答 5

13

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
于 2010-02-03T15:25:35.550 回答
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;
}
于 2009-07-06T10:10:08.167 回答
1

更新

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

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

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

但在此之前,您可能需要查看可用的配置选项。您可以:

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

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

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

于 2009-07-06T09:53:10.917 回答
1

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

来自javadoc

如果无法解析具有任何指定属性的占位符,配置器还将检查系统属性(例如“user.dir”)。这可以通过“systemPropertiesMode”进行定制。

于 2009-07-06T10:00:39.607 回答
0

创建一个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();
于 2009-07-06T13:04:27.447 回答