52
import org.apache.catalina.Context;
import org.apache.catalina.deploy.ContextResource;
import org.apache.catalina.startup.Tomcat;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatContextCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;

@Configuration
@EnableAutoConfiguration
@ComponentScan
@ImportResource("classpath:applicationContext.xml")
public class Application {

    public static void main(String[] args) throws Exception {
        new SpringApplicationBuilder()
                .showBanner(false)
                .sources(Application.class)
                .run(args);
}

@Bean
public TomcatEmbeddedServletContainerFactory tomcatFactory() {
    return new TomcatEmbeddedServletContainerFactory() {
        @Override
        protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
                Tomcat tomcat) {
            tomcat.enableNaming();
            return super.getTomcatEmbeddedServletContainer(tomcat);
        }
    };
}

@Bean
public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer() {
    return new EmbeddedServletContainerCustomizer() {
        @Override
        public void customize(ConfigurableEmbeddedServletContainer container) {
            if (container instanceof TomcatEmbeddedServletContainerFactory) {
                TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory = (TomcatEmbeddedServletContainerFactory) container;
                tomcatEmbeddedServletContainerFactory.addContextCustomizers(new TomcatContextCustomizer() {
                    @Override
                    public void customize(Context context) {
                        ContextResource mydatasource = new ContextResource();
                        mydatasource.setName("jdbc/mydatasource");
                        mydatasource.setAuth("Container");
                        mydatasource.setType("javax.sql.DataSource");
                        mydatasource.setScope("Sharable");
                        mydatasource.setProperty("driverClassName", "oracle.jdbc.driver.OracleDriver");
                        mydatasource.setProperty("url", "jdbc:oracle:thin:@mydomain.com:1522:myid");
                        mydatasource.setProperty("username", "myusername");
                        mydatasource.setProperty("password", "mypassword");

                        context.getNamingResources().addResource(mydatasource);

                    }
                });
            }
        }
    };
}

}

私はSpring Bootを使用しており、データソースのJNDIコンテキストを作成する組み込みのTomcatで起動しようとしています:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
        <version>1.1.4.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <version>1.1.4.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-oracle</artifactId>
        <version>1.0.0.RELEASE</version>
    </dependency>

@ImportResource を削除すると、アプリケーションは正常に起動します。Tomcat インスタンスに接続できます。すべてのアクチュエータ エンドポイントを確認できます。JConsole を使用して、MBean でデータソースを確認できるアプリケーションに接続できます (Catalina -> Resource -> Context -> "/" -> localhost -> javax.sql.DataSource -> jdbc/mydatasource)。

ここでは、JConsole 経由で MBean も表示されます (Tomcat -> DataSource -> / -> localhost -> javax.sql.DataSource -> jdbc/mydatasource)。

ただし、実際に JNDI を介して mydatasource を探しているものを @ImportResource すると、それが見つかりません。

<bean id="myDS" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="java:comp/env/jdbc/mydatasource"/>
</bean>

インポートした xml ファイルの関連部分

上記で構成している ContextResource は、アプリケーションが tomcat コンテナーにデプロイされるときにデプロイされる context.xml で使用していたものとまったく同じパラメーターを使用しています。インポートした Bean とアプリケーションは、Tomcat コンテナーにデプロイすると正しく動作します。

というわけで、文脈はわかったようですが、ネーミングが正しいとは思えません。リソース名のさまざまな組み合わせを試しましたが、このコンテキストでバインドされた「comp」を生成できないようです。

Caused by: javax.naming.NameNotFoundException: Name [comp/env/jdbc/mydatasource] is not bound in this Context. Unable to find [comp].
    at org.apache.naming.NamingContext.lookup(NamingContext.java:819)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:167)
    at org.apache.naming.SelectorContext.lookup(SelectorContext.java:156)
    at javax.naming.InitialContext.lookup(InitialContext.java:392)
    at org.springframework.jndi.JndiTemplate$1.doInContext(JndiTemplate.java:155)
    at org.springframework.jndi.JndiTemplate.execute(JndiTemplate.java:87)
    at org.springframework.jndi.JndiTemplate.lookup(JndiTemplate.java:152)
    at org.springframework.jndi.JndiTemplate.lookup(JndiTemplate.java:179)
    at org.springframework.jndi.JndiLocatorSupport.lookup(JndiLocatorSupport.java:95)
    at org.springframework.jndi.JndiObjectLocator.lookup(JndiObjectLocator.java:106)
    at org.springframework.jndi.JndiObjectFactoryBean.lookupWithFallback(JndiObjectFactoryBean.java:231)
    at org.springframework.jndi.JndiObjectFactoryBean.afterPropertiesSet(JndiObjectFactoryBean.java:217)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1612)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1549)
    ... 30 more
