spring boot自动配置之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配置

复制代码
@Configuration
public 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



@ComponentScan

public 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

 

posted @ 2018-02-06 14:00  星朝  阅读(722)  评论(0)    收藏  举报