[TOC]

1.1. 第一章 SpringBoot 简介

1.1.1. 1、什么是SpringBoot

【1】SpringBoot概述

​ SpringBoot是一个快速开发的框架,能过快速整合第三方框架,他是如何快速整合的呢?其实他是的基本原来是Maven依赖关系,Maven的集成,完全采用注解化,简化XML配置,内嵌HTTP服务器(Tomcate,jetty),默认嵌入Tomcate,最终以Java应用程序进行执行。

​ Spring Boot是开发者和Spring 本身框架的中间层,帮助开发者统筹管理应用的配置,提供基于实际开发中常见配置的默认处理(即==约定优于配置==),简化应用的开发,简化应用的运维;总的来说,其目的Spring Boot就是为了对Java web 的开发进行“简化”和加“快”速度,简化开发过程中引入或启动相关Spring 功能的配置。这样带来的好处就是降低开发人员对于框架的关注点,可以把更多的精力放在自己的业务代码上。

同时随着微服务概念的推广和实践,Spring Boot的精简理念又使其成为Java微服务开发的不二之选,也可以说,Spring Boot其实就是为了微服务而生的Java web框架。

【2】SpringBoot核心思想

1、 可独立运行的Spring项目:Spring Boot可以以jar包的形式独立运行。

2、 内嵌的Servlet容器:Spring Boot可以选择内嵌Tomcat、Jetty或者Undertow,无须以war包形式部署项目。

3、 简化的Maven配置:Spring提供推荐的基础 POM 文件来简化Maven 配置。

4、 自动配置Spring:Spring Boot会根据项目依赖来自动配置Spring 框架,极大地减少项目要使用的配置==>javaconfig。

5、 提供生产就绪型功能:提供可以直接在生产环境中使用的功能,如性能指标、应用信息和应用健康检查。

6、 无须配置xml配置:Spring Boot不生成代码。完全不需要任何xml配置即可实现Spring的所有配置。

【3】说说SpringBoot的优点

image-20201023204614373

【3.1】与spring无缝对接

​ 因为SpringBoot是伴随着Spring 4.0而生的,boot是引导的意思,也就是它的作用其实就是在于帮助开发者快速的搭建Spring框架,因此SpringBoot继承了Spring优秀的基因,在Spring中开发更为方便快捷。

image-20201023211921026

【3.2】简化依赖

通过对spring-boot-start-parent配置的引入可以快速的导入依赖

image-20201023211556640

点开spring-boot-dependencies我们可以看见大多数我们需要使用的框架都被声明,在构建线路的时候之需要引入即可

image-20201023212214700

核心模块 模块说明
spring-boot 核心加载
spring-boot-test 支持springboot的单元测试
spring-boot-actuator 监控健康管理应用
spring-boot-loader 允许你构建可用java –jar直接运行的jar包
spring-boot-autoconfigure 自动配置尝试推测用户可能需要的bean
spring-boot-starter-security security权限系统的支持
spring-boot-starter-amqp 对AMQP消息队列的支持
spring-boot-starter-web web项目的支持
spring-boot-starter-aop aop的支持
spring-boot-starter-jdbc jdbc的支持
【3.3】简化配置

原来mybatis的配置支持

image-20201023215239249

使用springboot之后的配置方式

image-20201023215158439

【3.4】简化部署

​ 在使用 Spring 时,项目部署时需要我们在服务器上部署 tomcat,然后把项目打成 war 包扔到 tomcat里,在使用 Spring Boot 后,我们不需要在服务器上去部署 tomcat,因为 Spring Boot 内嵌了 tomcat,我们只需要将项目打成 jar 包,使用 java -jar xxx.jar一键式启动项目。 另外,也降低对运行环境的基本要求,环境变量中有JDK即可

【3.5】简化监控

​ 我们可以引入 spring-boot-start-actuator 依赖,直接使用 REST 方式来获取进程的运行期性能参数,从而达到监控的目的,比较方便。但是 Spring Boot 只是个微框架,没有提供相应的服务发现与注册的配套功能,没有外围监控集成方案,没有外围安全管理方案,所以在微服务架构中,还需要 Spring Cloud 来配合一起使用。

1.1.2. 2、Hello-SpringBoot

【1】快速搭建SpringBoot

这里我们使用maven快速构建springboot想,选择springboot-day01目录,右键spring-boot-quickstart

image-20201027143930992

选择spring Initiallzr,这里JDK使用1.8,初始化的URL直接使用默认即可

image-20201027144037050

指定项目的坐标等相关信息系如下,需要注意的是Java version选择8

image-20201023220519688

选择初始化的一些模块信息,这里我们之选择springweb 相关的信息

image-20201027144713619

然后点击next

image-20201027144816547

点击finish,并把不需要的文件或者文件夹删除

image-20201027145110839

【2】结构项目说明

image-20201027145724577

关于pom文件的相关内容

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <!--这里会自动继承一个父项目他的上层提供了父模块-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.17.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <!--这里定义本项目的坐标-->
    <groupId>com.itheima.springboot</groupId>
    <artifactId>springboot-day01-quickstart</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <name>springboot-day01-quickstart</name>
    <description>Demo project for Spring Boot quickstart</description>

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

    <dependencies>
        <!--定义web模块相关的内容,包含关于springmvc的所有依赖、内部tomcat相关依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

image-20201027150144748

【3】搭建步骤

沿用springboot-day01-quickstart案例

image-20201027152618413

【3.1】目标
使用springboot快速写一个入门案例
【3.2】实现
添加ClientController,使用springmvc中的@RestController和@RequestMapping
使用QuickstartApplication直接启动项目

添加ClientController

package com.itheima.springboot;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @ClassName ClientController.java
 * @Description 测试类
 */
@RestController
@RequestMapping("client")
public class ClientController {

    @RequestMapping
    public String firstMethod(){
        return "Hello SrpingBoot的世界";
    }
}

打开QuickstartApplication

image-20201027152840364

完成启动

image-20201027153015901

访问:http://127.0.0.1:8080/client

image-20201027153105173

【3.3】小结
springboot可以直接支持spring环境
启动方式不需要再使用maven的tomcat插件,而是直接运行main方法调用内置的tomcat

1.1.3. 3、Hello-SpringBoot分析

【1】启动依赖

​ 在spring boot-day01-quickstart的pom.xml中的对spring-boot-starter-parent进行依赖

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

​ spring-boot-starter-parent对spring-boot-dependencies进行依赖

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.1.17.RELEASE</version>
    <relativePath>../../spring-boot-dependencies</relativePath>
  </parent>

spring-boot-starter-dependencies的pom.xml中我们可以发现,一部分坐标的版本、依赖管理、插件管理已经定义好,所以我们的SpringBoot工程继承spring-boot-starter-parent后已经具备版本锁定等配置了。所以起步依赖的作用就是进行依赖的传递。

按住Ctrl点击pom.xml中的spring-boot-starter-web,跳转到了spring-boot-starter-web的pom.xml,xml配置如下(只摘抄了部分重点配置):

<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starters</artifactId>
    <version>2.1.17.RELEASE</version>
  </parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
  <version>2.1.17.RELEASE</version>
  <name>Spring Boot Web Starter</name>
  <description>Starter for building web, including RESTful, applications using Spring
        MVC. Uses Tomcat as the default embedded container</description>
  <url>https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-web</url>
  <organization>
    <name>Pivotal Software, Inc.</name>
    <url>https://spring.io</url>
  </organization>
  <licenses>
    <license>
      <name>Apache License, Version 2.0</name>
      <url>https://www.apache.org/licenses/LICENSE-2.0</url>
    </license>
  </licenses>
  <developers>
    <developer>
      <name>Pivotal</name>
      <email>info@pivotal.io</email>
      <organization>Pivotal Software, Inc.</organization>
      <organizationUrl>https://www.spring.io</organizationUrl>
    </developer>
  </developers>
  <scm>
    <connection>scm:git:git://github.com/spring-projects/spring-boot.git</connection>
    <developerConnection>scm:git:ssh://git@github.com/spring-projects/spring-boot.git</developerConnection>
    <url>https://github.com/spring-projects/spring-boot</url>
  </scm>
  <issueManagement>
    <system>Github</system>
    <url>https://github.com/spring-projects/spring-boot/issues</url>
  </issueManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
      <version>2.1.17.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-json</artifactId>
      <version>2.1.17.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-tomcat</artifactId>
      <version>2.1.17.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.hibernate.validator</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>6.0.20.Final</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>5.1.18.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.1.18.RELEASE</version>
      <scope>compile</scope>
    </dependency>
  </dependencies>
