【问题标题】:how to put(bind) object to jndi in spring declaratively?如何以声明方式在spring中将(绑定)对象放入jndi?
【发布时间】:2011-08-23 21:43:01
【问题描述】:

我们有一个普通的独立 spring 应用程序,我们需要将 jdbc 数据源放在 jndi 中。 (我们使用 jboss 树缓存,它需要数据源在 jndi 中)。

一些谷歌搜索发现大多数使用 spring 的 jndi-lookup 示例,其中一个对象已经放入 jndi(通过 tomcat 或应用程序服务器等),但我们需要其他:我有一个普通的数据源 Spring bean,我将其注入其他服务,但我不能将它注入到 TreeCache,因为它只需要来自 jndi。

找到org.springframework.jndi.JndiTemplate,可以声明为bean,例如:

<bean id="fsJndiTemplate" class="org.springframework.jndi.JndiTemplate">
    <property name="environment">
        <props>
            <prop key="java.naming.factory.initial">com.sun.jndi.fscontext.RefFSContextFactory</prop>
            <prop key="java.naming.provider.url">file:///c:\windows\temp</prop>
        </props>
    </property>
</bean>

但除了在 java 代码中之外,没有找到如何与它绑定:fsJndiTemplate.bind(name, obj) from init-method of some other bean。 有没有办法以声明的方式做到这一点?

【问题讨论】:

标签: java spring configuration jndi


【解决方案1】:

您可以创建一个 JndiExporter,它使用 JndiTemplate 将对象映射与名称绑定:

<bean id="jndiExporter" class="org.xxx.JndiExporter">
    <property name="jndiTemplate" ref="jndiTemplate">
    <property name="objects">
          <map>
            <entry key="name1" value="bean1"/>
            <entry key="name2" value="bean2"/>
            <entry key="name3" value="bean3"/>
            <entry key="name4" value="bean4"/>
          </map>
    </property>
</bean>

您的 JndiExporter 必须实现 BeanFactoryAware 才能使用注入的 BeanFactory 检索 spring bean。

这是一种可能:)

【讨论】:

  • 是的,已经这样做了,看起来效果很好)但是有点好奇为什么春天没有这样的助手,只有多种 jndi 查找方式。
【解决方案2】:

嗨 对于这个问题,没有标准或最佳实践类型的方法。您将采用自己的方法。以下是另一种可以解决您的问题的方法。

  1. 使 javax.naming.InitialContext 成为一个 spring bean(比如 initialContext)。确保根据需要向其传递适当的初始属性映射。

  2. 现在创建另一个 bean,比如 JndiBinder。在这个bean中注入上面提到的#1 bean。这个 bean 将采用 jndi-names 和相应对象的映射。对于您的情况,该对象将是数据源,已在 spring 上下文中可用。

  3. 在 JndiBinder bean 定义中,编写一个 init 方法,该方法将为映射中的所有条目(jndi 名称和相应对象)调用 initialContext 的绑定方法。这样,提供的映射中的所有条目都绑定到 JNDI 树。

【讨论】:

【解决方案3】:

感谢您的提问。我写了一个 Treydone 解决方案的变体,并认为在这里有实际代码可能很有用(因为它很短):

public class JndiExporter implements InitializingBean {

    private final JndiTemplate template = new JndiTemplate();

    private Map<String, Object> jndiMapping = null;

    @Override
    public void afterPropertiesSet() throws Exception {
            for(Entry<String, Object> addToJndi: jndiMapping.entrySet()){
                    template.bind(addToJndi.getKey(), addToJndi.getValue());
            }
    }

    public void setJndiMapping(Map<String, Object> jndiMapping) {
            this.jndiMapping = jndiMapping;
    }

}

请注意,我实现的是 InitializingBean 而不是 BeanFactoryAware。这允许这样的配置(带有引用):

