2011-05-12 56 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();」線。

相關問題