</project>

从上面的spring-boot-starter-web的pom.xml中我们可以发现,spring-boot-starter-web就是将web开发要使用的spring-web、spring-webmvc等坐标进行了“打包”,这样我们的工程只要引入spring-boot-starter-web起步依赖的坐标就可以进行web开发了,同样体现了依赖传递的作用。

【2】启动类

  • @SpringBootApplication:标注SpringBoot的启动类,该注解具备多种功能(后面详细剖析)
  • SpringApplication.run(QuickstartApplication.class, args)代表运行SpringBoot的启动类,参数为SpringBoot启动类的字节码对象
package com.itheima.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

//启动注解
@SpringBootApplication
public class QuickstartApplication {

    //启动的main方法
    public static void main(String[] args) {
        SpringApplication.run(QuickstartApplication.class, args);
    }

}

【3】热加载配置

在开发中反复修改类、页面等资源,每次修改后都是需要重新启动才生效,这样每次启动都很麻烦,浪费了大量的时间,我们可以在修改代码后不重启就能生效,在 pom.xml 中添加如下配置就可以实现这样的功能,我们称之为热部署。

添加热部署支持:

<!--热部署配置-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
</dependency>

注意:IDEA进行SpringBoot热部署失败原因

出现这种情况,并不是热部署配置问题,其根本原因是因为Intellij IEDA默认情况下不会自动编译,需要对IDEA进行自动编译的设置,如下:

image-20201027165421461

如果你要支持运行状态的热部署请按住ctrl+alt+shift+/打开下列窗口进行配置【非常消耗性能请谨慎开启】

image-20201027165600460

image-20201027165730854

1.2. 第二章 SpringBoot 配置文件

1.2.1. 1、配置文件分类

SpringBoot 使用的是一个全局的配置文件,就是上面提到的application.properties配置文件,这个文件的路径放在src/main/resources/目录下面或者放在类路径下面,并且这个文件名是固定的。上面的例子中也可以看到,IDEA工具默认创建的时候创建到对应的第一个目录下面。既然是全局的配置文件,那么就可以对全局的默认配置进行修改。

关于这个配置文件有两种格式

  • application.properties
  • application.yml

主要以Key-Value的形式进行配置,其中属性Key主要分为两种:

  • 默认属性:根据 《默认属性列表》 填写,SpringBoot将自动读取配置和进行初始化。
  • 自定义属性:根据需求任意填写,但需要自行读取配置和进行初始化。

image-20201027222727342

1.2.2. 2、yml语法规则

【2.1】普通数据

语法:

  • key: value

  • 字符串默认不用加上单引号或者双引号。

  • 双引号:会转义字符串,特殊字符会作为本身想要表达的意思

    name:"xiaowang \n list"
    

    输出

    xiaowang
    list
    
  • 单引号:不会转义特殊字符,特殊字符会作为字符串来输出

    name:'xiaowang \n list'
    

    输出

    xiaowang \n list
    

【2.2】对象数据

​ 语法:

​ key:

​ key1: value1

​ key2: value2

​ 或者:

​ key: {key1: value1,key2: value2}

​ 示例:

person:
  name: haohao
  age: 31
  addr: beijing
#或者
person: {name: haohao,age: 31,addr: beijing}

【2.3】集合数据

​ 语法:

​ key:

​ - value1

​ - value2

​ 或者:

​ key: [value1,value2]

​ 示例:

#集合中的元素是字符串
city:
  - beijing
  - tianjin
  - shanghai
  - chongqing

#或者
city: [beijing,tianjin,shanghai,chongqing]

#集合中的元素是对象形式:List<Student>
student:
  - name: zhangsan
    age: 18
    score: 100
  - name: lisi
    age: 28
    score: 88
  - name: wangwu
    age: 38
    score: 90

注意:value1与 - 之间存在一个空格

1.2.3. 3、配置文件加载

【3.1】默认配置

默认属性:根据 《默认属性列表》 填写,SpringBoot将自动读取配置和进行初始化。

修改spring-boot-quickstart中的application.yml来修改tomcat的端口等信息:

server:
  port: 8081
  servlet:
    context-path: /platform
  tomcat:
    uri-encoding: utf-8

​ ==注意==:这里注意一点就是在键和值之间有一个空格,以空格的缩进来控制层级关系。空格的多少没有关系,只要空格对齐就表示属于同一层的元素,只要是左对齐的一列数据,都是同一层级的

启动项目后发现此事的端口和项目路径都发生了变化

image-20201027214608550

【3.2】自定义配置加载

cope项目springboot-day01-quickstart新建项目springboot-day01-yml

image-20201028101736737

【3.2.1】Environment

ClientController中注入Environment对象,使用environment.getProperty("server.port")获得application.yml中的端口

package com.itheima.springboot;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @ClassName ClientController.java
 * @Description 测试类
 */
@RestController
@RequestMapping
public class ClientController {

    @Autowired
    Environment environment;

    /***
     * @description environment环境中获取
     * @return
     * @return: java.lang.String
     */
    @RequestMapping("environment")
    public String environmentMethod(){
        return environment.getProperty("server.port");
    }
}
【3.2.2】@Value

编辑application.yml

server:
  port: 8081
  servlet:
    context-path: /platform
  tomcat:
    uri-encoding: utf-8

#添加基本的k:v格式数据
user:
  user-name: xiaowang
  age: 18

编辑ClientController,使用@Value获取user的属性

package com.itheima.springboot;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @ClassName ClientController.java
 * @Description 测试类
 */
@RestController
@RequestMapping
public class ClientController {

    @Autowired
    Environment environment;

    @Value("${user.user-name}")
    String name;

    @Value("${user.age}")
    Integer age;

    /***
     * @description environment环境中获取
     * @return
     * @return: java.lang.String
     */
    @RequestMapping("environment")
    public String environmentMethod(){
        return environment.getProperty("server.port");
    }

    /***
     * @description value方式获取
     * @return
     * @return: java.lang.String
     */
    @RequestMapping("value")
    public String ValueMethod(){
        return "姓名:"+name+"年纪:"+age;
    }
}

image-20201028103612105

【3.3.3】@ConfigurationProperties

定义ConfigInfo类,用于属性的字段声明

@ConfigurationProperties(prefix = "config-properties")

声明读取application.yml文件中以config-properties开头的节点数据

package com.itheima.springboot;

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

import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * @ClassName ConfigInfo.java
 * @Description ConfigurationProperties方式配置
 */
@ConfigurationProperties(prefix = "config-properties")
public class ConfigInfo {

    String name;

    Integer[] valArray;

    List<String> cityList;

    Map<String,String> userMap;

    List<Map<String,String>> cityListMap;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer[] getValArray() {
        return valArray;
    }

    public void setValArray(Integer[] valArray) {
        this.valArray = valArray;
    }

    public Map<String, String> getUserMap() {
        return userMap;
    }

    public void setUserMap(Map<String, String> userMap) {
        this.userMap = userMap;
    }

    public List<Map<String, String>> getCityListMap() {
        return cityListMap;
    }

    public void setCityListMap(List<Map<String, String>> cityListMap) {
        this.cityListMap = cityListMap;
    }

    public List<String> getCityList() {
        return cityList;
    }

    public void setCityList(List<String> cityList) {
        this.cityList = cityList;
    }

    @Override
    public String toString() {
        return "ConfigInfo{" +
                "name='" + name + '\'' +
                ", valArray=" + Arrays.toString(valArray) +
                ", cityList=" + cityList +
                ", userMap=" + userMap +
                ", cityListMap=" + cityListMap +
                '}';
    }
}

在application.yml中添加属性

server:
  port: 8081
  servlet:
    context-path: /platform
  tomcat:
    uri-encoding: utf-8

#添加基本的k:v格式数据
user:
  user-name: xiaowang
  age: 18

#@ConfigurationProperties方式配置
config-properties:
  name: 配置属性
  val-array: [1,2,3,4]
  city-list:
    - 北京
    - 上海
    - 广州
    - 深圳
  user-map:
    name: 小王
    age: 18
    address: 上海浦东
  city-list-map:
    - type: 超大城市
      name: 北京
    - type: 超大城市
      name: 上海

在pom.xml中添加

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
</dependency>

在ClientController中添加@EnableConfigurationProperties(value = ConfigInfo.class),开启自动配置,装配ConfigInfo的实例化对象,并且交予spring进行管理,在下面直接使用@Autowired注入configInfo对象

package com.itheima.springboot;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @ClassName ClientController.java
 * @Description 测试类
 */
@RestController
@RequestMapping
@EnableConfigurationProperties(value = ConfigInfo.class)
public class ClientController {

    @Autowired
    Environment environment;

    @Value("${user.user-name}")
    String name;

    @Value("${user.age}")
    Integer age;

    @Autowired
    ConfigInfo configInfo;

    /***
     * @description environment环境中获取
     * @return
     * @return: java.lang.String
     */
    @RequestMapping("environment")
    public String environmentMethod(){
        return environment.getProperty("server.port");
    }

    /***
     * @description value方式获取
     * @return
     * @return: java.lang.String
     */
    @RequestMapping("value")
    public String ValueMethod(){
        return "姓名:"+name+"年纪:"+age;
    }

    /***
     * @description ConfigurationProperties方式获取
     * @return
     * @return: java.lang.String
     */
    @RequestMapping("configurationProperties")
    public String ConfigurationPropertiesMethod(){
        return configInfo.toString();
    }
}

image-20201028113009884

小结:

@ConfigurationProperties:装配属性
@EnableConfigurationProperties:开启装配
spring-boot-configuration-processor:去除应用配置类与配置之间的提示警告,同时可以支持原始的xml和properties

【3.3】多环境配置

很多时候,我们项目在开发环境和生成环境的环境配置是不一样的,例如,数据库配置,在开发的时候,我们一般用测试数据库,而在生产环境的时候,我们是用正式的数据,这时候,我们可以利用profile在不同的环境下配置用不同的配置文件或者不同的配置。

Profile 的概念其实很早在 Spring Framework 就有了,在 Spring Framework 3.1 版本引入了注解 @ProfileEnvironment 环境配置的抽象,只是在 Spring Boot 框架里再进一步将 Profiles 功能进行扩展,使它也成为了 Spring Boot 特性之一。

目标

利用 springboot提供的profile机制完成各环境配置的灵活切换和激活

分析

image-20191103100928486

Springboot支持我们把不同环境的配置,配置到不同的配置文件中,但文件名要满足要求

文件名格式:
    application-{profileName}.yml
    application-{profileName}.yaml
以yml为例:
    案例1就可以额外在配置3个配置文件
    // 配置开发环境
    application-dev.yml
    // 配置生产环境
    application-product.yml
激活方式不变:
    在主配置中
    spring:
          profiles:
            active: dev #激活dev配置

修改springboot-day01-yml项目结构如下:

image-20201030183018307

application.yml配置

spring:
  profiles:
    #生效配置
    active: dev

application-dev.yml配置

server:
  port: 8081
  servlet:
    context-path: /platform
  tomcat:
    uri-encoding: utf-8

#添加基本的k:v格式数据
user:
  user-name: xiaowang
  age: 18

#@ConfigurationProperties方式配置
config-properties:
  name: 配置属性
  val-array: [1,2,3,4]
  city-list:
    - 北京
    - 上海
    - 广州
    - 深圳
  user-map:
    name: 小王
    age: 18
    address: 上海浦东
  city-list-map:
    - type: 超大城市
      name: 北京
    - type: 超大城市
      name: 上海

application-product.yml配置

server:
  port: 8080
  servlet:
    context-path: /platform
  tomcat:
    uri-encoding: utf-8

#添加基本的k:v格式数据
user:
  user-name: xiaowang
  age: 18

#@ConfigurationProperties方式配置
config-properties:
  name: 配置属性
  val-array: [1,2,3,4]
  city-list:
    - 北京
    - 上海
    - 广州
    - 深圳
  user-map:
    name: 小王
    age: 18
    address: 上海浦东
  city-list-map:
    - type: 超大城市
      name: 北京
    - type: 超大城市
      name: 上海

当前激活的配置是dev,启动:

image-20201030183351988

修改application.yml

spring:
  profiles:
    #生效配置
    active: product

当前激活的配置是product,启动

image-20201030183505110

我们发现端口变了

1.3. 第三章 SpringBoot 组件集成

前面我们做了springboot的入门,下面的我们来使用springboot做一个基础的应用,新建springboot-day01-basic-project

1.3.1. 1、基础环境搭建

这里我们使用maven快速构建springboot想,选择springboot-day01目录,右键springboot-day01-basic-project

image-20201027143930992

选择spring Initiallzr,这里JDK使用1.8,初始化的URL直接使用默认即可

image-20201027144037050

指定项目的坐标等相关信息系如下,需要注意的是Java version选择8

image-20201028143244459

选择热加载组件,和lombok支持

image-20201028143435089

选择springweb的支持

image-20201028143515192

点击next

image-20201028143750416

点击finish,并把不需要的文件或者文件夹删除

image-20201028144038822

自动生成的启动类名称太长这边修改一下

image-20201028144143876

