2011-05-12 62 views
8

我们有一个普通的独立的spring应用程序,我们需要将jdbc数据源放入jndi中。 (我们使用jboss treecache,它需要数据源在jndi中)。如何在spring中声明式地将对象绑定到jndi?

一些Google搜索在spring中发现了绝大多数的jndi查找示例,其中一个对象已经放入jndi中(通过tomcat或应用服务器等),但我们需要其他方式:我有一个普通的数据源Spring bean,到其他服务,但我不能注入到TreeCache,因为它只需要从jndi。

实测值org.springframework.jndi.JndiTemplate,其可被声明为豆,例如:

<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代码:从一些其他bean的初始化方法fsJndiTemplate.bind(name, obj)。 有没有办法做到这一点声明?

+0

http://stackoverflow.com/questions/4414115/spring-as-a-jndi-provider – Polaris878 2011-05-12 18:54:14

回答

7

你可以创建一个使用JndiTemplate映射一个对象的绑定名称的JndiExporter:

<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检索的Spring bean与注入Bean工厂。

这是一个可能会:)

+0

是的,已经这样做了,似乎它工作正常)但只是好奇有点为什么春天没有这样的帮手,只有多种jndi查找方式。 – yetanothercoder 2011-05-12 10:03:45

2

嗨 没有针对此问题没有标准或最佳实践型的方法。你会用自己的方法来。以下是可以照顾您的问题的另一种方法。

  1. 使javax.naming.InitialContext成为spring bean(例如initialContext)。确保您按需要传递适当的初始属性图。

  2. 现在创建另一个bean说JndiBinder。在这个bean中注入上面提到的#1的bean。这个bean将获取jndi名称和相应对象的映射。对于你的情况,该对象将是数据源,已在春季上下文中可用。

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

+0

这实际上是我在解决此问题时所做的:http://stackoverflow.com/questions/4414115/spring-as-a-jndi-provider – Polaris878 2011-05-12 18:54:07

12

感谢您的问题。我写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; 
    } 

} 

注意,我实现的,而不是实现BeanFactoryAware的InitializingBean。这允许这样的配置(参考):

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

我意识到这是一个老问题,但有一个办法做到这一点无需自定义代码。这是相当详细的,但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-onMethodInvokingFactoryBean豆。

<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"/> 
0

如果代码在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();”线。