java 如何在 spring 中声明性地将(绑定)对象到 jndi?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5974779/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 13:47:56  来源:igfitidea点击:

how to put(bind) object to jndi in spring declaratively?

javaspringconfigurationjndi

提问by yetanothercoder

We have a plain standalone spring application and we need to put jdbc datasource in jndi. (we use jboss treecache and it need datasource to be in the jndi).

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

Some googling found most of all jndi-lookup examples with spring, where an object is already put in jndi (by tomcat or application server etc), but we need otherwise: I have a plain datasource Spring bean, which I inject to other services, but I can't inject it to TreeCache, because it needs it only from jndi.

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

Found org.springframework.jndi.JndiTemplate, which can be declared as bean, e.g.:

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

but not found how to bind with it other than in java code: fsJndiTemplate.bind(name, obj)from init-method of some other bean. Is there any way to do it declaratively?

但没有找到如何与它绑定,而不是在 java 代码中:fsJndiTemplate.bind(name, obj)来自其他 bean 的 init-method。有没有办法以声明方式做到这一点?

采纳答案by Vincent Devillers

Your can create a JndiExporter which uses a JndiTemplate to bind a map of object with a name:

您可以创建一个 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>

Your JndiExporter have to implements BeanFactoryAware to retrieve the spring bean with the injected BeanFactory.

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

This is one possible may :)

这是一种可能:)

回答by Spina

Thanks for the questions. I wrote a variant of Treydone's solution and thought it might be useful to have actual code here (as it's pretty short):

感谢您提出问题。我写了一个 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;
    }

}

Note that I implemented InitializingBean instead of BeanFactoryAware. This allows a configuration (with references) like this:

请注意,我实现了 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>

回答by superEb

I realize this is an old question, but there's a way to do this without custom code. It's fairly verbose, but 100% declarative.

我意识到这是一个老问题,但有一种方法可以在没有自定义代码的情况下做到这一点。它相当冗长,但 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>

The MethodInvokingFactoryBeancan also be used to set System properties (which comes in handy when using Atomikos), as long as the bean that reads the System properties depends-onthat MethodInvokingFactoryBean.

MethodInvokingFactoryBean也可用于设置系统属性(使用Atomikos公司时非常便利),只要该读取系统属性中的豆depends-on那个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"/>

回答by Santosh

Hi There is no standard or best practices type approach for this problem. You will have come with with your own approach. Following is the another approach which can take care of your problem.

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

  1. Make javax.naming.InitialContext a spring bean (say initialContext). Make sure you pass it a appropriate map of initial properties as required.

  2. Now create another bean say JndiBinder. Inject the bean mentioned #1 above in this bean. This bean will take a map of jndi-names and corresponding objects. For your case, the object will be datasource, already available in spring context.

  3. In the JndiBinder bean definition, write a init-method which would call bind menthod of initialContext for all the entries in the map (of jndi-names and corresponding objects). This way all the entries in the map supplied are bound to JNDI tree.

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

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

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

回答by Agustí Sánchez

If the code is being executed outside a Servlet container, e.g. in a unit test, the JNDI context needs to be emulated. Otherwise you'll get the dreaded "Need to specify class name in environment ..." error.

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

SimpleNamingContextBuilder is better suited for that, than JndiTemplate:

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

Do not overlook the "contextBuilder.activate();" line.

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