pom.xml

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <!--springboot的父项目-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.17.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.itheima.springboot</groupId>
    <artifactId>springboot-day01-basic-project</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <name>springboot-day01-basic-project</name>
    <description>basic project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <!--mybatis-plus版本-->
        <mybatis-plus-boot-starter.version>3.2.0</mybatis-plus-boot-starter.version>
        <!--mybatis-plus代码生成器引擎-->
        <mybatis-plus-generator.version>3.3.1.tmp</mybatis-plus-generator.version>
        <!--druid的springboot版本配置-->
        <druid-spring-boot-starter>1.1.20</druid-spring-boot-starter>
        <!--swagger2版本支持-->
        <swagger2>2.9.2</swagger2>
        <!--orika 拷贝工具 -->
        <orika-core.version>1.5.4</orika-core.version>
        <!--lang3-->
        <commons.lang3.version>3.8.1</commons.lang3.version>
        <!--kryo-->
        <kryo.version>4.0.2</kryo.version>
    </properties>

    <dependencies>

        <!--springboot的web支持-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>


        <!--springboot的热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <!--MySQL支持-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!--lombok支持-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!--springboot的测试支持-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <!-- 拷贝对象 -->
        <dependency>
            <groupId>ma.glasnost.orika</groupId>
            <artifactId>orika-core</artifactId>
            <version>${orika-core.version}</version>
        </dependency>

        <!--工具包-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>${commons.lang3.version}</version>
        </dependency>

        <!--swagger2支持-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${swagger2}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>${swagger2}</version>
        </dependency>

        <!--druid的springboot配置-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${druid-spring-boot-starter}</version>
        </dependency>

        <!--springboot关于mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus-boot-starter.version}</version>
        </dependency>

        <!--代码生成器模板引擎 相关依赖-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>${mybatis-plus-generator.version}</version>
        </dependency>

        <!--springboot的freemarker支持-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>

        <!--springboot的cache支持-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>

        <!--springboot的redis支持-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <!--对象序列化-->
        <dependency>
            <groupId>com.esotericsoftware</groupId>
            <artifactId>kryo</artifactId>
            <version>${kryo.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>

        <dependency>
            <groupId>com.itheima.springboot</groupId>
            <artifactId>springboot-day01-defindtion-start</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <!--springboot的打包插件-->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>

            <!-- maven-surefire-plugin 测试包 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.4.2</version>
                <configuration>
                    <!-- 全局是否执行maven生命周期中的测试:是否跳过测试 -->
                    <skipTests>true</skipTests>
                    <!-- 解决测试中文乱码-->
                    <forkMode>once</forkMode>
                    <argLine>-Dfile.encoding=UTF-8</argLine>
                </configuration>
            </plugin>

        </plugins>
    </build>

</project>

在src/main/resources中添加:application.yml文件

image-20201028144920410

添加基础服务配置

#服务配置
server:
  #端口
  port: 8081
  servlet:
    #项目路径
    context-path: /platform
  #服务编码
  tomcat:
    uri-encoding: UTF-8

1.3.2. 2、配置Druid数据源

【1】添加依赖

<druid-spring-boot-starter>1.1.20</druid-spring-boot-starter>

<!--druid的springboot配置-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>${druid-spring-boot-starter}</version>
</dependency>

【2】添加yml配置

#服务配置
server:
  #端口
  port: 8081
  servlet:
    #项目路径
    context-path: /platform
  #服务编码
  tomcat:
    uri-encoding: UTF-8
#spring相关配置
spring:
  #应用配置
  application:
    #应用名称
    name: springboot-day01-basic-project
  #数据源配置
  datasource:
    #选择druid数据源
    druid:
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost:3306/spring-travel?useUnicode=true&characterEncoding=utf8
      username: root
      password: root

注意:这里关于AOP和事务的配置,只要springboot添加了打他source配置就会自动开启

1.3.3. 3、配置Mybatis-Plus框架

【1】添加依赖

<!--mybatis-plus版本-->
<mybatis-plus-boot-starter.version>3.2.0</mybatis-plus-boot-starter.version>
<!--mybatis-plus代码生成器引擎-->
<mybatis-plus-generator.version>3.3.1.tmp</mybatis-plus-generator.version>

<!--springboot关于mybatis-plus-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>${mybatis-plus-boot-starter.version}</version>
</dependency>

<!--代码生成器模板引擎 相关依赖-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>${mybatis-plus-generator.version}</version>
</dependency>

<!--springboot的freemarker支持-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>

【2】添加yml配置

#服务配置
server:
  #端口
  port: 8081
  servlet:
    #项目路径
    context-path: /platform
  #服务编码
  tomcat:
    uri-encoding: UTF-8
#spring相关配置
spring:
  #应用配置
  application:
    #应用名称
    name: springboot-day01-basic-project
  #数据源配置
  datasource:
    #选择druid数据源
    druid:
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost:3306/spring-boot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8
      username: root
      password: root
#mubatis配置
mybatis-plus:
  # MyBaits 别名包扫描路径,通过该属性可以给包中的类注册别名
  type-aliases-package: com.itheima.springboot.pojo
  # 该配置请和 typeAliasesPackage 一起使用,如果配置了该属性,则仅仅会扫描路径下以该类作为父类的域对象 。
  type-aliases-super-type: com.itheima.springboot.basic.BasicPojo
  configuration:
    # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
    log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
    # 驼峰下划线转换
    map-underscore-to-camel-case: true
    use-generated-keys: true
    default-statement-timeout: 60
    default-fetch-size: 100
  global-config:
    db-config:
      #主键类型(雪花ID)
      id-type: assign_id
    #机器 ID 部分(影响雪花ID)
    worker-id: 1
    #数据标识 ID 部分(影响雪花ID)
    datacenter-id: 1
logging:
  config: classpath:logback.xml

【3】代码生成器

image-20201202102006079

mybatis-plus-generrator.properties

#数据库地址
url=jdbc:mysql://127.0.0.1:3306/spring-boot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&tinyInt1isBit=false
#数据库账号
userName=root
#数据库密码
password=root
#此处为本项目src所在路径(代码生成器输出路径)
projectPath=E:/spring-boot-project/springboot-day01/springboot-day01-basic-project
#设置作者
author=Admin
#自定义包路径
parent=com.itheima
#装代码的文件夹名
moduleName=springboot
#设置表前缀,不设置则默认无前缀
tablePrefix =sh_
#数据库表名(此处切不可为空,如果为空,则默认读取数据库的所有表名)
tableName=sh_resource,sh_role,sh_role_resource,sh_user,sh_user_role
#生成的层级
entity=true
entity.ftl.path=/templates/entity.java
mapper=true
mapper.ftl.path=/templates/mapper.java
service=true
service.ftl.path=/templates/service.java
serviceImp=true
serviceImp.ftl.path=/templates/serviceImpl.java
controller=true
controller.ftl.path=/templates/controller.java

【4】分页、回填插件

package com.itheima.springboot.config;

import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.itheima.springboot.handler.MyBatisMetaObjectHandler;
import com.itheima.springboot.utils.SnowflakeIdWorker;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Description:配置文件
 */
@Configuration
@EnableConfigurationProperties(MybatisPlusProperties.class)
public class MyBatisPlusConfig {

    @Autowired
    MybatisPlusProperties mybatisPlusProperties;

    /**
     * @Description 雪花算法工具
     */
    @Bean("snowflakeIdWorker")
    public SnowflakeIdWorker seqGeneratorSharding(){
        return new SnowflakeIdWorker(mybatisPlusProperties.getGlobalConfig().getWorkerId(),
                mybatisPlusProperties.getGlobalConfig().getDatacenterId());
    }

    /**
     * 自动填充
     */
    @Bean
    public MyBatisMetaObjectHandler myMetaObjectHandler() {
        return new MyBatisMetaObjectHandler();
    }

    /**
     * 分页插件
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }
}

1.3.4. 4、配置Swagger2

【1】为什么使用swagger

​ 由于Spring Boot能够快速开发、便捷部署等特性,相信有很大一部分Spring Boot的用户会用来构建RESTful API。而我们构建RESTful API的目的通常都是由于多终端的原因,这些终端会共用很多底层业务逻辑,因此我们会抽象出这样一层来同时服务于多个移动端或者Web前端。

这样一来,我们的RESTful API就有可能要面对多个开发人员或多个开发团队:IOS开发、Android开发或是Web开发等。为了减少与其他团队平时开发期间的频繁沟通成本,传统做法我们会创建一份RESTful API文档来记录所有接口细节,然而这样的做法有以下几个问题:

    由于接口众多,并且细节复杂(需要考虑不同的HTTP请求类型、HTTP头部信息、HTTP请求内容等),高质量地创建这份文档本身就是件非常吃力的事,下游的抱怨声不绝于耳。
    随着时间推移,不断修改接口实现的时候都必须同步修改接口文档,而文档与代码又处于两个不同的媒介,除非有严格的管理机制,不然很容易导致不一致现象。

【2】配置swagger2

【2.1】添加pom依赖
<!--swagger2版本支持-->
<swagger2>2.9.2</swagger2>

<!--swagger2支持-->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>${swagger2}</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>${swagger2}</version>
</dependency>
【2.2】SwaggerConfig
package com.itheima.springboot.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
@ComponentScan("springfox.documentation.swagger.web")
public class SwaggerConfig {

    @Bean
    public Docket createRestApi() {
        // 构建API文档  文档类型为swagger2
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                // 配置 api扫描路径
                .apis(RequestHandlerSelectors.basePackage("com.itheima.springboot"))
                // 指定路径的设置  any代表所有路径
                .paths(PathSelectors.any())
                // api的基本信息
                .build().apiInfo(new ApiInfoBuilder()
                        // api文档名称
                        .title("整合Swagger接口文档")
                        // api文档描述
                        .description("整合Swagger,描述信息......")
                        // api文档版本
                        .version("1.0") // 版本
                        // api作者信息
                        .contact(new Contact("mrchen", "blog.csdn.net", "aaa@gmail.com"))
                        .build());
    }

}

放过静态资源

package com.itheima.springboot.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

/**
 * @ClassName WebMvcConfig.java
 * @Description webMvc高级配置
 */
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {

    /**
     * 资源路径 映射
     */
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        /**
         * 支持webjars
         */
        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
        /**
         * 支持swagger
         */
        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");
        super.addResourceHandlers(registry);
    }
}
【2.3】swagger注解

@Api:

作用在类上,用来标注该类具体实现内容。表示标识这个类是swagger的资源 。 参数: tags:可以使用tags()允许您为操作设置多个标签的属性,而不是使用该属性。

​ description:可描述描述该类作用。

@ApiImplicitParam:

作用在方法上,表示单独的请求参数 参数: name :参数名。 value : 参数的具体意义,作用。 required : 参数是否必填。 dataType :参数的数据类型。 paramType :查询参数类型,这里有几种形式:

类型 作用
path 以地址的形式提交数据
query 直接跟参数完成自动映射赋值
body 以流的形式提交 仅支持POST
header 参数在request headers 里边提交
form 以form表单的形式提交 仅支持POST

在这里我被坑过一次:当我发POST请求的时候,当时接受的整个参数,不论我用body还是query,后台都会报Body Missing错误。这个参数和SpringMvc中的@RequestBody冲突,索性我就去掉了paramType,对接口测试并没有影响。

@ApiImplicitParams:

用于方法,包含多个 @ApiImplicitParam: 例:

@ApiImplicitParams({
    @ApiImplicitParam(name = "id", value = "book's name", required = true, dataType = "Long", paramType = "query"),
    @ApiImplicitParam(name = "date", value = "book's date", required = false, dataType = "string", paramType = "query")})

@ApiModel:

用于类,表示对类进行说明,用于参数用实体类接收;

@ApiModelProperty:

用于方法,字段 ,表示对model属性的说明或者数据操作更改 例:

@ApiModel(value = "User", description = "用户")
public class User implements Serializable{

    private static final long serialVersionUID = 1546481732633762837L;

    /**
     * 用户ID
     */
    @ApiModelProperty(value = "用户ID", required = true)
    @NotEmpty(message = "{id.empty}", groups = {Default.class,New.class,Update.class})
    protected String id;

    /**
     * code/登录帐号
     */
    @ApiModelProperty(value = "code/登录帐号")
    @NotEmpty(message = "{itcode.empty}", groups = {Default.class,New.class,Update.class})
    protected String itcode;

    /**
     * 用户姓名
     */
    @ApiModelProperty(value = "用户姓名")
    @NotEmpty(message = "{name.empty}", groups = {Default.class,New.class,Update.class})
    protected String name;
}

@ApiOperation:

用于方法,表示一个http请求的操作 。

@ApiOperation(value = "获取图书信息", notes = "获取图书信息", response = Book.class, responseContainer = "Item", produces = "application/json")
@ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "book's name", required = true, dataType = "Long", paramType = "query"),
            @ApiImplicitParam(name = "date", value = "book's date", required = false, dataType = "string", paramType = "query")})
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
@ResponseBody
public Book getBook(@PathVariable Long id, String date) {
    return books.get(id);
}