<bean id="jndiExporter" class="com.ra.web.util.JndiExporter">
    <property name="jndiMapping">
        <map>
            <entry key="bean1" value-ref="other_spring_bean_id" />
            <entry key="bean2" value="literal_value" />
        </map>
    </property>
</bean>

【讨论】:

    【解决方案4】:

    我意识到这是一个老问题,但有一种方法可以在没有自定义代码的情况下做到这一点。它相当冗长,但 100% 是声明性的。

    <!-- inside container, use JndiTemplate -->
    <bean id="jndiBinder" class="org.springframework.jndi.JndiTemplate"/>
    <!-- outside container (e.g. for tests), use SimpleNamingContextBuilder -->
    <!-- <bean id="jndiBinder" class="org.springframework.mock.jndi.SimpleNamingContextBuilder" factory-method="emptyActivatedContextBuilder"/> -->
    
    <!-- use MethodInvokingFactoryBean to call 'bind' on 'jndiBinder' -->
    <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="targetObject" ref="jndiBinder"/>
        <property name="targetMethod" value="bind"/>
        <property name="arguments">
            <array>
                <value type="java.lang.String">java:comp/UserTransaction</value>
                <ref bean="atomikosUserTransaction"/>
            </array>
        </property>
    </bean>
    
    <!-- define as many bindings as you need -->
    <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="targetObject" ref="jndiBinder"/>
        <property name="targetMethod" value="bind"/>
        <property name="arguments">
            <array>
                <value type="java.lang.String">another/jndi/name</value>
                <value>literal_value</value>
            </array>
        </property>
    </bean>
    

    MethodInvokingFactoryBean也可以用来设置系统属性(which comes in handy when using Atomikos),只要读取系统属性depends-on的bean就是MethodInvokingFactoryBean

    <bean id="atomikosSystemProps" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="targetObject">
            <bean class="java.lang.System" factory-method="getProperties"/>
        </property>
        <property name="targetMethod" value="putAll"/>
        <property name="arguments" ref="atomikosJtaProps"/>
    </bean>
    <bean id="atomikosJtaProps" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
        <property name="properties">
            <props>
                <prop key="com.atomikos.icatch.no_file">true</prop>
                <prop key="com.atomikos.icatch.hide_init_file_path">true</prop>
                <prop key="com.atomikos.icatch.service">com.atomikos.icatch.standalone.UserTransactionServiceFactory</prop>
                <prop key="com.atomikos.icatch.log_base_dir">/opt/txlogs</prop>
            </props>
        </property>
    </bean>
    <bean id="atomikosUserTransactionService" class="com.atomikos.icatch.config.UserTransactionServiceImp" init-method="init" destroy-method="shutdownForce" depends-on="atomikosSystemProps"/>
    

    【讨论】:

      【解决方案5】:

      如果代码在 Servlet 容器之外执行,例如在单元测试中,需要模拟 JNDI 上下文。否则你会得到可怕的“需要在环境中指定类名......”错误。

      SimpleNamingContextBuilder 比 JndiTemplate 更适合:

      public class JndiExporter implements InitializingBean {
      
      private final SimpleNamingContextBuilder contextBuilder = new SimpleNamingContextBuilder();
      
      private Map<String, Object> jndiMapping = null;
      
      @Override
      public void afterPropertiesSet() throws Exception {
          for (Entry<String, Object> addToJndi : jndiMapping.entrySet()) {
              contextBuilder.bind(addToJndi.getKey(), addToJndi.getValue());
          }
      
          contextBuilder.activate();
      }
      
      public void setJndiMapping(Map<String, Object> jndiMapping) {
          this.jndiMapping = jndiMapping;
      }
      

      不要忽视“contextBuilder.activate();”行。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-11-25
        • 2012-01-10
        • 1970-01-01
        • 1970-01-01
        • 2018-12-02
        • 2017-01-24
        • 1970-01-01
        • 2011-03-25
        相关资源
        最近更新 更多