一、问题
在启动springboot项目中遇到如下问题:
Description:
Failed to configure a DataSource: ‘url’ attribute is not specified and no embedded datasource could be configured.
Reason: Failed to determine a suitable driver class
Action:
Consider the following:
If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).
这个问题其实是url和数据源配置错误问题,果断查看配置文件和依赖。
另外,本项目用的是mybatisPlus+MySQL,使用默认连接池hakis
———————————————————————————————
二、原代码展示
1、配置文件
server:
port: 9999
spring:
redis:
host: 127.0.0.1
port: 6379
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/lkd_user?characterEncoding=utf-8&serverTimezone=UTC
username: root
password: root
经过校对,没有问题
2、依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<groupId>com.zb</groupId>
<artifactId>mysec</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--fastjson依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.33</version>
</dependency>
<!--jwt依赖-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
</project>
经过校对,暂时没有发现错误
———————————————————————————————
三、发现
发现没有存在明显的配置文件和依赖的问题,于是又看了下控制台打印日志,真正错误其实在上面,日志级别为warn。
2022-08-21 19:10:31.248 WARN 39656 — [ main] ConfigServletWebServerApplicationContext :
Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘userDetailServiceImpl’: Unsatisfied dependency expressed through field ‘tbUserMapper’; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘tbUserMapper’ defined in file [D:\maven\spring\springcloud\mysec\target\classes\com\zb\mapper\TbUserMapper.class]: Unsatisfied dependency expressed through bean property ‘sqlSessionFactory’; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘sqlSessionFactory’ defined in class path resource [com/baomidou/mybatisplus/autoconfigure/MybatisPlusAutoConfiguration.class]: Unsatisfied dependency expressed through method ‘sqlSessionFactory’ parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘dataSource’ defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration H i k a r i . c l a s s ] : B e a n i n s t a n t i a t i o n v i a f a c t o r y m e t h o d f a i l e d ; n e s t e d e x c e p t i o n i s o r g . s p r i n g f r a m e w o r k . b e a n s . B e a n I n s t a n t i a t i o n E x c e p t i o n : F a i l e d t o i n s t a n t i a t e [ c o m . z a x x e r . h i k a r i . H i k a r i D a t a S o u r c e ] : F a c t o r y m e t h o d ′ d a t a S o u r c e ′ t h r e w e x c e p t i o n ; n e s t e d e x c e p t i o n i s o r g . s p r i n g f r a m e w o r k . b o o t . a u t o c o n f i g u r e . j d b c . D a t a S o u r c e P r o p e r t i e s Hikari.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.zaxxer.hikari.HikariDataSource]: Factory method 'dataSource' threw exception; nested exception is org.springframework.boot.autoconfigure.jdbc.DataSourceProperties Hikari.class]:Beaninstantiationviafactorymethodfailed;nestedexceptionisorg.springframework.beans.BeanInstantiationException:Failedtoinstantiate[com.zaxxer.hikari.HikariDataSource]:Factorymethod′dataSource′threwexception;nestedexceptionisorg.springframework.boot.autoconfigure.jdbc.DataSourcePropertiesDataSourceBeanCreationException: Failed to determine a suitable driver class
这里打印了bean的依赖问题,如下:
userDetailServiceImpl——依赖——》tbUserMapperl——依赖——》 sqlSessionFactory——依赖——》MybatisPlusAutoConfiguration ——依赖——》dataSource——依赖——》jdbc.DataSourceProperties
这里最终原因是:Failed to determine a suitable driver class,翻译下就是:无法确定合适的驱动程序类。错误主要跟DataSource有关。
———————————————————————————————
四、找原因
1、查看DataSourceConfiguration类,看看导入的实际DataSource配置。
abstract class DataSourceConfiguration {
//创建DataBase
@SuppressWarnings("unchecked")
protected static <T> T createDataSource(DataSourceProperties properties, Class<? extends DataSource> type) {
return (T) properties.initializeDataSourceBuilder().type(type).build();
}
/**
* Hikari DataSource configuration.
*/
@Configuration
@ConditionalOnClass(HikariDataSource.class)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource",
matchIfMissing = true)
static class Hikari {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.hikari")
public HikariDataSource dataSource(DataSourceProperties properties) {
HikariDataSource dataSource = createDataSource(properties, HikariDataSource.class);
if (StringUtils.hasText(properties.getName())) {
dataSource.setPoolName(properties.getName());
}
return dataSource;
}
//省略其他Database配置(tomcat 、dbcp2、generic)
}
}
这里断点,走的是HikariDataSource的dataSource()工厂方法,然后调用createDataSource()。
评估表达式(ALT+F8)计算createDataSource(),正好是导致异常的最终原因:
org.springframework.boot.autoconfigure.jdbc.DataSourceProperties$DataSourceBeanCreationException: Failed to determine a suitable driver class
(断点的时候已经发现jdbc爆红,没有成功导入)
继续往下走,进入到DataSourceProperties类initializeDataSourceBuilder()
public DataSourceBuilder<?> initializeDataSourceBuilder() {
return DataSourceBuilder.create(getClassLoader()).type(getType()).driverClassName(determineDriverClassName())
.url(determineUrl()).username(determineUsername()).password(determinePassword());
}
这里采用建造者模式链式调用,方法依次为创建——》类型——》驱动类名——》URL——》用户名——》密码。
评估表达式(ALT+F8)递进式计算:链式调用的返回结果。发现链式调用到driverClassName(determineDriverClassName())报错,并且后面也的determine()全部报错。
(这里已经发现问题,猜测配置文件声明的DriverClassName、url、username、password没有被识别到)
继续往下走,进入到determineDriverClassName()方法
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)) {
//这里正好是控制台日志warn信息的最后一行提示
throw new DataSourceBeanCreationException("Failed to determine a suitable driver class", this,
this.embeddedDatabaseConnection);
}
return driverClassName;
}
运行到这已经结束,错误信息中嵌套的最后异常就是出自这里
DataSourceBeanCreationException: Failed to determine a suitable driver class
断点中发现,该方法的类DataSourceProperties中的字段driverClassName、url、username、password全部为NULL。
于是又打开了其他运行正常的项目,断点上述各个位置,重新debug
首先进入到DataSourceConfiguration类的dataSource方法
可以看到,这个时候已经完成配置项的读取,往下走到DataSourceProperties类的initializeDataSourceBuilder()方法
这里并没有引发异常,同时发现determineXXX()方法都能得到对应的值。
到这,可以确定是配置信息没有成功被读取到,也就是YAML配置失效。
———————————————————————————————
五、解决方案
1、检查YAML文件位置、是否存在语法错误(四个配置项都没有成功读取,可以排除)
2、查看POM文件是否存在配置错误
最后发现问题是POM打包方式错误,多写了一行
<modelVersion>4.0.0</modelVersion>
<!-- <packaging>pom</packaging>-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
———————————————————————————————
六、建议
1、不用看mybatis的mapper接口和映射文件、实体类。
项目启动时没有查数据库,也就没有调用mapper层,自然不会引发这个错误。
就算报错上提示信息也很明确,网上很多说要检查这个,感觉没有必要。
2、看yml/properties配置文件中跟url相关的,是否存在错误。
配置写多了,基本不会出现语法或声明错误,而且有代码提示