SpringBoot的数据访问和启动配置原理

moran
2020-11-13 / 0 评论 / 24 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2021年04月14日,已超过59天没有更新,若内容或图片失效,请留言反馈。

SpringBoot的数据访问

对于数据访问层,无论是SQL还是NOSQL,SpringBoot默认采用整合SpringData的方式进行统一处理,添加大量自动配置,屏蔽很多配置。引入各种xxxTemplate,xxxRepository来简化我们对数据访问层的操作。对我们来说只需要进行简单的设置即可。

整合基本JDBC与数据源

1)、引入starter
引入spring-boot-starter-jdbc启动器依赖

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

2)、配置applicaiton.yml

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/jdbc?serverTimezone=GMT
    username: root
    password: 123456

3)、测试

@Test
void contextLoads() throws SQLException {
    // class com.zaxxer.hikari.HikariDataSource
    System.out.println(dataSource.getClass());
    Connection connection = dataSource.getConnection();
    System.out.println(connection);
    connection.close();
}
  • 在SpirngBoot1.x时默认数据源是org.apache.tomcat.pool.DataSource
  • 在SpringBoot2.x及以上版本是使用com.zaxxer.hikari.HikariDataSource作为默认数据源
  • 数据源的自动配置的Properties类是DataSourceProperties
  • SpringBoot默认可以支持连接池:
org.apache.tomcat.jdbc.pool.DataSource、HikariDataSource、BasicDatasource(DBCP)
  • SpringBoot还默认支持项目启动通过sql文件建表。默认只需将文件命名为(放置在classPath下):
schema-*.sql、data-*.sql
默认规则:schema.sql、schema-all.sql

同时还可以通过配置文件指定sql文件。

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/jdbc?serverTimezone=GMT
    username: root
    password: 123456
    schema:
      - classPath:department.sql
  • SpringBoot还自动配置了JdbcTemplate用于操作数据(可以注入)
@Autowired
JdbcTemplate jdbcTemplate;
@ResponseBody
@GetMapping("/query")
public Map<String,Object> map(){
    List<Map<String, Object>> list = jdbcTemplate.queryForList("select * from department");
    return list.get(0);
}

4)、高级配置:使用druid数据源

  • 引入druid依赖
<!--使用druid数据连接池-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.3</version>
</dependency>
  • 配置属性
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/jdbc?serverTimezone=GMT
    username: root
    password: 123456
    # 更改数据源
    type: com.alibaba.druid.pool.DruidDataSource
    #  数据源其他配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true
    #   配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

后面的数据源其他配置是不能生效的,因为在DataSourceProperties配置属性类中是没有这些字段的,因此需要自己写配置类将数据源添加到容器中。

  • 配置类

    package top.pblog.moran.config;
    
    import com.alibaba.druid.pool.DruidDataSource;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import javax.sql.DataSource;
    
    @Configuration
    public class DruidConfig {
    
        @Bean
        @ConfigurationProperties(prefix = "spring.datasource")
        public DataSource druid(){
            return new DruidDataSource();
        }
    }

    使用配置类添加dataSource到容器后,还需要添加log4j的依赖,否则会抛出异常。

5)、配置druid数据源监控

package top.pblog.moran.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class DruidConfig {

    // 和配置文件中以spirng.datasource前缀开头的配置注入到返回的bean对象中
    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean
    public DataSource druid(){
        return new DruidDataSource();
    }

    // 配置Druid监控
    // 1.配置一个后台管理Servlet
    @Bean
    public ServletRegistrationBean statViewServlet(){
        ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
        Map<String,String> initParams = new HashMap<>();
        initParams.put("loginUsername","admin");
        initParams.put("loginPassword","123456");
        initParams.put("allow","");// 默认允许所有访问
        initParams.put("deny","localhost");
        return bean;
    }

    // 2.配置一个监控的filter

    public FilterRegistrationBean webStatFilter(){
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.setFilter(new WebStatFilter());

        Map<String,String> initParams = new HashMap<>();
        initParams.put("exclusions","*.js,*.css,/druid/");

        bean.setInitParameters(initParams);

        bean.setUrlPatterns(Arrays.asList("/*"));
        return bean;
    }
}

整合Mybatis

1)、引入starter
引入mybatis-spring-boot-starter启动器依赖

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.3</version>
</dependency>


2)、注解模式
通过@Mapper注解标注给dao层接口。mybatis会对这些列代理出对象注入到容器中。

注解@Options声明插入新记录的id生成策略为自增。
当mapper很多时,可以通过注解@MapperScan在入口类标注。

默认的应用是必须要和数据库中的字段一致的,可以通过配置类修改。
自定义mybatis配置规则:

3)、配置文件
在yml文件中配置

整合JPA



1)、引入依赖

2)、编写并注解配置实体类

3)、dao层接口

该接口以后两个泛型,第一个是操作的实体类,第二个是实体类的主键类型。
4)、基本配置

SpringBoot的启动配置原理


几个重要的事件回调机制
配置在META-INF/spring.factories
ApplicationContextInitializer
SpringApplicationRunListener
只需要放在ioc容器中
ApplicationRunner
CommandLineRunner
启动流程:
1)、创建SpringApplication对象

initialize(sources);
private void initialize(Object[] sources) {
    //保存主配置类
    if (sources != null && sources.length > 0) {
        this.sources.addAll(Arrays.asList(sources));
    }
    //判断当前是否一个web应用
    this.webEnvironment = deduceWebEnvironment();
    //从类路径下找到META-INF/spring.factories配置的所有ApplicationContextInitializer;然后保存起来
    setInitializers((Collection) getSpringFactoriesInstances(
        ApplicationContextInitializer.class));
    //从类路径下找到ETA-INF/spring.factories配置的所有ApplicationListener
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    //从多个配置类中找到有main方法的主配置类
    this.mainApplicationClass = deduceMainApplicationClass();
}


2)、运行run方法

public ConfigurableApplicationContext run(String... args) {
   StopWatch stopWatch = new StopWatch();
   stopWatch.start();
   ConfigurableApplicationContext context = null;
   FailureAnalyzers analyzers = null;
   configureHeadlessProperty();
    
   //获取SpringApplicationRunListeners;从类路径下META-INF/spring.factories
   SpringApplicationRunListeners listeners = getRunListeners(args);
    //回调所有的获取SpringApplicationRunListener.starting()方法
   listeners.starting();
   try {
       //封装命令行参数
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(
            args);
      //准备环境
      ConfigurableEnvironment environment = prepareEnvironment(listeners,
            applicationArguments);
               //创建环境完成后回调SpringApplicationRunListener.environmentPrepared();表示环境准备完成
       
      Banner printedBanner = printBanner(environment);
       
       //创建ApplicationContext;决定创建web的ioc还是普通的ioc
      context = createApplicationContext();
       
      analyzers = new FailureAnalyzers(context);
       //准备上下文环境;将environment保存到ioc中;而且applyInitializers();
       //applyInitializers():回调之前保存的所有的ApplicationContextInitializer的initialize方法
       //回调所有的SpringApplicationRunListener的contextPrepared();
       //
      prepareContext(context, environment, listeners, applicationArguments,
            printedBanner);
       //prepareContext运行完成以后回调所有的SpringApplicationRunListener的contextLoaded();
       
       //s刷新容器;ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat);Spring注解版
       //扫描,创建,加载所有组件的地方;(配置类,组件,自动配置)
      refreshContext(context);
       //从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调
       //ApplicationRunner先回调,CommandLineRunner再回调
      afterRefresh(context, applicationArguments);
       //所有的SpringApplicationRunListener回调finished方法
      listeners.finished(context, null);
      stopWatch.stop();
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass)
               .logStarted(getApplicationLog(), stopWatch);
      }
       //整个SpringBoot应用启动完成以后返回启动的ioc容器;
      return context;
   }
   catch (Throwable ex) {
      handleRunFailure(context, listeners, analyzers, ex);
      throw new IllegalStateException(ex);
   }
}

3)、事件监听机制
配置在META-INF/spring.factories
ApplicationContextInitializer

public class HelloApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("ApplicationContextInitializer...initialize..."+applicationContext);
    }
}

SpringApplicationRunListener

public class HelloSpringApplicationRunListener implements SpringApplicationRunListener {

    //必须有的构造器
    public HelloSpringApplicationRunListener(SpringApplication application, String[] args){

    }

    @Override
    public void starting() {
        System.out.println("SpringApplicationRunListener...starting...");
    }

    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {
        Object o = environment.getSystemProperties().get("os.name");
        System.out.println("SpringApplicationRunListener...environmentPrepared.."+o);
    }

    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        System.out.println("SpringApplicationRunListener...contextPrepared...");
    }

    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        System.out.println("SpringApplicationRunListener...contextLoaded...");
    }

    @Override
    public void finished(ConfigurableApplicationContext context, Throwable exception) {
        System.out.println("SpringApplicationRunListener...finished...");
    }
}

