如何在 Spring 中以声明方式将对象绑定(绑定)到 jndi?

发布于 2024-11-06 11:29:20 字数 884 浏览 1 评论 0原文

我们有一个普通的独立 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。 有没有办法以声明方式做到这一点?

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).

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.

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

迷爱 2024-11-13 11:29:20

感谢您的提问。我编写了 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>

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):

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:

<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>
明明#如月 2024-11-13 11:29:20

我意识到这是一个老问题,但是有一种方法可以在没有自定义代码的情况下做到这一点。它相当冗长,但 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 还可以用于设置系统属性(当使用 Atomikos),只要读取系统属性的 Bean 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"/>

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.

<!-- 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 MethodInvokingFactoryBean can 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-on that 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"/>
迟到的我 2024-11-13 11:29:20

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

这是一种可能:)

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

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

This is one possible may :)

唔猫 2024-11-13 11:29:20

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

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

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

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

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.

谁许谁一生繁华 2024-11-13 11:29:20

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

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.

SimpleNamingContextBuilder is better suited for that, than 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文