調試排查 Cannot determine embedded database driver class for database type NONE 的錯誤
把工程導入IDE裏,直接啓動應用,拋出來的異常信息是:html
Error starting ApplicationContext. To display the auto-configuration report re-run your application with 'debug' enabled. 2017-11-29 14:26:34.478 ERROR 29736 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPLICATION FAILED TO START *************************** Description: Cannot determine embedded database driver class for database type NONE Action: If you want an embedded database please put a supported one on the classpath. If you have database settings to be loaded from a particular profile you may need to active it (no profiles are currently active).
其實這時有兩個思路,直接google搜索Cannot determine embedded database driver class for database type NONE,就能夠找到解決辦法。java
第二種方式,仔細查看日誌內容,能夠發現有To display the auto-configuration report re-run your application with 'debug' enabled.。spring
搜索下這個,就能夠在spring的官方網站上找到相關的信息:https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-auto-configuration.htmlapache
就是用戶只要配置了debug這個開關,就會把auto-configuration 相關的信息打印出來。tomcat
熟悉spring的環境變量注入的話,就能夠知道有幾種打開這個的方式:bash
在args裏增長--debug
在application.properties裏增長debug=true
經過-Ddebug=true
增長debug開關以後的信息
增長debug開關以後,能夠看到打印出了錯誤堆棧:app
2017-11-29 14:33:08.776 DEBUG 29907 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter : Application failed to start due to an exception org.springframework.boot.autoconfigure.jdbc.DataSourceProperties$DataSourceBeanCreationException: Cannot determine embedded database driver class for database type NONE. If you want an embedded database please put a supported one on the classpath. If you have database settings to be loaded from a particular profile you may need to active it (no profiles are currently active). at org.springframework.boot.autoconfigure.jdbc.DataSourceProperties.determineDriverClassName(DataSourceProperties.java:245) ~[spring-boot-autoconfigure-1.4.7.RELEASE.jar:1.4.7.RELEASE] at org.springframework.boot.autoconfigure.jdbc.DataSourceProperties.initializeDataSourceBuilder(DataSourceProperties.java:182) ~[spring-boot-autoconfigure-1.4.7.RELEASE.jar:1.4.7.RELEASE] at org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration.createDataSource(DataSourceConfiguration.java:42) ~[spring-boot-autoconfigure-1.4.7.RELEASE.jar:1.4.7.RELEASE] at org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Tomcat.dataSource(DataSourceConfiguration.java:53) ~[spring-boot-autoconfigure-1.4.7.RELEASE.jar:1.4.7.RELEASE] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_112]
拋出異常的代碼是: maven
/** * Determine the driver to use based on this configuration and the environment. * @return the driver to use * @since 1.4.0 */ public String determineDriverClassName() { if (StringUtils.hasText(this.driverClassName)) { Assert.state(driverClassIsLoadable(), "Cannot load driver class: " + this.driverClassName); return this.driverClassName; } String driverClassName = null; if (StringUtils.hasText(this.url)) { driverClassName = DatabaseDriver.fromJdbcUrl(this.url).getDriverClassName(); } if (!StringUtils.hasText(driverClassName)) { driverClassName = this.embeddedDatabaseConnection.getDriverClassName(); } if (!StringUtils.hasText(driverClassName)) { throw new DataSourceBeanCreationException(this.embeddedDatabaseConnection, this.environment, "driver class"); } return driverClassName; }
能夠看出來是沒有找到 DataSource 的driver class,而後拋出了 DataSourceBeanCreationException。ide
那麼一種解決辦法是,在maven依賴里加入一些 DataSource driver class。函數
可是應用本身的代碼裏並無使用DataSource,哪裏致使spring boot要建立一個DataSource對象?哪裏致使spring boot要建立DataSource
從異常棧上,能夠找到DataSourceConfiguration$Tomcat 這個類,那麼查找下它的引用,能夠發現它是被
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration.PooledDataSourceConfiguration import引入的。 @Configuration @Conditional(PooledDataSourceCondition.class) @ConditionalOnMissingBean({ DataSource.class, XADataSource.class }) @Import({ DataSourceConfiguration.Tomcat.class, DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Dbcp.class, DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class }) protected static class PooledDataSourceConfiguration { }
那麼 PooledDataSourceConfiguration 是怎麼生效的呢?從代碼上能夠看到@Conditional(PooledDataSourceCondition.class)。
那麼再看PooledDataSourceCondition的具體實現:
/** * {@link AnyNestedCondition} that checks that either {@code spring.datasource.type} * is set or {@link PooledDataSourceAvailableCondition} applies. */ static class PooledDataSourceCondition extends AnyNestedCondition { PooledDataSourceCondition() { super(ConfigurationPhase.PARSE_CONFIGURATION); } @ConditionalOnProperty(prefix = "spring.datasource", name = "type") static class ExplicitType { } @Conditional(PooledDataSourceAvailableCondition.class) static class PooledDataSourceAvailable { } }
PooledDataSourceCondition引入了@Conditional(PooledDataSourceAvailableCondition.class) : /** * {@link Condition} to test if a supported connection pool is available. */ static class PooledDataSourceAvailableCondition extends SpringBootCondition { @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { ConditionMessage.Builder message = ConditionMessage .forCondition("PooledDataSource"); if (getDataSourceClassLoader(context) != null) { return ConditionOutcome .match(message.foundExactly("supported DataSource")); } return ConditionOutcome .noMatch(message.didNotFind("supported DataSource").atAll()); } /** * Returns the class loader for the {@link DataSource} class. Used to ensure that * the driver class can actually be loaded by the data source. * @param context the condition context * @return the class loader */ private ClassLoader getDataSourceClassLoader(ConditionContext context) { Class<?> dataSourceClass = new DataSourceBuilder(context.getClassLoader()) .findType(); return (dataSourceClass == null ? null : dataSourceClass.getClassLoader()); } }
從代碼裏,能夠看到是嘗試查找dataSourceClass,若是找到,條件就成立。那麼debug下,能夠發現查找到的dataSourceClass是:org.apache.tomcat.jdbc.pool.DataSource 。
那麼再看下org.apache.tomcat.jdbc.pool.DataSource這個類是從哪裏來的呢?
從maven依賴樹能夠看到,依賴是來自:spring-boot-starter-jdbc。因此是應用依賴了spring-boot-starter-jdbc,可是並無配置DataSource引發的問題。
問題解決辦法
有兩種:
沒有使用到DataSource,則能夠把spring-boot-starter-jdbc的依賴去掉,這樣就不會觸發spring boot相關的代碼
把spring boot自動初始化DataSource相關的代碼禁止掉
禁止的辦法有兩種:
在main函數上配置exclude
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class })
在application.properties裏配置:
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration
總結
應用沒有使用到DataSource,可是在pom.xml裏引入了spring-boot-starter-jdbc
spring-boot-starter-jdbc帶入了tomcat-jdbc,它裏面有org.apache.tomcat.jdbc.pool.DataSource
spring boot裏的PooledDataSourceConfiguration,判斷classpath下面有DataSource的實現類,嘗試去建立DataSource bean
在初始化DataSourceProperties時,嘗試經過jdbc的url來探測driver class
由於應用並無配置url,因此最終在DataSourceProperties.determineDriverClassName()裏拋出Cannot determine embedded database driver class for database type NONE
最後:
排查spring boot的AutoConfiguration問題時,能夠按異常棧,一層層排查Configuration是怎麼引入的,再排查Condition具體的判斷代碼。