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)
org.mybatis
mybatis
${mybatis.version}
org.mybatis
mybatis-spring
${mybatis-spring.version}
org.springframework.boot
spring-boot-starter-web
mysql
mysql-connector-java
com.zaxxer
HikariCP
tools
com.sun
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 classSencondDataSourceConfiguration {
@Bean
@ConfigurationProperties(prefix= "spring.datasource")publicDataSource newDataSource() {returnDataSourceBuilder.create().build();
}
@Bean(name= "secondDatasource")
@ConfigurationProperties(prefix= "spring.second-datasource")publicDataSource secondDataSource() {returnDataSourceBuilder.create().build();
}
}
3.工作原理
spring boot的auto-configuration最具魔力的地方是@EnableAutoConfiguration注解
通常注解application应用使用@SpringBootApplication或者如下自定义的方式:
@Configuration
@EnableAutoConfiguration
@ComponentScanpublic classApplication
{
}
@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来分析一下:
/*** {@linkEnableAutoConfiguration Auto-configuration} for {@linkDataSource}.
*
*@authorDave Syer
*@authorPhillip Webb
*@authorStephane Nicoll
*@authorKazuki Shimizu*/@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class})
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ Registrar.class, DataSourcePoolMetadataProvidersConfiguration.class})public classDataSourceAutoConfiguration {private static final Log logger =LogFactory
.getLog(DataSourceAutoConfiguration.class);
@Bean
@ConditionalOnMissingBeanpublicDataSourceInitializer dataSourceInitializer(DataSourceProperties properties,
ApplicationContext applicationContext) {return newDataSourceInitializer(properties, applicationContext);
}/*** Determines if the {@codedataSource} being used by Spring was created from
* {@linkEmbeddedDataSourceConfiguration}.
*@parambeanFactory the bean factory
*@returntrue if the data source was auto-configured.*/
public static booleancontainsAutoConfiguredDataSource(
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 classEmbeddedDatabaseConfiguration {
}
@Configuration
@Conditional(PooledDataSourceCondition.class)
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class})
@Import({ DataSourceConfiguration.Tomcat.class, DataSourceConfiguration.Hikari.class,
DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class})protected static classPooledDataSourceConfiguration {
}
@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 classTomcatDataSourceJmxConfiguration {
@BeanpublicObject dataSourceMBean(DataSource dataSource) {if (dataSource instanceofDataSourceProxy) {try{return((DataSourceProxy) dataSource).createPool().getJmxPool();
}catch(SQLException ex) {
logger.warn("Cannot expose DataSource to JMX (could not connect)");
}
}return null;
}
}/*** {@linkAnyNestedCondition} that checks that either {@codespring.datasource.type}
* is set or {@linkPooledDataSourceAvailableCondition} applies.*/
static class PooledDataSourceCondition extendsAnyNestedCondition {
PooledDataSourceCondition() {super(ConfigurationPhase.PARSE_CONFIGURATION);
}
@ConditionalOnProperty(prefix= "spring.datasource", name = "type")static classExplicitType {
}
@Conditional(PooledDataSourceAvailableCondition.class)static classPooledDataSourceAvailable {
}
}/*** {@linkCondition} to test if a supported connection pool is available.*/
static class PooledDataSourceAvailableCondition extendsSpringBootCondition {
@OverridepublicConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message=ConditionMessage
.forCondition("PooledDataSource");if (getDataSourceClassLoader(context) != null) {returnConditionOutcome
.match(message.foundExactly("supported DataSource"));
}returnConditionOutcome
.noMatch(message.didNotFind("supported DataSource").atAll());
}/*** Returns the class loader for the {@linkDataSource} class. Used to ensure that
* the driver class can actually be loaded by the data source.
*@paramcontext the condition context
*@returnthe class loader*/
privateClassLoader getDataSourceClassLoader(ConditionContext context) {
Class> dataSourceClass = newDataSourceBuilder(context.getClassLoader())
.findType();return (dataSourceClass == null ? null: dataSourceClass.getClassLoader());
}
}/*** {@linkCondition} to detect when an embedded {@linkDataSource} type can be used.
* If a pooled {@linkDataSource} is available, it will always be preferred to an
* {@codeEmbeddedDatabase}.*/
static class EmbeddedDatabaseCondition extendsSpringBootCondition {private final SpringBootCondition pooledCondition = newPooledDataSourceCondition();
@OverridepublicConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message=ConditionMessage
.forCondition("EmbeddedDataSource");if (anyMatches(context, metadata, this.pooledCondition)) {returnConditionOutcome
.noMatch(message.foundExactly("supported pooled data source"));
}
EmbeddedDatabaseType type=EmbeddedDatabaseConnection
.get(context.getClassLoader()).getType();if (type == null) {returnConditionOutcome
.noMatch(message.didNotFind("embedded database").atAll());
}return ConditionOutcome.match(message.found("embedded database").items(type));
}
}/*** {@linkCondition} to detect when a {@linkDataSource} is available (either because
* the user provided one or because one will be auto-configured).*/@Order(Ordered.LOWEST_PRECEDENCE- 10)static class DataSourceAvailableCondition extendsSpringBootCondition {private final SpringBootCondition pooledCondition = newPooledDataSourceCondition();private final SpringBootCondition embeddedCondition = newEmbeddedDatabaseCondition();
@OverridepublicConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message=ConditionMessage
.forCondition("DataSourceAvailable");if (hasBean(context, DataSource.class)|| hasBean(context, XADataSource.class)) {returnConditionOutcome
.match(message.foundExactly("existing data source bean"));
}if (anyMatches(context, metadata, this.pooledCondition,this.embeddedCondition)) {returnConditionOutcome.match(message
.foundExactly("existing auto-configured data source bean"));
}returnConditionOutcome
.noMatch(message.didNotFind("any existing data source bean").atAll());
}private boolean hasBean(ConditionContext context, Class>type) {returnBeanFactoryUtils.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 implementsBeanClassLoaderAware, EnvironmentAware, InitializingBean {public static final String PREFIX = "spring.datasource";
...
...privateString driverClassName;privateString url;privateString username;privateString 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