Spring Boot 2内嵌Tomcat中JNDI注册及使用失败求助
Hey there! Let's dig into why your JNDI datasource isn't being found in your embedded Tomcat setup with Spring Boot 2.0.0.RELEASE. The core issue here is a timing mismatch between when Spring tries to look up the JNDI resource and when Tomcat actually binds it to the naming context. Here's how to fix it step by step:
Problem Breakdown
Your error Name [jdbc/OracleDSNonXA] is not bound in this Context happens because Spring's context initializes and tries to fetch the JNDI datasource before Tomcat finishes registering the resource in its naming context. Let's adjust the configuration to sync these processes.
Step 1: Fix Tomcat Factory Configuration
Your TomcatServletWebServerFactory setup is close, but we need to ensure the naming context initializes properly and the resource is bound correctly. Update your startup class bean:
@Bean public ServletWebServerFactory tomcatFactory() { return new TomcatServletWebServerFactory() { @Override protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) { tomcat.enableNaming(); // Prevent auto-deploy issues that can delay context setup tomcat.getHost().setAutoDeploy(false); return super.getTomcatWebServer(tomcat); } @Override protected void postProcessContext(Context context) { ContextResource resource = new ContextResource(); // Use the base name here; we'll add the java:comp/env/ prefix in lookup resource.setName("jdbc/OracleDSNonXA"); resource.setType(javax.sql.DataSource.class.getName()); resource.setProperty("driverClassName", "oracle.jdbc.driver.OracleDriver"); resource.setProperty("url", "DBURL"); resource.setProperty("username", "DBUSER"); resource.setProperty("password", "PASSWORD"); context.getNamingResources().addResource(resource); // Add a resource link to ensure cross-context lookup works ContextResourceLink resourceLink = new ContextResourceLink(); resourceLink.setName("jdbc/OracleDSNonXA"); resourceLink.setGlobal("jdbc/OracleDSNonXA"); resourceLink.setType(javax.sql.DataSource.class.getName()); context.getNamingResources().addResourceLink(resourceLink); } }; }
Step 2: Adjust JNDI DataSource Bean
We need to make sure Spring doesn't try to look up the JNDI resource too early. Update your jndiDataSource bean in ApplicationConfig:
@Bean(destroyMethod = "") public DataSource jndiDataSource() throws NamingException { JndiObjectFactoryBean bean = new JndiObjectFactoryBean(); // Match the full JNDI path with the prefix bean.setJndiName("java:comp/env/jdbc/OracleDSNonXA"); bean.setProxyInterface(javax.sql.DataSource.class); bean.setLookupOnStartup(false); // Lazy init ensures we wait until Tomcat is ready bean.setLazyInit(true); bean.afterPropertiesSet(); return (DataSource) bean.getObject(); }
Step 3: Clean Up Hibernate Configuration
Your Hibernate setup has a potential conflict. The hibernate.transaction.manager_lookup_class setting is usually for standalone Hibernate setups, not when using Spring's transaction management. Comment it out:
// jpaProperties.setProperty("hibernate.transaction.manager_lookup_class", trxManagerLookupClass);
Step 4: Quick Pre-Flight Checks
- Verify Oracle Driver: Make sure the Oracle JDBC driver is in your dependencies (e.g., for Maven):
<dependency> <groupId>com.oracle.database.jdbc</groupId> <artifactId>ojdbc8</artifactId> <version>12.2.0.1</version> </dependency> - Remove Auto-Configured Datasource: If you have
spring.datasource.*properties inapplication.properties, delete them—Spring Boot will auto-create a datasource that conflicts with your JNDI setup. - Double-Check Credentials/URL: Ensure your DBURL, DBUSER, and PASSWORD are correct (no typos!).
Why This Works
In Spring Boot 2.x, embedded Tomcat starts in parallel with the Spring context. By enabling Tomcat's naming early, adding the resource link, and making the JNDI bean lazy-init, we ensure Spring waits until Tomcat has fully bound the datasource before trying to look it up.
内容的提问来源于stack exchange,提问作者sahas

