SpringBoot | 3.1 配置数据源


前言

前面介绍了SpringBoot的自动配置原理,用一句话归纳是:启动时加载全部,最终按照条件进行装配。本章节表面上是讲数据访问,但其核心仍是讲SpringBoot的自动配置,只不过自动配置的对象是数据库相关的依赖(如:druid、MyBatis、MyBatis-Plugs等)。这些依赖的导入与装配都是SpringBoot帮咱们自动完成的。css

SpringBoot默认使用的数据源是Hikari(下面有源码分析),之后咱们将使用阿里的Druid数据源进行数据库相关配置与操做。java

在本篇,咱们能够知道:mysql

  • 数据源的自动配置原理(2.);
  • SpringBoot整合第三方技术的两种方式(3.);
  • SpringBoot在处理自动配置时的逻辑(3.2.2);
  • Druid数据源自动配置原理(3.2.2);

能够直接跳转至第四点总结那查看源码结构图。git


1. 数据源的自动配置

咱们先基于SpringBoot默认的HikariDataSource数据源,导入JDBC场景,看看SpringBoot帮咱们自动配置了什么。github

首先导入JDBC场景依赖web

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>

导入JDBC场景依赖后,咱们能够在Maven的Dependencies依赖里看出spring-boot-starter-data-jdbc自动帮咱们引入了数据源、JDBC与事务相关jar包。spring

JDBC场景
少了数据库链接驱动依赖sql

咱们能够发现,这其中没有数据库链接驱动依赖,道理很简单,SpringBoot并不知道咱们要使用什么数据库(MySQL仍是Oracle或其余)。数据库

直接在pom.xml文件里添加数据库驱动依赖便可app

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

咱们不用关注数据库驱动的版本,在SpringBoot里使用版本仲裁自动匹配( 数据库版本和驱动版本对应 ),固然,咱们也能够自定义版本。

自定义链接驱动版本:

  1. pom.xmldependency直接依赖引入具体版本(maven的就近依赖原则)

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.49</version>
    </dependency>
  2. pom.xmlproperties从新声明版本(maven属性的就近优先原则)

    <properties>
        <java.version>1.8</java.version>
        <mysql.version>5.1.49</mysql.version>
    </properties>

2. *数据源自动配置源码分析

基于上面的例子,咱们来剖析SpringBoot数据源的自动装配原理。

因为数据源的配置是SpringBoot帮咱们自动配置的,所以咱们在外部依赖库里找到jdbc相关的自动配置:
在这里插入图片描述
往下翻翻找到jdbc的包:

在这里插入图片描述
能够看到引入jdbc相关依赖后SpringBoot帮咱们引入了不少自动配置类,如:

  • DataSourceAutoConfiguration:数据源的自动配置类;

  • DataSourceTransactionManagerAutoConfiguration:事务管理器的自动配置;

  • JdbcTemplateAutoConfiguration:JdbcTemplate的自动配置,能够来对数据库进行crud(JdbcTemplate是Spring对JDBC的封装,目的是使JDBC更加易于使用);

  • JndiDataSourceAutoConfiguration:jndi的自动配置;

  • XADataSourceAutoConfiguration:分布式事务相关的自动配置;

  • 等......

咱们对其中两个进行分析:

2.1 DataSourceAutoConfiguration:数据源自动配置类

解释了SpringBoot底层的数据源是Hikari;

其中DataSourceAutoConfiguration是数据源的自动配置类,咱们点进去看源码,发现其定义了一些静态方法,其中底层数据源相关的是:

@Configuration(proxyBeanMethods = false)
@Conditional({DataSourceAutoConfiguration.PooledDataSourceCondition.class})
@ConditionalOnMissingBean({DataSource.class, XADataSource.class})
@Import({Hikari.class, Tomcat.class, Dbcp2.class, OracleUcp.class, Generic.class, DataSourceJmxConfiguration.class})
protected static class PooledDataSourceConfiguration {
    protected PooledDataSourceConfiguration() {
    }
}

