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 并尝试使用嵌入式tomcat启动,它为我的数据源创建了一个JNDI上下文:
<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)
然而,当我@ImportResource实际上通过JNDI查找mydatasource时,它没有找到它。
<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
7条答案
按热度按时间du7egjpx1#
默认情况下,JNDI在嵌入式Tomcat中是禁用的,这会导致
NoInitialContextException
。您需要调用Tomcat.enableNaming()
来启用它。最简单的方法是使用TomcatEmbeddedServletContainer
子类:如果您采用这种方法,您还可以通过覆盖
TomcatEmbeddedServletContainerFactory
子类中的postProcessContext
方法在JNDI中注册DataSource
。context.getNamingResources().addResource
将资源添加到java:comp/env
上下文,因此资源的名称应为jdbc/mydatasource
,而不是java:comp/env/mydatasource
。Tomcat使用线程上下文类加载器来确定应该针对哪个JNDI上下文执行查找。您要将资源绑定到Web应用程序的JNDI上下文,因此需要确保在Web应用程序的类加载器是线程上下文类加载器时执行查找。您应该能够通过在
jndiObjectFactoryBean
上将lookupOnStartup
设置为false
来实现这一点。您还需要将expectedType
设置为javax.sql.DataSource
:这将为DataSource创建一个代理,实际的JNDI查找将在第一次使用时执行,而不是在应用程序上下文启动期间执行。
上面描述的方法在this Spring Boot sample中说明。
ntjbwcob2#
我最近需要在Sping Boot 中使用带有嵌入式Tomcat的JNDI。
实际的答案给予了一些有趣的提示来解决我的任务,但这还不够,因为可能没有为Sping Boot 2更新。
这是我在Sping Boot 2.0.3.RELEASE上测试的贡献。
指定运行时类路径中可用的数据源
您有多种选择:
如果你没有指定它们中的任何一个,使用默认配置,数据源的示例化将抛出一个异常:
org.apache.tomcat.jdbc.pool.DataSourceFactory
。你可以在资源声明中这样做:
resource.setProperty("factory", "org.apache.tomcat.jdbc.pool.DataSourceFactory");
我将在下面解释在哪里添加这一行。<dependency> <groupId>org.apache.tomcat</groupId> <artifactId>tomcat-dbcp</artifactId> <version>8.5.4</version> </dependency>
当然,根据您的Sping Boot Tomcat嵌入式版本调整工件版本。
<dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <version>3.1.0</version> </dependency>
并指定在资源声明中附带的工厂:
数据源配置/声明
您必须自定义创建
TomcatServletWebServerFactory
示例的bean。要做两件事:
例如,对于PostgreSQL和DBCP 2数据源,请执行以下操作:
这里是Tomcat JDBC和HikariCP数据源的变体。
在
postProcessContext()
中,按照前面对Tomcat JDBC ds的解释设置工厂属性:对于HikariCP:
使用/注入数据源
您现在应该能够通过使用标准
InitialContext
示例在任何地方查找JNDI资源:您也可以使用Spring的
JndiObjectFactoryBean
来查找资源:要利用DI容器,您还可以将
DataSource
设置为Spring bean:所以你现在可以在任何Spring bean中注入DataSource,比如:
请注意,互联网上的许多示例似乎都禁用了在启动时查找JNDI资源:
但我认为这是无助的,因为它调用后,
afterPropertiesSet()
,做查找!yzckvree3#
毕竟我得到了答案感谢维基索纳,首先是豆子:
完整的代码在这里:https://github.com/wilkinsona/spring-boot-sample-tomcat-jndi
oknwwptz4#
在SpringBoot 2.1中,我找到了另一个解决方案。扩展标准工厂类方法getTomcatWebServer。然后从任何地方将其作为bean返回。
但是从context.xml加载资源不起作用。我会尽力查出来的。
hmtdttj45#
请注意,而不是
我不得不使用下面的方法签名
nvbavucw6#
你试过
@Lazy
加载数据源吗?因为您正在Spring上下文中初始化嵌入式Tomcat容器,所以必须延迟DataSource
的初始化(直到JNDI变量设置完毕)。**N.B.**我还没有机会测试这段代码!
您可能还需要在使用DataSource的任何地方添加
@Lazy
注解。例如mftmpeh87#
在Sping Boot v3中,以前的解决方案似乎不再可行。
在Spring文档中,我采用了这种方法:
1.创建
WebServerFactoryCustomizer
并创建您的jndi资源1.添加生命周期侦听器以启用命名
1.添加tomcatjdbc依赖项
定制者:
依赖性: