spring boot自動配置之jdbc

1.DataSource配置java

1.1 默認配置application.xmlmysql

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)web

      <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>spring

2.自定義DataSourcesql

1.1 application.xml配置文件apache

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配置tomcat

@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.工做原理springboot

 spring boot的auto-configuration最具魔力的地方是@EnableAutoConfiguration註解mybatis

 一般註解application應用使用@SpringBootApplication或者以下自定義的方式:app

@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

相關文章
相關標籤/搜索