其中老熟人@ConditionalOnMissingBean注解的含义是当容器内没有DataSource数据源时,才进行下面的自动配置默认的数据源;而@Import注解则说明了咱们要引入的默认数据源是Hikari数据源。也就是说,在咱们不作任何处理的状况下,SpringBoot为咱们底层配置好的链接池是:HikariDataSource


2.2 JdbcTemplateAutoConfiguration:JdbcTemplate的自动配置类

说明了jdbc配置的语法:包括前缀与用户可配置的属性有哪些;
JdbcTemplate是Spring对JDBC的封装,目的是使JDBC更加易于使用;

JdbcTemplateAutoConfiguration源码:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({DataSource.class, JdbcTemplate.class})
@ConditionalOnSingleCandidate(DataSource.class)
@AutoConfigureAfter({DataSourceAutoConfiguration.class})
@EnableConfigurationProperties({JdbcProperties.class})
@Import({JdbcTemplateConfiguration.class, NamedParameterJdbcTemplateConfiguration.class})
public class JdbcTemplateAutoConfiguration {
    public JdbcTemplateAutoConfiguration() {
    }
}

@EnableConfigurationProperties注解可知,自动配置的相关属性在JdbcProperties.class类里,咱们点进去看看:
在这里插入图片描述
经过源码咱们能够知道如下信息:

  • 能够在application.properties文件里修改jdbc相关属性,前缀是:spring.jdbc
  • 可配置项有:
    • fetchSize:为jdbc驱动程序提供一个提示,它提示此Statement生成的ResultSet对象须要更多行时应该从数据库获取的行数。
    • maxRows:将此Statement 对象生成的全部ResultSet对象能够包含的最大行数限制设置为给定数。
    • queryTimeout:超时设置。
    • 等……

在这里插入图片描述


2.3 修改数据源的配置项

这里直接给出示例:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/db_account
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
    
#    默认配置,可不写
#    type: com.zaxxer.hikariDataSource

测试类以下:

@Slf4j
@SpringBootTest
class Boot05WebAdminApplicationTests {

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Test
    void contextLoads() {

//        jdbcTemplate.queryForObject("select * from account_tbl")
//        jdbcTemplate.queryForList("select * from account_tbl",)
        Long aLong = jdbcTemplate.queryForObject("select count(*) from account_tbl", Long.class);
        log.info("记录总数:{}",aLong);
    }
}

3. 配置Druid数据源的两种方式

在实际生产中,咱们通常使用阿里的Druid替换默认的Hikari数据源。

Druid官方github地址为:https://github.com/alibaba/druid

在SpringBoot中,整合第三方技术有两种方式:自定义与starter。在实际生产中通常使用starter,它只须要引入一个xxx-spring-boot-starter依赖就好,能帮咱们省去不少配置工做。但咱们也可能遇到自定义配置的需求,所以这两种方法都会说起。


3.1 自定义配置Druid数据源

重点不在这,这里只列出相关步骤便可;

1) 首先引入druid数据源依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.17</version>
</dependency>

2) SpringBoot采用编写配置类的方式

在config包下新建MyDataSourceConfig类

@Configuration
public class MyDataSourceConfig {

    // 默认的自动配置是判断容器中没有才会配@ConditionalOnMissingBean(DataSource.class),即有@Bean后容器自动配置不生效
    @ConfigurationProperties("spring.datasource") //绑定属性
    @Bean
    public DataSource dataSource() throws SQLException {
        DruidDataSource druidDataSource = new DruidDataSource();

        //在注册本身的数据源时要给核心属性赋值,又由于不能写死在代码里,故抽取到配置文件里
//        druidDataSource.setUrl();
//        druidDataSource.setUsername();
//        druidDataSource.setPassword();
        
        //加入监控功能(可写进配置文件)
        druidDataSource.setFilters("stat,wall");
        //设置最大活跃线程数(可写进配置文件)
        druidDataSource.setMaxActive(10);
        return druidDataSource;
    }

    /**
     * 配置 druid的监控页功能
     * @return
     */
    @Bean
    public ServletRegistrationBean statViewServlet(){
        StatViewServlet statViewServlet = new StatViewServlet();
        ServletRegistrationBean<StatViewServlet> registrationBean = new ServletRegistrationBean<>(statViewServlet, "/druid/*");

        //配置初始化参数:须要帐号密码才能登录查看监控页面
        registrationBean.addInitParameter("loginUsername","admin");
        registrationBean.addInitParameter("loginPassword","123456");

        return registrationBean;
    }