配置(META-INF/spring.factories)

org.springframework.context.ApplicationContextInitializer=\
com.atguigu.springboot.listener.HelloApplicationContextInitializer

org.springframework.boot.SpringApplicationRunListener=\
com.atguigu.springboot.listener.HelloSpringApplicationRunListener

只需要放在ioc容器中
ApplicationRunner

@Component
public class HelloApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("ApplicationRunner...run....");
    }
}

CommandLineRunner

@Component
public class HelloCommandLineRunner implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("CommandLineRunner...run..."+ Arrays.asList(args));
    }
}

自定义stater

starter:

  • 这个场景需要使用到的依赖是什么?
  • 如何编写自动配置

    @Configuration  //指定这个类是一个配置类
    @ConditionalOnXXX  //在指定条件成立的情况下自动配置类生效
    @AutoConfigureAfter  //指定自动配置类的顺序
    @Bean  //给容器中添加组件
    
    @ConfigurationPropertie结合相关xxxProperties类来绑定相关的配置
    @EnableConfigurationProperties //让xxxProperties生效加入到容器中
    
    自动配置类要能加载
    将需要启动就加载的自动配置类,配置在META-INF/spring.factories
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
    org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
  • 模式
    启动器只用来做依赖导入;

专门来写一个自动配置模块;
启动器依赖自动配置;别人只需要引入启动器(starter)
mybatis-spring-boot-starter;自定义启动器名-spring-boot-starter
步骤:

1)、启动器模块
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.atguigu.starter</groupId>
    <artifactId>atguigu-spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!--启动器-->
    <dependencies>

        <!--引入自动配置模块-->
        <dependency>
            <groupId>com.atguigu.starter</groupId>
            <artifactId>atguigu-spring-boot-starter-autoconfigurer</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>
2)、自动配置模块
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>

   <groupId>com.atguigu.starter</groupId>
   <artifactId>atguigu-spring-boot-starter-autoconfigurer</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <packaging>jar</packaging>

   <name>atguigu-spring-boot-starter-autoconfigurer</name>
   <description>Demo project for Spring Boot</description>

   <parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>1.5.10.RELEASE</version>
  <relativePath/> <!-- lookup parent from repository -->
   </parent>

   <properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  <java.version>1.8</java.version>
   </properties>

   <dependencies>

  <!--引入spring-boot-starter;所有starter的基本配置-->
  <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter</artifactId>
  </dependency>

   </dependencies>
</project>
package com.atguigu.starter;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "atguigu.hello")
public class HelloProperties {

    private String prefix;
    private String suffix;

    public String getPrefix() {
        return prefix;
    }

    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }

    public String getSuffix() {
        return suffix;
    }

    public void setSuffix(String suffix) {
        this.suffix = suffix;
    }
}
package com.atguigu.starter;

public class HelloService {

    HelloProperties helloProperties;

    public HelloProperties getHelloProperties() {
        return helloProperties;
    }

    public void setHelloProperties(HelloProperties helloProperties) {
        this.helloProperties = helloProperties;
    }

    public String sayHellAtguigu(String name){
        return helloProperties.getPrefix()+"-" +name + helloProperties.getSuffix();
    }
}
package com.atguigu.starter;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnWebApplication //web应用才生效
@EnableConfigurationProperties(HelloProperties.class)
public class HelloServiceAutoConfiguration {

    @Autowired
    HelloProperties helloProperties;
    @Bean
    public HelloService helloService(){
        HelloService service = new HelloService();
        service.setHelloProperties(helloProperties);
        return service;
    }
}

更多SpringBoot整合案例

SpringBoot简单的开发

  • 通过SpringBoot Initialzer快速创建SpringBoot项目,创建Controller就可以访问了。
  • 配置类


通过@Bean标注的返回的对象已经被添加到容器

  • 创建一个Properties配置信息类,通过@ConfigurationProperties注解的类会注入配置文件中的配置信息。

通过在类上标注@EnableConfigurationProperties(JdbcProperties.class),这样注解信息类才会生效。
然后可以直接实例化JdbcProperties对象,他的对象属性都应经注入了。

  • 除了使用@ConfigUrationProperties可以注入配置文件的信息外,还可以使用spring的底层注解@Value
  • 静态资源放置在classPath:/META-INF/resources或者classPath:/resources或者classPath:/static或者classPath:/public下。

lombok应用


0

评论 (0)

取消