1.3.5. 5、配置SpringCache+Redis缓存【重点】

【1】添加依赖

<!--springboot的cache支持-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

<!--springboot的redis支持-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!--对象序列化-->
<dependency>
    <groupId>com.esotericsoftware</groupId>
    <artifactId>kryo</artifactId>
    <version>${kryo.version}</version>
</dependency>

【2】添加yml配置

#服务配置
server:
  #端口
  port: 8081
  servlet:
    #项目路径
    context-path: /platform
  #服务编码
  tomcat:
    uri-encoding: UTF-8
#spring相关配置
spring:
  #应用配置
  application:
    #应用名称
    name: springboot-day01-basic-project
  #数据源配置
  datasource:
    #选择druid数据源
    druid:
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost:3306/spring-boot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8
      username: root
      password: root
  redis:
    host: 127.0.0.1 #Redis服务器地址
    port: 7379 #Redis服务器连接端口
    timeout: 2000 #请求redis服务的超时时间,这里注意设置成0时取默认时间2000
    jedis: #阻塞的
      pool:
        #连接池最大连接数(使用负值表示没有限制)
        #建议为业务期望QPS/一个连接的QPS,例如50000/1000=50
        #一次命令时间(borrow|return resource+Jedis执行命令+网络延迟)的平均耗时约为1ms,一个连接的QPS大约是1000
        max-active: 50
        #连接池中的最大空闲连接
        #建议和最大连接数一致,这样做的好处是连接数从不减少,从而避免了连接池伸缩产生的性能开销。
        max-idle: 50
        #连接池中的最小空闲连接
        #建议为0,在无请求的状况下从不创建链接
        min-idle: 0
        #连接池最大阻塞等待时间 毫秒(-1表示没有限制)
        #建议不要为-1,连接池占满后无法获取连接时将在该时间内阻塞等待,超时后将抛出异常。
        max-wait: 2000
#mubatis配置
mybatis-plus:
  # MyBaits 别名包扫描路径,通过该属性可以给包中的类注册别名
  type-aliases-package: com.itheima.springboot.pojo
  # 该配置请和 typeAliasesPackage 一起使用,如果配置了该属性,则仅仅会扫描路径下以该类作为父类的域对象 。
  type-aliases-super-type: com.itheima.springboot.basic.BasicPojo
  configuration:
    # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
    log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
    # 驼峰下划线转换
    map-underscore-to-camel-case: true
    use-generated-keys: true
    default-statement-timeout: 60
    default-fetch-size: 100
  global-config:
    db-config:
      #主键类型(雪花ID)
      id-type: assign_id
    #机器 ID 部分(影响雪花ID)
    worker-id: 1
    #数据标识 ID 部分(影响雪花ID)
    datacenter-id: 1
logging:
  config: classpath:logback.xml

【3】RedisCacheConfig配置

package com.itheima.springboot.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.itheima.springboot.serializer.KryoRedisSerializer;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.*;

import java.time.Duration;

/**
 * @ClassName RedisCacheConfig.java
 * @Description redis配置
 */
@Configuration
@EnableCaching
public class RedisCacheConfig {


    /**
     * 申明缓存管理器,会创建一个切面(aspect)并触发Spring缓存注解的切点(pointcut)
     * 根据类或者方法所使用的注解以及缓存的状态,这个切面会从缓存中获取数据,将数据添加到缓存之中或者从缓存中移除某个值

     * @return
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        // KryoRedisSerializer 替换默认序列化
//        KryoRedisSerializer kryoRedisSerializer = new KryoRedisSerializer(Object.class);

        // 配置序列化(解决乱码的问题)
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(60))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(genericJackson2JsonRedisSerializer))
                .disableCachingNullValues();

        return RedisCacheManager.builder(redisConnectionFactory)
                .cacheDefaults(config)
                .build();
    }
}

【4】注解详解

对于缓存声明,spring的缓存提供了一组java注解:

  • @Cacheable:触发缓存写入。
  • @CacheEvict:触发缓存清除。
  • @CachePut:更新缓存(不会影响到方法的运行)。
  • @Caching:重新组合要应用于方法的多个缓存操作
  • @CacheConfig:设置类级别上共享的一些常见缓存设置【了解】
【4.1】@Cacheable注解

​ 如果缓存中没有:查询数据库,存储缓存,返回结果,如果缓存中有:直接返回结果

​ 作用:可以用来进行缓存的写入,将结果存储在缓存中,以便于在后续调用的时候可以直接返回缓存中的值,而不必再执行实际的方法。 最简单的使用方式,注解名称=缓存名称,使用例子如下:

@Cacheable

【4.2】@CacheEvict注解

@CacheEvict:删除缓存的注解,这对删除旧的数据和无用的数据是非常有用的。这里还多了一个参数(allEntries),设置allEntries=true时,可以对整个条目进行批量删除

@CacheEvict

【4.3】@CachePut注解

@CachePut:当需要更新缓存而不干扰方法的运行时 ,可以使用该注解。也就是说,始终执行该方法,并将结果放入缓存

@CachePut

【4.4】@Caching注释

在使用缓存的时候,有可能会同时进行更新和删除,会出现同时使用多个注解的情况.而@Caching可以实现

//添加user缓存的同时,移除userPage的缓存
@Caching(put =@CachePut(value = "user",key ="#userVo.id"),
        evict = @CacheEvict(value = "userPage",allEntries = true))
【4.5】@CacheConfig注解

缓存提供了许多的注解选项,但是有一些公用的操作,我们可以使用@CacheConfig在类上进行全局设置。 以下是个简单的例子

@CacheConfig("books") 
public class BookRepositoryImpl implements BookRepository {

    @Cacheable
    public Book findBook(ISBN isbn) {...}
}

【5】使用示例

UserController

package com.itheima.springboot.web;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.springboot.basic.ResponseWrap;
import com.itheima.springboot.enums.StatusEnum;
import com.itheima.springboot.exception.ProjectException;
import com.itheima.springboot.pojo.User;
import com.itheima.springboot.service.IUserService;
import com.itheima.springboot.utils.BeanConv;
import com.itheima.springboot.utils.EmptyUtil;
import com.itheima.springboot.utils.ExceptionsUtil;
import com.itheima.springboot.vo.UserVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.web.bind.annotation.*;

import java.util.Date;

/**
 * @Description:用户表 前端控制器
 */
@RestController
@RequestMapping("/springboot/user")
@Api(tags = "用户操作")
@Log4j2
@CrossOrigin
public class UserController {

    @Autowired
    IUserService userService;

    /***
     * @description 注册用户
     * @param userVo 注册信息
     * @return: java.lang.Boolean
     */
    @PostMapping
    @ApiOperation(value = "用户注册",notes = "用户注册")
    @ApiImplicitParam(name = "userVo",value = "注册信息",required = true,dataType = "UserVo")
    @Caching(put =@CachePut(value = "user",key ="#result.data.id"),evict = @CacheEvict(value = "userPage",allEntries = true))
    public ResponseWrap<UserVo> saveUser(@RequestBody UserVo userVo) throws ProjectException {
        try {
            User user = BeanConv.toBean(userVo, User.class);
            userService.save(user);
            BeanConv.toBean(user, userVo);
            return ResponseWrap.<UserVo>builder()
                    .code(StatusEnum.SUCCEED.getCode())
                    .msg(StatusEnum.SUCCEED.getMsg())
                    .operationTime(new Date())
                    .data(userVo)
                    .build();
        }catch (Exception e){
            log.error("用户注册:{}", ExceptionsUtil.getStackTraceAsString(e));
            throw new ProjectException(StatusEnum.REGISTER_USER_FAIL.getCode(),
                    StatusEnum.REGISTER_USER_FAIL.getMsg());
        }
    }

    /***
     * @description 删除用户
     * @param userId 用户Id
     * @return: java.lang.Boolean
     */
    @DeleteMapping("{userId}")
    @ApiOperation(value = "删除用户",notes = "删除用户")
    @ApiImplicitParam(name = "userId",value = "删除用户",required = true,dataType = "Long")
    @Caching(evict = {@CacheEvict(value = "user",key ="#userId"),@CacheEvict(value = "userPage",allEntries = true)})
    public ResponseWrap<Boolean> deleteUser(@PathVariable("userId") Long userId) throws ProjectException {
        try {
            Boolean flag = userService.removeById(userId);
            return ResponseWrap.<Boolean>builder()
                    .code(StatusEnum.SUCCEED.getCode())
                    .msg(StatusEnum.SUCCEED.getMsg())
                    .operationTime(new Date())
                    .data(flag)
                    .build();
        }catch (Exception e){
            log.error("删除用户:{}", ExceptionsUtil.getStackTraceAsString(e));
            throw new ProjectException(StatusEnum.DELETE_USER_FAIL.getCode(),
                    StatusEnum.DELETE_USER_FAIL.getMsg());
        }
    }

    /***
     * @description 编辑用户
     * @param userVo 注册信息
     * @return: java.lang.Boolean
     */
    @PutMapping
    @ApiOperation(value = "编辑用户",notes = "编辑用户")
    @ApiImplicitParam(name = "userVo",value = "编辑用户",required = true,dataType = "UserVo")
    @Caching(put =@CachePut(value = "user",key ="#userVo.id"),evict = @CacheEvict(value = "userPage",allEntries = true))
    public ResponseWrap<Boolean> updateUser(@RequestBody UserVo userVo) throws ProjectException {
        try {
            Boolean flag = userService.saveOrUpdate(BeanConv.toBean(userVo, User.class));
            return ResponseWrap.<Boolean>builder()
                    .code(StatusEnum.SUCCEED.getCode())
                    .msg(StatusEnum.SUCCEED.getMsg())
                    .operationTime(new Date())
                    .data(flag)
                    .build();
        }catch (Exception e){
            log.error("查询用户:{}", ExceptionsUtil.getStackTraceAsString(e));
            throw new ProjectException(StatusEnum.UPDATE_USER_FAIL.getCode(),
                    StatusEnum.UPDATE_USER_FAIL.getMsg());
        }
    }

    /***
     * @description 查询用户
     * @param userId 用户Id
     * @return: java.lang.Boolean
     */
    @GetMapping("/{userId}")
    @ApiOperation(value = "查询用户",notes = "查询用户")
    @ApiImplicitParam(name = "userId",value = "查询用户",required = true,dataType = "Long")
    @Cacheable(value = "user",key ="#userId")
    public ResponseWrap<UserVo> findUserById(@PathVariable("userId") Long userId) throws ProjectException {
        try {
            User user = userService.getById(userId);
            return ResponseWrap.<UserVo>builder()
                    .code(StatusEnum.SUCCEED.getCode())
                    .msg(StatusEnum.SUCCEED.getMsg())
                    .operationTime(new Date())
                    .data(BeanConv.toBean(user,UserVo.class))
                    .build();
        }catch (Exception e){
            log.error("查询用户:{}", ExceptionsUtil.getStackTraceAsString(e));
            throw new ProjectException(StatusEnum.FIND_USER_FAIL.getCode(),
                    StatusEnum.FIND_USER_FAIL.getMsg());
        }
    }

    /***
     * @description 分页查询用户
     * @param current 当前页面
     * @param size 每页条数
     * @return: java.lang.Boolean
     */
    @PostMapping("{current}/{size}")
    @ApiOperation(value = "分页查询用户",notes = "查询用户")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "current",value = "当前页码",required = true,dataType = "Long"),
            @ApiImplicitParam(name = "size",value = "每页条数",required = true,dataType = "Long"),
            @ApiImplicitParam(name = "userVo",value = "用户条件",required = true,dataType = "UserVo")
    })
    @Cacheable(value = "userPage",key ="'current_'+#current+'_userVo_'+#userVo.hashCode()")
    public ResponseWrap<Page<UserVo>> findUserPage(@PathVariable("current") Long current,
                                                   @PathVariable("size") Long size,
                                                   @RequestBody UserVo userVo) throws ProjectException {
        try {
            //构建分页
            Page<User> page = new Page<>(current,size);
            //查询条件
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            if (!EmptyUtil.isNullOrEmpty(userVo.getLoginName())) {
                queryWrapper.likeRight(StringUtils.camelToUnderline(User.Fields.loginName),userVo.getLoginName());
            }
            if (!EmptyUtil.isNullOrEmpty(userVo.getMobil())) {
                queryWrapper.likeRight(StringUtils.camelToUnderline(User.Fields.mobil),userVo.getMobil());
            }
            if (!EmptyUtil.isNullOrEmpty(userVo.getEnableFlag())) {
                queryWrapper.eq(StringUtils.camelToUnderline(User.Fields.enableFlag),userVo.getEnableFlag());
            }
            //执行查询
            Page<User> userPageResult = userService.page(page, queryWrapper);
            Page<UserVo> userVoPageResult = new Page<>();
            BeanConv.toBean(userPageResult,userVoPageResult);
            userVoPageResult.setRecords(BeanConv.toBeanList(userPageResult.getRecords(),UserVo.class));
            return ResponseWrap.<Page<UserVo>>builder()
                    .code(StatusEnum.SUCCEED.getCode())
                    .msg(StatusEnum.SUCCEED.getMsg())
                    .operationTime(new Date())
                    .data(userVoPageResult)
                    .build();
        }catch (Exception e){
            log.error("用户注册:{}", ExceptionsUtil.getStackTraceAsString(e));
            throw new ProjectException(StatusEnum.FIND_USER_FAIL.getCode(),
                    StatusEnum.FIND_USER_FAIL.getMsg());
        }
    }

}

1.3.6. 6、配置SpringBoot测试

【1】springboot测试配置

<!--springboot的测试支持-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

注解

注解 说明
@RunWith(SpringRunner.class) 替换底层运行期
@SpringBootTest 使用springboot启动项目并且添加测试

==注意:如果springboot的版本为2.2.X以上版本则可以不添加@RunWith(SpringRunner.class)==

1.4. 第四章 SpringBoot 源码分析

1.4.1. 1、自动装配源码

​ 众所周知spring-boot入门容易精通难,说到底spring-boot是对spring已有的各种技术的整合封装,因为封装了所以使用简单,也因为封装了所以越来越多的"拿来主义"者们不愿意去关注其具体实现!为了更好的使用spring-boot所以必要的源码探索是非常有必要的!今天开始探索的第一步:自动装配原理-----------------(此处默认各位看官熟悉spring的各种基础注解)

要谈自动装配我们需要从项目的初始注解入手:@SpringBootApplication

package com.itheima.springboot;

import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

//自动装配的开始
@SpringBootApplication
@MapperScan("com.itheima.springboot.mapper")
@Slf4j
public class BasicProjectApplication {

    public static void main(String[] args) {
        SpringApplication.run(BasicProjectApplication.class, args);
    }

}

那下面我们观察一下SpringBootApplication

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//继承@Configuration,创建配置生效
@SpringBootConfiguration
//开启自动装配
@EnableAutoConfiguration
//开启扫描机制,但是不包含自动配置的filter
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
.....}

从上面的类中我们发现:@EnableAutoConfiguration是开启核心配置的注解,下面我们观察@EnableAutoConfiguration

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//确保了将项目目录下所有的bean注入到容器
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    //使用类的字节码排除要装配的类
    Class<?>[] exclude() default {};

    //使用类的名称排除要装配的类
    String[] excludeName() default {};
}

在这个注解下我们需要关注两个注解:@AutoConfigurationPackage、@Import({AutoConfigurationImportSelector.class})

【A】@AutoConfigurationPackage

我们先观察@AutoConfigurationPackage,我们发现它有导入@Import(AutoConfigurationPackages.Registrar.class)

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

}

实质上,它负责保存标注相关注解的类的所在包路径。使用一个BasePackage类,保存这个路径。然后使用@Import注解将其注入到ioc容器中。这样,可以在容器中拿到该路径

点击AutoConfigurationPackages

image-20210105220934118

image-20210105221038303

点击Register,我们发现它是AutoConfigurationPackages类的静态类

image-20201030132742840