    /**
     * WebStatFilter 用于采集web-jdbc关联监控的数据
     */
    @Bean
    public FilterRegistrationBean webStatFilter(){
        WebStatFilter webStatFilter = new WebStatFilter();
        FilterRegistrationBean<WebStatFilter> filterRegistrationBean = new FilterRegistrationBean<>(webStatFilter);
        //设置拦截路径
        filterRegistrationBean.setUrlPatterns(Arrays.asList("/*"));
        filterRegistrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");

        return filterRegistrationBean;
    }

}

优化写法:凡是setxxx方法均可以在配置文件中说明,优化后可在配置文件中直接说明。Spring原生配置方式:

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
    destroy-method="close">
    <property name="url" value="${jdbc.url}" />
    <property name="username" value="${jdbc.username}" />
    <property name="password" value="${jdbc.password}" />
    <property name="maxActive" value="20" />
    <property name="initialSize" value="1" />
    <property name="maxWait" value="60000" />
    <property name="minIdle" value="1" />
    <property name="timeBetweenEvictionRunsMillis" value="60000" />
    <property name="minEvictableIdleTimeMillis" value="300000" />
    <property name="testWhileIdle" value="true" />
    <property name="testOnBorrow" value="false" />
    <property name="testOnReturn" value="false" />
    <property name="poolPreparedStatements" value="true" />
    <property name="maxOpenPreparedStatements" value="20" />

3.2 自动配置Druid数据源及源码分析

在实际生产中,咱们大部分时候使用自动配置方式;相比上面繁琐而复杂的工做,自动配置就简单不少了,这里先列出相关步骤方法在,再作源码分析,看看SpringBoot为咱们干了些什么。

3.2.1 自动配置Druid数据源

这里也是直接给出示例代码;

1) 引入druid-starter

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.17</version>
</dependency>

*小知识:第三方提供的Starter统一用xxx-spring-boot-starter;而官方提供的Starter统一用spring-boot-starter-xxx。

2) 直接配置便可

