JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序,同时,JDBC也是个商标名。
1.DataSource配置
1.1 默认配置application.xml
spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=root
spring.datasource.password=****
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
需要在pom.xml加入依赖(我使用了mybatis+mysql)
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>${mybatis-spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<exclusions>
<exclusion>
<artifactId>tools</artifactId>
<groupId>com.sun</groupId>
</exclusion>
</exclusions>
</dependency>
2.自定义DataSource
1.1 application.xml配置文件
spring:
application:
name: data-multidatasource
datasource:
type: com.zaxxer.hikari.HikariDataSource
url: jdbc:mysql://localhost:3306/test
username: sa
password: ****
second-datasource:
driver-class-name: org.hsqldb.jdbc.JDBCDriver
url: jdbc:hsqldb:mem:db2
username: sa
password:****
1.2 自定义DataSource配置
@Configurationpublic class SencondDataSourceConfiguration {
@Bean
@ConfigurationProperties(prefix = "spring.datasource") public DataSource newDataSource() { return DataSourceBuilder.create().build();
}
@Bean(name = "secondDatasource") @ConfigurationProperties(prefix = "spring.second-datasource") public DataSource secondDataSource() { return DataSourceBuilder.create().build();
}
}
3.工作原理
spring boot的auto-configuration最具魔力的地方是@EnableAutoConfiguration注解
通常注解application应用使用@SpringBootApplication或者如下自定义的方式:
@Configuration
@EnableAutoConfiguration
@ComponentScanpublic class Application
{
}
@EnableAutoConfiguration注解开启了spring ApplicationContext的自动配置功能,
它通过扫描classpath下的组件,满足不同Conditions的bean注册到容器中。
spring boot提供了不同的AutoConfiguration实现类,这些类都在spring-boot-autoconfigure-{version}.jar中,用来注册各种各样的组件。
通常,当AutoConfiguration实现类打上@Configuration标签,可以作为spring配置类,当AutoConfiguration实现类打上@EnableConfigurationProperties标签,可以绑定自定义属性或者更多Conditional bean注册方法。
下面就org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration来分析一下:
/**
* {@link EnableAutoConfiguration Auto-configuration} for {@link DataSource}.
*
* @author Dave Syer
* @author Phillip Webb
* @author Stephane Nicoll
* @author Kazuki Shimizu */@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ Registrar.class, DataSourcePoolMetadataProvidersConfiguration.class })public class DataSourceAutoConfiguration { private static final Log logger = LogFactory
.getLog(DataSourceAutoConfiguration.class);
@Bean
@ConditionalOnMissingBean public DataSourceInitializer dataSourceInitializer(DataSourceProperties properties,
ApplicationContext applicationContext) { return new DataSourceInitializer(properties, applicationContext);
} /**
* Determines if the {@code dataSource} being used by Spring was created from
* {@link EmbeddedDataSourceConfiguration}.
* @param beanFactory the bean factory
* @return true if the data source was auto-configured. */
public static boolean containsAutoConfiguredDataSource(
ConfigurableListableBeanFactory beanFactory) { try {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition("dataSource"); return EmbeddedDataSourceConfiguration.class.getName()
.equals(beanDefinition.getFactoryBeanName());
} catch (NoSuchBeanDefinitionException ex) { return false;
}
}
@Conditional(EmbeddedDatabaseCondition.class)
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
@Import(EmbeddedDataSourceConfiguration.class) protected static class EmbeddedDatabaseConfiguration {
}
@Configuration
@Conditional(PooledDataSourceCondition.class)
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
@Import({ DataSourceConfiguration.Tomcat.class, DataSourceConfiguration.Hikari.class,
DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class }) protected static class PooledDataSourceConfiguration {
}
@Configuration
@ConditionalOnProperty(prefix = "spring.datasource", name = "jmx-enabled")
@ConditionalOnClass(name = "org.apache.tomcat.jdbc.pool.DataSourceProxy")
@Conditional(DataSourceAutoConfiguration.DataSourceAvailableCondition.class)
@ConditionalOnMissingBean(name = "dataSourceMBean") protected static class TomcatDataSourceJmxConfiguration {
@Bean public Object dataSourceMBean(DataSource dataSource) { if (dataSource instanceof DataSourceProxy) { try { return ((DataSourceProxy) dataSource).createPool().getJmxPool();
} catch (SQLException ex) {
logger.warn("Cannot expose DataSource to JMX (could not connect)");
}
} return null;
}
} /**
* {@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 {
}
} /**
* {@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());
}
} /**
* {@link Condition} to detect when an embedded {@link DataSource} type can be used.
* If a pooled {@link DataSource} is available, it will always be preferred to an
* {@code EmbeddedDatabase}. */
static class EmbeddedDatabaseCondition extends SpringBootCondition { private final SpringBootCondition pooledCondition = new PooledDataSourceCondition();
@Override public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage
.forCondition("EmbeddedDataSource"); if (anyMatches(context, metadata, this.pooledCondition)) { return ConditionOutcome
.noMatch(message.foundExactly("supported pooled data source"));
}
EmbeddedDatabaseType type = EmbeddedDatabaseConnection
.get(context.getClassLoader()).getType(); if (type == null) { return ConditionOutcome
.noMatch(message.didNotFind("embedded database").atAll());
} return ConditionOutcome.match(message.found("embedded database").items(type));
}
} /**
* {@link Condition} to detect when a {@link DataSource} is available (either because
* the user provided one or because one will be auto-configured). */
@Order(Ordered.LOWEST_PRECEDENCE - 10) static class DataSourceAvailableCondition extends SpringBootCondition { private final SpringBootCondition pooledCondition = new PooledDataSourceCondition(); private final SpringBootCondition embeddedCondition = new EmbeddedDatabaseCondition();
@Override public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage
.forCondition("DataSourceAvailable"); if (hasBean(context, DataSource.class) || hasBean(context, XADataSource.class)) { return ConditionOutcome
.match(message.foundExactly("existing data source bean"));
} if (anyMatches(context, metadata, this.pooledCondition, this.embeddedCondition)) { return ConditionOutcome.match(message
.foundExactly("existing auto-configured data source bean"));
} return ConditionOutcome
.noMatch(message.didNotFind("any existing data source bean").atAll());
} private boolean hasBean(ConditionContext context, Class<?> type) { return BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
context.getBeanFactory(), type, true, false).length > 0;
}
}
}
从上面看到,
1. DataSourceAutoConfiguration打上了@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })标签,这意味着只有当DataSource.class和EmbeddedDatabaseType.class出现在classpath时,DataSourceAutoConfiguration内的自动配置bean才可能被注册。
2. DataSourceAutoConfiguration打上了@EnableConfigurationProperties(DataSourceProperties.class)标签,意味着application.properties中的属性和DataSourceProperties类自动绑定了。
@ConfigurationProperties(prefix = DataSourceProperties.PREFIX)public class DataSourceProperties implements BeanClassLoaderAware, EnvironmentAware, InitializingBean { public static final String PREFIX = "spring.datasource";
...
... private String driverClassName; private String url; private String username; private String password;
... //setters and getters}
上面的配置类中说明,在application.properties中以spring.datasource开头的属性将自动绑定到DataSourceProperties对象上。其他注解,如@ConditionalOnMissingBean, @ConditionalOnClass and @ConditionalOnProperty等,标识只要条件满足,bean definition将注册到ApplicationContext中。
参考文件:
【1】http://www.liaoxuefeng.com/article/001484212576147b1f07dc0ab9147a1a97662a0bd270c20000
【2】https://dzone.com/articles/how-springboot-autoconfiguration-magic-works
[3] https://baike.baidu.com/item/jdbc/485214