4

6 に答える 6

62

デフォルトでは、組み込みの Tomcat で JNDI が無効になっているため、NoInitialContextException. Tomcat.enableNaming()有効にするには呼び出す必要があります。これを行う最も簡単な方法は、TomcatEmbeddedServletContainerサブクラスを使用することです。

@Bean
public TomcatEmbeddedServletContainerFactory tomcatFactory() {
    return new TomcatEmbeddedServletContainerFactory() {

        @Override
        protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
                Tomcat tomcat) {
            tomcat.enableNaming();
            return super.getTomcatEmbeddedServletContainer(tomcat);
        }
    };
}

このアプローチを取る場合、サブクラスでメソッドをDataSourceオーバーライドすることにより、JNDI に を登録することもできます。postProcessContextTomcatEmbeddedServletContainerFactory

context.getNamingResources().addResourcejava:comp/envリソースをコンテキストに追加するため、リソースの名前は ではありjdbc/mydatasourceませんjava:comp/env/mydatasource

Tomcat は、スレッド コンテキスト クラス ローダーを使用して、ルックアップを実行する必要がある JNDI コンテキストを決定します。リソースを Web アプリの JNDI コンテキストにバインドしているため、Web アプリのクラス ローダーがスレッド コンテキスト クラス ローダーの場合にルックアップが実行されるようにする必要があります。lookupOnStartupを に設定することfalseで、これを実現できるはずですjndiObjectFactoryBeanexpectedTypeまた、次のように設定する必要がありますjavax.sql.DataSource

<bean class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="java:comp/env/jdbc/mydatasource"/>
    <property name="expectedType" value="javax.sql.DataSource"/>
    <property name="lookupOnStartup" value="false"/>
</bean>

これにより、アプリケーション コンテキストの起動時ではなく、最初の使用時に実際の JNDI ルックアップが実行される DataSource のプロキシが作成されます。

上記のアプローチは、この Spring Boot サンプルに示されています。

于 2014-07-24T21:55:07.267 に答える
5

SpringBoot 2.1 では、別の解決策を見つけました。標準ファクトリ クラス メソッド getTomcatWebServer を拡張します。そして、どこからでもBeanとして返します。

public class CustomTomcatServletWebServerFactory extends TomcatServletWebServerFactory {

    @Override
    protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
        System.setProperty("catalina.useNaming", "true");
        tomcat.enableNaming();
        return new TomcatWebServer(tomcat, getPort() >= 0);
    }
}

@Component
public class TomcatConfiguration {
    @Bean
    public ConfigurableServletWebServerFactory webServerFactory() {
        TomcatServletWebServerFactory factory = new CustomTomcatServletWebServerFactory();

        return factory;
    }

ただし、context.xml からのリソースの読み込みは機能しません。調べてみます。

于 2018-11-24T11:48:09.030 に答える
1

@Lazyデータソースを読み込んでみましたか? Spring コンテキスト内で組み込み Tomcat コンテナーを初期化しているため、DataSource(JNDI 変数がセットアップされるまで) 初期化を遅らせる必要があります。

注意:このコードをまだテストする機会がありません!

@Lazy
@Bean(destroyMethod="")
public DataSource jndiDataSource() throws IllegalArgumentException, NamingException {
    JndiObjectFactoryBean bean = new JndiObjectFactoryBean();
    bean.setJndiName("java:comp/env/jdbc/myDataSource");
    bean.setProxyInterface(DataSource.class);
    //bean.setLookupOnStartup(false);
    bean.afterPropertiesSet();
    return (DataSource)bean.getObject();
}

@Lazyまた、DataSource が使用されている場所に注釈を追加する必要がある場合もあります。例えば

@Lazy
@Autowired
private DataSource dataSource;
于 2016-05-04T07:03:27.083 に答える
1

代わりに注意してください

public TomcatEmbeddedServletContainerFactory tomcatFactory()

次のメソッドシグネチャを使用する必要がありました

public EmbeddedServletContainerFactory embeddedServletContainerFactory() 
于 2015-10-22T09:16:37.577 に答える