将属性传递给 Spring 上下文

发布于 2024-07-26 16:29:10 字数 870 浏览 4 评论 0 原文

我正在使用 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 配置来实现此目的?

I'm using Spring to handle RMI calls to some remote server. It is straightforward to construct an application context and obtain the bean for remote invocations from within the client:

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

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

However I don't see a simple way to pass properties into the configuration. For example if I want to determine the host name for the remote server at runtime within the client.

I'd ideally have an entry in the Spring context like this:

<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>

and pass the properties to the context from the client as a parameter.

I can use a PropertyPlaceholderConfigurer in the context to substitute for these properties, but as far as I can tell this only works for properties read from a file.

I have an implementation that addresses this (added as an answer) but I'm looking for a standard Spring implementation to avoid rolling my own. Is there another Spring configurer (or anything else) to help initialise the configuration or am I better off looking at java config to achieve this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

花心好男孩 2024-08-02 16:29:10

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

TestClass.java

包com.spring.ioc; 

  公共类测试类{ 

      私有字符串优先; 
      私有字符串第二个; 

      公共字符串 getFirst() { 
          先返回; 
      } 

      公共无效setFirst(字符串第一){ 
          this.first = 第一个; 
      } 

      公共字符串 getSecond() { 
          返回第二个; 
      } 

      公共无效setSecond(字符串第二){ 
          this.第二=第二; 
      } 
  } 
  

SpringStart.java

包com.spring; 

  导入java.util.Properties; 

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

  公共类 SpringStart { 
      公共静态无效主(字符串[] args)抛出异常{ 
      PropertyPlaceholderConfigurer 配置器 = new PropertyPlaceholderConfigurer(); 
      属性属性 = new Properties(); 
      property.setProperty("first.prop", "第一个值"); 
      property.setProperty("second.prop", "第二个值"); 
      配置器.setProperties(属性); 

      ClassPathXmlApplicationContext 上下文 = new ClassPathXmlApplicationContext(); 
      context.addBeanFactoryPostProcessor(配置器); 

      context.setConfigLocation(“spring-config.xml”); 
      上下文.refresh(); 

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

spring-config.xml

 
   

       
          <属性名称=“first”值=“${first.prop}”/> 
          <属性名称=“第二”值=“$ {第二.prop}”/> 
       

   
  

输出:

第一个值 
  第二个值 
  

See 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>

Output:

first value
second value
小姐丶请自重 2024-08-02 16:29:10

我现有的解决方案涉及定义一个新的 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,因此它将被注入上面的地图。 然后它实现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;
}

My existing solution involves defining a new MapAwareApplicationContext that takes a Map as an additional constructor argument.

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();
}

It overrides postProcessBeanFactory() to add in a MapAwareProcessor:

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

The MapAwareProcessor implements postProcessBeforeInitialization() to inject the map into any type that implements the MapAware interface:

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

    return bean;
}

I then add a new bean to my config to declare a MapAwarePropertyPlaceholderConfigurer:

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

The configurer implements MapAware, so it will be injected with the Map as above. It then implements resolvePlaceholder() to resolve properties from the map, or delegate to the parent configurer:

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;
}
心作怪 2024-08-02 16:29:10

更新

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

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

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

但在此之前,您可能需要查看

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

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

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

Update:

Based on the question update, my suggestion is:

  1. Create a ServiceResolver bean which handles whatever you need to handle based on client input;
  2. Declare this bean as a dependency to the relevant services;
  3. At runtime, you may update / use this bean however you see fit.

The ServiceResolver can then, either on the init-method or on each invocation determine the values to return to the client, based on e.g. JNDI lookups or enviroment variables.

But before doing that, you might want to take a look at the configuration options available. You can either:

  • add property files which don't have to be present at compile-time;
  • look up values from JNDI;
  • get values from the System.properties.

If you need to lookup properties from a custom location, take a look at org.springframework.beans.factory.config.BeanFactoryPostProcessor and how the org.springframework.beans.factory.config.PropertyPlaceholderConfigurer is implemented.

The basic idea is that you get the beans with the 'raw' properties, e.g. ${jdbcDriverClassName} and then you get to resolve them and replace them with the desired values.

马蹄踏│碎落叶 2024-08-02 16:29:10

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

来自 javadoc< /a>

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

PropertyPlaceholderConfigurer can fetch properties from a file, that's true, but if it can't find them, it falls back to using system properties. This sounds like a viable option for your client application, just pass the system property in using -D when you launch the client.

From the javadoc

A configurer will also check against
system properties (e.g. "user.dir") if
it cannot resolve a placeholder with
any of the specified properties. This
can be customized via
"systemPropertiesMode".

疾风者 2024-08-02 16:29:10

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

Create an RmiProxyFactoryBean instance and configure the serviceUrl property directly in your code:

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();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文