zoukankan      html  css  js  c++  java
  • 深入Spring Boot:怎样排查 Cannot determine embedded database driver class for database type NONE

    写在前面
    这个demo来说明怎么一步步排查一个常见的spring boot AutoConfiguration的错误。

    https://github.com/hengyunabc/spring-boot-inside/tree/master/demo-database-type-NONE

    调试排查 Cannot determine embedded database driver class for database type NONE 的错误
    把工程导入IDE里,直接启动应用,抛出来的异常信息是:


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


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    其实这时有两个思路,直接google搜索Cannot determine embedded database driver class for database type NONE,就可以找到解决办法。

    第二种方式,仔细查看日志内容,可以发现有To display the auto-configuration report re-run your application with 'debug' enabled.。

    搜索下这个,就可以在spring的官方网站上找到相关的信息:https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-auto-configuration.html

    就是用户只要配置了debug这个开关,就会把auto-configuration 相关的信息打印出来。

    熟悉spring的环境变量注入的话,就可以知道有几种打开这个的方式:

    在args里增加--debug
    在application.properties里增加debug=true
    通过-Ddebug=true
    增加debug开关之后的信息
    增加debug开关之后,可以看到打印出了错误堆栈:

    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]
    1
    2
    3
    4
    5
    6
    7
    8
    抛出异常的代码是:

    /**
    * 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;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    可以看出来是没有找到 DataSource 的driver class,然后抛出了 DataSourceBeanCreationException。

    那么一种解决办法是,在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 {

    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    那么 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 {

    }

    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    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());
    }

    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    从代码里,可以看到是尝试查找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 })
    1
    在application.properties里配置:

    spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration
    1
    总结
    应用没有使用到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具体的判断代码。

    原文链接:https://blog.csdn.net/hengyunabc/article/details/78762097

  • 相关阅读:
    JVM 常量池、运行时常量池、字符串常量池
    JVM Direct Memory
    JVM 方法区
    JVM GC Roots
    jvm 堆
    jvm slot复用
    JVM 虚拟机栈
    JVM 程序计数器
    java打印树形目录结构
    java 通过反射获取数组
  • 原文地址:https://www.cnblogs.com/cxhfuujust/p/11653012.html
Copyright © 2011-2022 走看看