红框代码为指定@ComponentScan的扫描路径,此处打断点启动项目我们可以发现这个为项目的顶级包名

image-20201030130124845

注意:这里不是自动装配的核心,我们暂时不继续跟踪。

【B】@Import({AutoConfigurationImportSelector.class})

接下来我们看看引入的AutoConfigurationImportSelector【这里才是自动装配的灵魂】

点击AutoConfigurationImportSelector,进去代码查看getCandidateConfigurations(annotationMetadata, attributes)

image-20201030132426820

点击this.getCandidateConfigurations(annotationMetadata, attributes)方法

image-20201030132550151

上面还看不出什么,继续点loadSpringFactories

image-20201030133004691

我们可以发现spring-boot会去META-INF/spring.factories找对应的配置类信息

image-20201030133307329

image-20201030133438988

这里我以AOP的自动装配为例子

image-20201030133734812

上面的图中可以看到META-INF/spring.factories的AopAutoConfiguration,然后直接开启@EnableAspectJAutoProxy,后面的内容就是spring容器进行完成。

1.4.2. 2、启动加载源码

【1】启动源码

程序启动入口

@SpringBootApplication
@MapperScan("com.itheima.springboot.mapper")
@Slf4j
public class BasicProjectApplication {

    //项目的启动入口
    public static void main(String[] args) {
        SpringApplication.run(BasicProjectApplication.class, args);
    }

}

run:是一个静态方法,会调用创建SpringApplication实例并run

image-20201030135021078

先看new SpringApplication(primarySources)构建的实例,点击SpringApplication

image-20201030135836647

点击this进入下图中方法

image-20201030135653943

SpringApplication的构造函数主要设置了一些基本参数并配置source、判断了是否web应用、创建初始化构造器、创建应用监听器、找出main方法所在的类。

点击:getSpringFactoriesInstances为获取spring工厂中的实例,具体代码块为

image-20201030140433551

new SpringApplication(primarySources)初始化完成后,执行run方法:

image-20201030140657279

image-20201030141628721

上面代码执行完成后springboot就启动完成

【2】初始化Spring-IOC

那么springboot到底是怎么构建IOC容器的呢?我们接着上面的启动流程看:

image-20201030142909748

查看上图中的refreshContext(context)的方法

image-20201030143043530

继续点击refreshContext

image-20201030143152839

继续点击refreshContext

image-20201030143347705

最终完成bean的声明

image-20201030143549363

【3】初始化Spring-MVC

【3.1】前端控制器加载

首先找到spring.factories文件中的前端控制器配置DispatcherServletAutoConfiguration

image-20201030144625696

查看DispatcherServletAutoConfiguration

  • @ConditionalOnClass(DispatcherServlet.class):主要负责前段控制器
  • @AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class):加载内部的tomcat


image-20201030145212976

查看ServletWebServerFactoryAutoConfiguration,里面有关于tomcat的相关配置类的导入

image-20201030145558182

进行观察DispatcherServletAutoConfiguration

image-20201030150527304

image-20201030150638429

【3.2】WebMvc自动配置

首先找到spring.factories文件中的前端控制器配置WebMvcAutoConfiguration

image-20201030151039693

这里我们可以看见关于springmvc的所有配置

image-20201030151226989

1.4.3. 3、启动及自动装配全图

大家可以右键打开此图然后在图画中查看更加清晰

555177-20191219195

【3.1】自动装配流程

SpringBoot为我们提供了各种框架的默认配置,而默认配置生效的步骤如下:

​ 1、系统启动加载@SpringBootApplication然后加载@EnableAutoConfiguration

​ 2、@EnableAutoConfiguration是开启核心配置的注解,注解上@Import({AutoConfigurationImportSelector.class})

​ 3、AutoConfigurationImportSelector中getCandidateConfigurations(annotationMetadata, attributes)加载所有配置

​ 4、getCandidateConfigurations中使用loadSpringFactories

​ 5、loadSpringFactories会去META-INF/spring.factories中已配置好的信息

因此,使用SpringBoot自动配置的关键有两点:

1)启动器starter

要想自动配置生效,只需要引入依赖即可,而依赖版本我们也不用操心,因为只要引入了SpringBoot提供的stater(启动器),就会自动管理依赖及版本了。

因此,玩SpringBoot的第一件事情,就是找starter,SpringBoot提供了大量的默认starter

2)全局配置yml文件

另外,SpringBoot的默认配置,都会读取默认属性,而这些属性可以通过自定义application.yml文件来进行覆盖。这样虽然使用的还是默认配置,但是配置中的值改成了我们自定义的。

因此,玩SpringBoot的第二件事情,就是通过application.yaml来覆盖默认属性值,形成自定义配置。

【3.2】启动流程

SpringBoot项目启动第一步就是创建SpringApplication的实例,并且调用SpringApplication.run()这个方法。

创建SpringApplication实例主要完成三件事情:

  • 记录当前启动类字节码
  • 判断当前项目类型,普通Servlet、响应式WebFlux、NONE
  • 加载/META-INF/spring.factories文件,初始化ApplicationContextInitializer和ApplicationListener实例

而后的run()方法则会创建spring容器,流程如下:

  • 准备监听器,监听Spring启动的各个过程

  • 创建并配置环境参数Environment

  • 创建ApplicationContext

    prepareContext():初始化ApplicationContext,准备运行环境

  • refreshContext(context):准备Bean工厂,调用一个BeanDefinition和BeanFactory的后处理器,初始化各种Bean,初始化tomcat

  • afterRefresh():拓展功能,目前为空

  • 发布容器初始化完毕的事件

1.5. 第五章 自定义自动装配

1.5.1. 1、注解理解

如果你想自定义注解请先对于下列注解需要有一定的认识

【1】配置及属性读取注解

注解 描述
@Configuration 声明一个配置类
@Bean 声明方法返回对象为一个bean
@ConfigurationProperties(prefix = "config-properties") 声明读取yml文件一config-properties开头的属性
@EnableConfigurationProperties(value = ConfigInfo.class) 声明ConfigInfo配置属性为配置类,且交予spring管理

关键点:@ConfigurationProperties注解主要用来把properties配置文件转化为bean来使用的,而@EnableConfigurationProperties注解的作用是@ConfigurationProperties注解生效。如果只配置@ConfigurationProperties注解,在IOC容器中是获取不到properties配置文件转化的bean的。

【2】条件注解

核心条件注解

注解 描述
@ConditionalOnBean 应用中包含指定bean,配置才生效
@ConditionalOnMissingBean 应用中不包含指定bean,配置才生效
@ConditionalOnClass 应用中包含指定类,配置才生效
@ConditionalOnMissingClass 应用中不包含指定类,配置才生效
@ConditionalOnWebApplication 配置只有在web环境下配置才生效
@ConditionalOnNotWebApplication 配置只有在非web环境下配置才生效
@Conditional 条件判定

【3】核心条件顺序注解

注解 描述
@AutoConfigureAfter 在指定配置类加载之后加载
@AutoConfigureBefore 在指定配置类加载之前加载
@AutoConfigureOrder 指定加载配置优先级,默认0

下面我们以AopAutoConfiguration的自动配置为例进行说明,找到spring.factories中的DispatcherServletAutoConfiguration

image-20201030144625696

image-20201030155706954

1.5.2. 2、自定义SpringBoot-start

【1】自定义start的意义

​ 在我们的日常开发工作中,经常会有一些独立于业务之外的配置模块,我们经常将其放到一个特定的包下,然后如果另一个工程需要复用这块功能的时候,需要将代码硬拷贝到另一个工程,重新集成一遍,麻烦至极。如果我们将这些可独立于业务代码之外的功配置模块封装成一个个starter,复用的时候只需要将其在pom中引用依赖即可,SpringBoot为我们完成自动装配,简直不要太爽。

统一功能的抽取
只需要简单的配置就可以完成bean的构建

【2】案例演示

【2.1】目标
使用spring的条件注解
自定义oss的start,被依赖后通过配置自动依赖
【2.2】实现步骤
【2.2.1】新建项目

新建springboot-day01-defindtion-start项目如下:

image-20201203151055227

【2.2.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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <!--这里会自动继承一个父项目他的上层提供了总舵的模块-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.17.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <!--这里定义本项目的坐标-->
    <groupId>com.itheima.springboot</groupId>
    <artifactId>springboot-day01-defindtion-start</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <name>springboot-day01-defindtion-start</name>

    <properties>
        <!--oss-->
        <aliyun-sdk-oss.version>3.8.0</aliyun-sdk-oss.version>
        <!--guava版本 -->
        <guava.version>20.0</guava.version>
    </properties>

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

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
        </dependency>

        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>${aliyun-sdk-oss.version}</version>
        </dependency>

        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>${guava.version}</version>
        </dependency>

    </dependencies>

    <build>
    </build>