spring:
  datasource:  #数据源的基本属性
    url: jdbc:mysql://localhost:3306/db_account
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver

    druid:  #druid数据源相关配置
      aop-patterns: com.atguigu.admin.*  #监控SpringBean
      filters: stat,wall     # 底层开启功能,stat(sql监控),wall(防火墙)

      stat-view-servlet:   # 配置监控页功能
        enabled: true  #默认false,须要手动开启
        login-username: admin  #登陆的用户名
        login-password: admin  #登陆的密码
        resetEnable: false  #禁用重置按钮

      web-stat-filter:  # 监控web
        enabled: true
        urlPattern: /*  #匹配的路径
        exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'  #排除的路径

      filter:
        stat:    # 对上面filters里面的stat的详细配置
          enabled: true
          slow-sql-millis: 1000  #慢查询时间,超过1000ms的查询都是慢查询
          logSlowSql: true  #是否使用日志记录慢查询
        wall:    # 对上面filters里面的wall的详细配置
          enabled: true
          config:
            drop-table-allow: false

SpringBoot配置官方示例:https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter

配置项列表:
https://github.com/alibaba/druid/wiki/DruidDataSource%E9%85%8D%E7%BD%AE%E5%B1%9E%E6%80%A7%E5%88%97%E8%A1%A8


3.2.2 *自动配置的源码分析

按上面操做就能把jdbc配置好了,但咱们须要知道两个问题:
1.为何引入一个依赖就能将jdbc配置好,换句话说引入这个依赖后SpringBoot帮咱们干了些什么?
2.用户可修改的配置项有哪些?
这些问题能够在下面的源码分析中找到答案。


【核心原理】原理概述:

  • 导入starter场景启动器后,根据SpringBoot的设置模式,首先找到META-INF包下的spring.factories工厂,经过读取EnableAutoConfiguration获取启动时加载的类 :XXXAutoConfiguration自动配置类;

  • 自动配置类会利用@Bean注解把场景下相关组件注册进容器中,这些组件的核心配置项会经过@EnableConfigurationProperties注解跟XXXProperties配置文件绑定;

  • 由此咱们能够得到配置类(XXXAutoConfiguration)与配置项(XXXProperties)信息。

    • 配置类(XXXAutoConfiguration)里配置了核心组件;

    • 配置项(XXXProperties)里主要包含两个信息。其一是经过@ConfigurationProperties注解能够获取配置文件的前缀(prefix=Constants.XXX);其二是配置项可修改的参数(YYY)名称及参数(ZZZ)。咱们在yml里经过[前缀.参数名称=参数](XXX.YYY=ZZZ)修改默认参数;


【案例分析】Druid数据源自动配置的实现:

上述原理在Druid数据源自动配置中简而言之就是:导入starter场景启动器,根据SpringBoot的设置模式,会有DruidDataSourceAutoConfigure自动配置类,自动配置类会把场景下相关组件注册进容器中,相关组件的核心配置项跟配置文件绑定。

引入Druid的stater依赖后,能够在META-INF包下的spring.factories工厂里找到Druid数据源的自动配置类:
在这里插入图片描述
按住“Ctrl+左键”点进去查看DruidDataSourceAutoConfigure 源码:

@Configuration
@ConditionalOnClass({DruidDataSource.class})
@AutoConfigureBefore({DataSourceAutoConfiguration.class})
@EnableConfigurationProperties({DruidStatProperties.class, DataSourceProperties.class})
@Import({DruidSpringAopConfiguration.class, DruidStatViewServletConfiguration.class, DruidWebStatFilterConfiguration.class, DruidFilterConfiguration.class})
public class DruidDataSourceAutoConfigure {
    private static final Logger LOGGER = LoggerFactory.getLogger(DruidDataSourceAutoConfigure.class);

    public DruidDataSourceAutoConfigure() {
    }

    @Bean(initMethod = "init")
    @ConditionalOnMissingBean
    public DataSource dataSource() {
        LOGGER.info("Init DruidDataSource");
        return new DruidDataSourceWrapper();
    }
}

这里有几个须要注意的点:

  • @AutoConfigureBefore(DataSourseAutoConfiguration.class)语句的含义是:在SpringBoot自动配置数据源前先配置Druid数据源;这是一个优先级的关系,即Druid场景的自动配置类优先执行;

    • 缘由:官方DataSourseAutoConfiguration里配置了Hikari数据源,只有在容器里没有数据源时才能配置数据源。(详情见本篇2.1点)
      @ConditionalOnMissingBean
      public DataSource dataSource(){
          return new DruidDataSourceWrapper();
      }
  • @Import()注解给咱们导入了如下组件:

    • DruidSpringAopConfiguration.class:利用AOP配置SpringBean监控的相关组件;

      • 配置项:spring.datasource.druid.aop-patterns
    • DruidStatViewServletConfiguration.class:监控页相关配置;

      • 配置项:spring.datasource.druid.stat-view-servlet,默认开启
    • DruidWebStatFilterConfiguration.class:web监控配置;

      • 配置项:spring.datasource.druid.web-stat-filter:默认开启
    • DruidFilterConfiguration.class:全部Druid默认filter的配置;

      private static final String FILTER_STAT_PREFIX = "spring.datasource.druid.filter.stat";
      private static final String FILTER_CONFIG_PREFIX = "spring.datasource.druid.filter.config";
      private static final String FILTER_ENCODING_PREFIX = "spring.datasource.druid.filter.encoding";
      private static final String FILTER_SLF4J_PREFIX = "spring.datasource.druid.filter.slf4j";
      private static final String FILTER_LOG4J_PREFIX = "spring.datasource.druid.filter.log4j";
      private static final String FILTER_LOG4J2_PREFIX = "spring.datasource.druid.filter.log4j2";
      private static final String FILTER_COMMONS_LOG_PREFIX = "spring.datasource.druid.filter.commons-log";
      private static final String FILTER_WALL_PREFIX = "spring.datasource.druid.filterwall";
  • 在DataSource的构造方法里,给咱们new了一个DruidDataSourceWrapper(Druid数据源包装器);点进去查看源码:

    • 能够知道:扩展配置项(绑定属性)在spring.datasource.druid里;
    • 包装器主要做用是读取用户在application文件里的前缀为spring.datasource.druid.XXX,属性为XXX的值,并用这些值对Druid数据源进行配置;

DruidDataSourceWrapper源码:

@ConfigurationProperties("spring.datasource.druid")
class DruidDataSourceWrapper extends DruidDataSource implements InitializingBean {
    @Autowired
    private DataSourceProperties basicProperties;

    DruidDataSourceWrapper() {
    }

    public void afterPropertiesSet() throws Exception {
        if (super.getUsername() == null) {
            super.setUsername(this.basicProperties.determineUsername());
        }

        if (super.getPassword() == null) {
            super.setPassword(this.basicProperties.determinePassword());
        }

        if (super.getUrl() == null) {
            super.setUrl(this.basicProperties.determineUrl());
        }

        if (super.getDriverClassName() == null) {
            super.setDriverClassName(this.basicProperties.getDriverClassName());
        }
    }

    @Autowired(
        required = false
    )
    public void autoAddFilters(List<Filter> filters) {
        super.filters.addAll(filters);
    }

    public void setMaxEvictableIdleTimeMillis(long maxEvictableIdleTimeMillis) {
        try {
            super.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);
        } catch (IllegalArgumentException var4) {
            super.maxEvictableIdleTimeMillis = maxEvictableIdleTimeMillis;
        }

    }
}


4. 总结

在本篇里咱们知道了数据源的自动配置原理,其原理结构图以下:

  • org.springframework.boot.autoconfigure.jdbc:SpringBoot自动配置包
    • DataSourceAutoConfiguration:数据源的自动配置类;
    • DataSourceTransactionManagerAutoConfiguration:事务管理器的自动配置;
    • JdbcTemplateAutoConfiguration:JdbcTemplate的自动配置;
    • JndiDataSourceAutoConfiguration:jndi的自动配置;
    • XADataSourceAutoConfiguration:分布式事务相关的自动配置;

以及Druid数据源自动配置原理,其源码结构图以下:

  • DruidDataSourceAutoConfigure:Druid数据源自动配置类;
    • @AutoConfigureBefore():优先配置Druid数据源;
    • @Import():导入组件;
      • DruidSpringAopConfiguration.class:利用AOP配置SpringBean监控的相关组件;
      • DruidStatViewServletConfiguration.class:监控页相关配置;
      • DruidWebStatFilterConfiguration.class:web监控配置;
      • DruidFilterConfiguration.class:全部Druid默认filter的配置;
    • DruidDataSourceWrapper:Druid数据源包装器;

最重要的是知道了SpringBoot整合第三方技术的两种方式:

  • 自定义;
  • starter(推荐);

对SpringBoot的自动配置逻辑也有必定的认识:

  • 导入starter场景启动器后,根据SpringBoot的设置模式,首先找到META-INF包下的spring.factories工厂,经过读取EnableAutoConfiguration获取启动时加载的类 :XXXAutoConfiguration自动配置类;

  • 自动配置类会利用@Bean注解把场景下相关组件注册进容器中,这些组件的核心配置项会经过@EnableConfigurationProperties注解跟XXXProperties配置文件绑定;

  • 由此咱们能够得到配置类(XXXAutoConfiguration)与配置项(XXXProperties)信息。

    • 配置类(XXXAutoConfiguration)里配置了核心组件;
    • 配置项(XXXProperties)里主要包含两个信息。其一是经过@ConfigurationProperties注解能够获取配置文件的前缀(prefix=Constants.XXX);其二是配置项可修改的参数(YYY)名称及参数(ZZZ)。咱们在yml里经过[前缀.参数名称=参数](XXX.YYY=ZZZ)修改默认参数;


最后

新人制做,若有错误,欢迎指出,感激涕零!
欢迎关注公众号,会分享一些更平常的东西!
如需转载,请标注出处!
相关文章
相关标签/搜索