本来的项目(基于SpringBoot 2.0.5-RELEASE)集成了JPA、mybatis的注解、XML方式访问DB。 后面集成多数据源的时候启动SpringBoot时出现了如下错误:
ERROR 32320 — [main] com.zaxxer.hikari.HikariConfig : HikariPool-1 - jdbcUrl is required with driverClassName. Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘entityManagerFactory’ defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Unsatisfied dependency expressed through method ‘entityManagerFactory’ parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘entityManagerFactoryBuilder’ defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Unsatisfied dependency expressed through method ‘entityManagerFactoryBuilder’ parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘jpaVendorAdapter’ defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.orm.jpa.JpaVendorAdapter]: Factory method ‘jpaVendorAdapter’ threw exception; nested exception is java.lang.IllegalArgumentException: jdbcUrl is required with driverClassName. at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:732) at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:474) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1247) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1096) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:535) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495) at org.springframework.beans.factory.support.AbstractBeanFactory.lambdadoGetBean0(AbstractBeanFactory.java:317) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1089) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:859) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550) at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:780) at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:412) at org.springframework.boot.SpringApplication.run(SpringApplication.java:333) at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:139) at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99) at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:117) … 24 mor
后来查看了源码,流程如下:
代码语言:javascript复制@Bean(name = "test1DataSource")
@ConfigurationProperties(prefix = "spring.datasource")
@Primary
public DataSource testDataSource() {
return DataSourceBuilder.create().build();
}
进入DataSourceBuilder.create().build():
代码语言:javascript复制public T build() {
Class<? extends DataSource> type = getType();
DataSource result = BeanUtils.instantiateClass(type);
maybeGetDriverClassName();
bind(result);
return (T) result;
}
maybeGetDriverClassName(); 方法:
代码语言:javascript复制private void maybeGetDriverClassName() {
if (!this.properties.containsKey("driverClassName")
&& this.properties.containsKey("url")) {
String url = this.properties.get("url");
String driverClass = DatabaseDriver.fromJdbcUrl(url).getDriverClassName();
this.properties.put("driverClassName", driverClass);
}
}
bind(result) 方法:
代码语言:javascript复制private void bind(DataSource result) {
ConfigurationPropertySource source = new MapConfigurationPropertySource(
this.properties);
ConfigurationPropertyNameAliases aliases = new ConfigurationPropertyNameAliases();
aliases.addAliases("url", "jdbc-url");
aliases.addAliases("username", "user");
Binder binder = new Binder(source.withAliases(aliases));
binder.bind(ConfigurationPropertyName.EMPTY, Bindable.ofInstance(result));
}
因为springboot首推的连接池使用了HikariCP连接池,GET到这个点, HikariDataSource的获取数据库连接的getConnection方法:
代码语言:javascript复制@Override
public Connection getConnection() throws SQLException
{
if (isClosed()) {
throw new SQLException("HikariDataSource " this " has been closed.");
}
if (fastPathPool != null) {
return fastPathPool.getConnection();
}
// See http://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java
HikariPool result = pool;
if (result == null) {
synchronized (this) {
result = pool;
if (result == null) {
validate();
LOGGER.info("{} - Starting...", getPoolName());
try {
pool = result = new HikariPool(this);
this.seal();
}
catch (PoolInitializationException pie) {
if (pie.getCause() instanceof SQLException) {
throw (SQLException) pie.getCause();
}
else {
throw pie;
}
}
LOGGER.info("{} - Start completed.", getPoolName());
}
}
}
return result.getConnection();
}
HikariConfig的validate方法
代码语言:javascript复制public void validate()
{
if (poolName == null) {
poolName = generatePoolName();
}
else if (isRegisterMbeans && poolName.contains(":")) {
throw new IllegalArgumentException("poolName cannot contain ':' when used with JMX");
}
// treat empty property as null
catalog = getNullIfEmpty(catalog);
connectionInitSql = getNullIfEmpty(connectionInitSql);
connectionTestQuery = getNullIfEmpty(connectionTestQuery);
transactionIsolationName = getNullIfEmpty(transactionIsolationName);
dataSourceClassName = getNullIfEmpty(dataSourceClassName);
dataSourceJndiName = getNullIfEmpty(dataSourceJndiName);
driverClassName = getNullIfEmpty(driverClassName);
jdbcUrl = getNullIfEmpty(jdbcUrl);
// Check Data Source Options
if (dataSource != null) {
if (dataSourceClassName != null) {
LOGGER.warn("{} - using dataSource and ignoring dataSourceClassName.", poolName);
}
}
else if (dataSourceClassName != null) {
if (driverClassName != null) {
LOGGER.error("{} - cannot use driverClassName and dataSourceClassName together.", poolName);
// NOTE: This exception text is referenced by a Spring Boot FailureAnalyzer, it should not be
// changed without first notifying the Spring Boot developers.
throw new IllegalStateException("cannot use driverClassName and dataSourceClassName together.");
}
else if (jdbcUrl != null) {
LOGGER.warn("{} - using dataSourceClassName and ignoring jdbcUrl.", poolName);
}
}
else if (jdbcUrl != null || dataSourceJndiName != null) {
// ok
}
else if (driverClassName != null) {
LOGGER.error("{} - jdbcUrl is required with driverClassName.", poolName);
throw new IllegalArgumentException("jdbcUrl is required with driverClassName.");
}
else {
LOGGER.error("{} - dataSource or dataSourceClassName or jdbcUrl is required.", poolName);
throw new IllegalArgumentException("dataSource or dataSourceClassName or jdbcUrl is required.");
}
validateNumerics();
if (LOGGER.isDebugEnabled() || unitTest) {
logConfiguration();
}
}
这个if分支:
代码语言:javascript复制jdbcUrl = getNullIfEmpty(jdbcUrl);
else if (driverClassName != null) {
LOGGER.error("{} - jdbcUrl is required with driverClassName.", poolName);
throw new IllegalArgumentException("jdbcUrl is required with driverClassName.");
}
这里要求DataSource的参数存在 jdbcUrl 、以及driverClassName,而我们的配置是:
代码语言:javascript复制spring.datasource.url=jdbc:mysql://localhost:3306/mypydb
spring.datasource.username=root
spring.datasource.password=11111111
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource2.url=jdbc:mysql://localhost:3306/mypydb2
spring.datasource2.username=root
spring.datasource2.password=11111111
spring.datasource2.driver-class-name=com.mysql.jdbc.Driver
所以将url改为 jdbc-url 即可。
代码语言:javascript复制spring.datasource.jdbc-url=jdbc:mysql://localhost:3306/mypydb
spring.datasource2.jdbc-url=jdbc:mysql://localhost:3306/mypydb2