</project>
【2.2.3】编写自动装配

OssAliyunConfigProperties类,定义需要自动加载的OSS属性配置

package com.itheima.springboot.config;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.springframework.boot.context.properties.ConfigurationProperties;

import java.io.Serializable;

/**
 * @Description 阿里云OSS上传配置类
 */

@Setter
@Getter
@NoArgsConstructor
@ToString
@ConfigurationProperties(prefix = "itheima.framework.oss")
public class OssAliyunConfigProperties implements Serializable {

    /**
     * 域名站点
     */
    private String endpoint ;

    /**
     * 秘钥Id
     */
    private String accessKeyId ;

    /**
     * 秘钥
     */
    private String accessKeySecret ;

    /**
     * 桶名称
     */
    private String bucketName ;

    /**
     * 设置OSSClient允许打开的最大HTTP连接数,默认为1024个
     */
    private Integer maxConnections ;

    /**
     * 设置Socket层传输数据的超时时间,默认为50000毫秒
     */
    private Integer socketTimeout;

    /**
     * 设置建立连接的超时时间,默认为50000毫秒
     */
    private Integer connectionTimeout;

    /**
     * 设置连接空闲超时时间。超时则关闭连接,默认为60000毫秒。
     */
    private Integer connectionRequestTimeout ;

    /**
     * 设置连接空闲超时时间。超时则关闭连接,默认为60000毫秒。
     */
    private Integer idleConnectionTime ;

    /**
     * 设置失败请求重试次数,默认为3次。
     */
    private Integer maxErrorRetry ;

    /**
     * 设置用户代理,指HTTP的User-Agent头,默认为aliyun-sdk-java。
     */
    private String userAgent ;

    /**
     * 代理端口
     */
    private String proxyHost;

    /**
     * 代理账号
     */
    private String proxyUsername;

    /**
     * 代理密码
     */
    private String proxyPassword;

    /**
     * 白名单
     */
    private String whiteList;


}

OssAliyunAutoConfig

package com.itheima.springboot;

/**
 * @ClassName UserService.java
 * @Description 用户业务
 */
public class UserService {

    private User user;
/*
 * <b>文件名</b>:OssAliyunConfig.java
 *
 * 文件描述:
 *
 *
 * 2018年12月18日  上午10:53:44
 */

package com.itheima.springboot.config;

import com.aliyun.oss.ClientConfiguration;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.common.comm.Protocol;
import com.aliyun.oss.model.CannedAccessControlList;
import com.aliyun.oss.model.CreateBucketRequest;
import com.aliyun.oss.model.SetBucketLoggingRequest;
import com.itheima.springboot.service.FileStorageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/***
 * @description Oss文件存储服务器
 * @return
 */
@Slf4j
@Configuration
@EnableConfigurationProperties({OssAliyunConfigProperties.class})
//当引入FileStorageService接口时
@ConditionalOnClass(FileStorageService.class)
public class OssAliyunAutoConfig {

    @Autowired
    OssAliyunConfigProperties ossAliyunConfigField;


    @Bean
    @ConditionalOnMissingBean(FileStorageService.class)
    public ClientConfiguration clientConfiguration() {
        // 创建ClientConfiguration。ClientConfiguration是OSSClient的配置类,可配置代理、连接超时、最大连接数等参数。
        ClientConfiguration conf = new ClientConfiguration();
        // 设置OSSClient允许打开的最大HTTP连接数,默认为1024个。
        conf.setMaxConnections(ossAliyunConfigField.getMaxConnections());
        // 设置Socket层传输数据的超时时间,默认为50000毫秒。
        conf.setSocketTimeout(ossAliyunConfigField.getSocketTimeout());
        // 设置建立连接的超时时间,默认为50000毫秒。
        conf.setConnectionTimeout(ossAliyunConfigField.getConnectionTimeout());
        // 设置从连接池中获取连接的超时时间(单位:毫秒),默认不超时。
        conf.setConnectionRequestTimeout(ossAliyunConfigField.getConnectionRequestTimeout());
        // 设置连接空闲超时时间。超时则关闭连接,默认为60000毫秒。
        conf.setIdleConnectionTime(ossAliyunConfigField.getIdleConnectionTime());
        // 设置失败请求重试次数,默认为3次。
        conf.setMaxErrorRetry(ossAliyunConfigField.getMaxErrorRetry());
        // 设置是否开启二级域名的访问方式,默认不开启。
        conf.setSLDEnabled(false);
        // 设置连接OSS所使用的协议(HTTP/HTTPS),默认为HTTP。
        conf.setProtocol(Protocol.HTTP);
        // 设置用户代理,指HTTP的User-Agent头,默认为aliyun-sdk-java。
        conf.setUserAgent(ossAliyunConfigField.getUserAgent());
//        // 设置是否支持将自定义域名作为Endpoint,默认支持。
//        conf.setSupportCname(false);
//        // 设置代理服务器端口。
//        conf.setProxyHost(ossAliyunConfigField.getProxyHost());
//        // 设置代理服务器验证的用户名。
//        conf.setProxyUsername(ossAliyunConfigField.getProxyUsername());
//        // 设置代理服务器验证的密码。
//        conf.setProxyPassword(ossAliyunConfigField.getProxyPassword());
        return conf;
    }

    @Bean
    @ConditionalOnBean(ClientConfiguration.class)
    public OSSClient ossClient(){
        log.info("-----------------开始创建OSSClient--------------------");
        OSSClient ossClient = new OSSClient(ossAliyunConfigField.getEndpoint(), ossAliyunConfigField.getAccessKeyId(), ossAliyunConfigField.getAccessKeySecret(), clientConfiguration());
        //判断容器是否存在,不存在就创建
        if (!ossClient.doesBucketExist(ossAliyunConfigField.getBucketName())) {
            ossClient.createBucket(ossAliyunConfigField.getBucketName());
            CreateBucketRequest createBucketRequest = new CreateBucketRequest(ossAliyunConfigField.getBucketName());
            //设置问公共可读
            createBucketRequest.setCannedACL(CannedAccessControlList.PublicRead);
            ossClient.createBucket(createBucketRequest);
        }

//        //添加访问白名单
//        List<String> refererList = Arrays.asList(whiteList.split(","));
//        // 添加Referer白名单。Referer参数支持通配符星号(*)和问号(?)。
//        // 设置存储空间Referer列表。设为true表示Referer字段允许为空。
//        BucketReferer br = new BucketReferer(true, refererList);
//        ossClient.setBucketReferer(ossAliyunConfigField.getBucketName(), br);

        //添加客户端访问日志
        SetBucketLoggingRequest request = new SetBucketLoggingRequest(ossAliyunConfigField.getBucketName());
        // 设置存放日志文件的存储空间。
        request.setTargetBucket(ossAliyunConfigField.getBucketName());
        // 设置日志文件存放的目录。
        request.setTargetPrefix(ossAliyunConfigField.getBucketName());
        ossClient.setBucketLogging(request);

        log.info("-----------------结束创建OSSClient--------------------");
        return ossClient;
    }


}

    public UserService(User user) {
        this.user = user;
    }

    public UserService() {
    }

    public String sayHello(){
        return user.toString();
    }
}
【2.2.4】定义spring.factories

image-20201203152825796

注意:这里的文件及文件夹都是自己建立的

spring.factories的内容,当jar被引用的时候,OSSAliyunFileStorageService将会被加载

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    com.itheima.springboot.service.impl.OSSAliyunFileStorageService
【2.2.5】验证自动装配

在springboot-day01-basic-project项目的pom文件中依赖

<dependency>
    <groupId>com.itheima.springboot</groupId>
    <artifactId>springboot-day01-defindtion-start</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</dependency>

image-20201203152935998

把springboot-day01-defindtion-start在maven中执行install,然后执行springboot-day01-basic-project的maven中执行install

执行测试类

image-20201203153117808

【2.3】小结
依赖:
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-autoconfigure</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
    </dependency>
编写依赖条件:
    @EnableConfigurationProperties({OssAliyunConfigProperties.class})
    //当引入FileStorageService接口时
    @ConditionalOnClass(FileStorageService.class)
定义spring.factories
        org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    com.itheima.springboot.service.impl.OSSAliyunFileStorageService

results matching ""

    No results matching ""