我们有一个普通的独立spring应用程序,我们需要将jdbc数据源放在jndi中。 (我们使用jboss treecache,它需要数据源在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)
来自其他bean的init-method。
有没有办法以声明的方式做到这一点?
答案 0 :(得分: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;
}
}
请注意,我实现了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>
答案 1 :(得分:7)
您可以创建一个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。
这是可能的一种:)
答案 2 :(得分: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-on
的{{1}}。
MethodInvokingFactoryBean
答案 3 :(得分:3)
您好 此问题没有标准或最佳实践类型方法。您将拥有自己的方法。以下是另一种可以解决您问题的方法。
使javax.naming.InitialContext成为一个spring bean(比如initialContext)。确保根据需要将初始属性的适当地图传递给它。
现在创建另一个bean说JndiBinder。在这个bean中注入上面提到的#1 bean。这个bean将获取jndi-names和相应对象的映射。对于您的情况,该对象将是数据源,已在spring上下文中提供。
在JndiBinder bean定义中,编写一个init方法,该方法将为地图中的所有条目(jndi-names和相应对象)调用initialContext的bind menthod。这样,提供的映射中的所有条目都绑定到JNDI树。
答案 4 :(得分: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();”线。