Spring Boot 完整講解


SpringBoot學習筆記

文章寫得比較詳細,所以很長(105336 字數),可以參考目錄

文章目錄

一、 Spring Boot 入門

Spring Boot 官方文檔

預:必須掌握的技術:

  • Spring 框架的使用經驗
  • 熟練使用Maven進行項目構建和依賴管理
  • 熟練使用IDEA或Eclipse

1. Spring Boot 簡介

背景

Spring Boot 來簡化Spring 應用的開發,約定大於配置,去繁從簡,明顯提高開發效率

解決的問題

  1. Spring全家桶時代
  2. Spring Boot ——》J2EE一站式解決方案
  3. Spring Cloud -> 分布式整體解決方案(Spring技術棧)

優點

  • 快速創建獨立運行的Spring項目以及與主流框架集成
  • 使用嵌入式Servlet容器,應用無需打成WAR包,直接使用Java -jar jar包名即可運行
  • starters自動依賴與版本控制
  • 大量的自動配置,簡化開發,也可以修改默認值
  • 無需配置XML,無需代碼生成,開箱即用
  • 准生產環境的運行時應用監控
  • 與雲計算的天然集成

缺點

入門快,精通很難

Spring Boot是對Spring 框架的再封裝,若對Spring不了解,對SpringBoot的封裝機制也不會很了解,Spring Boot中許多自動配置,需要我們了解Spring的API(只有在了解Spring的API后才能更精通Spring Boot

2.微服務

martin fowler詳細介紹了微服務Martin Fowler:microservices

What are Microservices?

微服務是一種架構風格

提倡在開發應用時,一個應用應該是一組小型服務;可以通過HTTP的方式進行互通

單體應用

  1. All In One ,
  2. 是傳統的架構,
  3. 優點:開發 、部署、運維要簡單,
  4. 缺點:牽一發動全身,不適合大型應用
  5. 在多個服務器上復制這個單體進行擴展

微服務

  1. 一個微服務架構把每個功能元素放進一個獨立的服務中
  2. 通過跨域服務器分發這些服務進行擴展,只在需要時進行擴展🏃
  3. 每一個功能元素都是一個可以替換和獨立升級的軟件單元
  4. 詳細參照微服務文檔

3.環境准備

  • Spring Boot推薦使用jdk的版本:1.7及以上
  • maven:3.3及以上版本
  • intellij idea
  • Spring Boot

Maven設置:

給maven的settings.xml配置文件的profiles標簽添加

表明使用jdk1.8進行開發

<profile>
	<id>jdk-1.8</id>
	<activation>
	<activeByDefault>true/ activeByDefault>
	<jdk>1.8</jdk>
</activation>
	<properties>
		<maven. compiler .source>1。8</maven.compiler.source>
		<maven. compi ler. target>1.8</maven.compller.target>
		<maven. compiler. compilerverslon>1.8</maven.compiler.compllerversion>
	</propert1es>
</profile>

Idea設置

進入設置頁面 Ctrl+Alt+S

Build,Execution,Deployment–>Build Tools–>Maven

設置maven為本地的maven, 庫也為本地maven庫

4.Spring Boot HelloWorld

完成一個功能:Hello,比較簡單,不記了

5.Hello World 的探究

1)pom文件

a.父項目

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

他的父項目是

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

其中有各種依賴的版本,Spring Boot 通過此父項目真正管理Spring Boot里面的所有依賴版本(Spring Boot的依賴仲裁中心)

以后導入依賴不需要版本,(沒有depenencies中依賴管理的需要自己聲明版本號)

b.導入的依賴

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

spring-boot-stater-web

spring-boot-stater:Spring Boot 場景啟動器:幫我們導入了web模塊正常運行的組件

Spring Boot將所有的功能都抽取出來,做成一個個的staters(啟動器),只需要在項目中引入這些啟動器,相關的依賴就能自動導入

2)主程序類,主入口類

@SpringBootApplication
public class CaitApplication {

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

}

@SpringBootApplication 核心注解,標注在某個類上說明這個類是Spring Boot的主配置類,Spring Boot 一個運行這個類的main方法來啟動應用:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
@ConfigurationPropertiesScan
public @interface SpringBootApplication {
  • @SpringBootConfiguration Spring Boot 的配置類;

    • 標注在某個類上,表示這是一個Spring Boot 的配置類
    • @Configuration 配置類上來標注這個注解;(Spring4.X)
      • 配置類————配置文件;
  • @EnableAutoConfiguration 開啟自動配置功能;

    • 以前我們需要配置的東西,Spring Boot 幫我們自動配置;

    • @EnableAutoConfiguration告訴SpringBoot開啟自動配置功能;這樣自動配置才能生效

      • @AutoConfigurationPackage : 自動配置包
      • @**Import({AutoConfigurationImportSelector.class})**Spring的底層注解,給容器中導入一個組件;導入的組件有AutoConfigurationImportSelector.class

      更多知識在 Spring注解版

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {

接下來從底層理解:

我們進入

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

@Import既然是SpringBoot 的底層注解,那Import的是什么?我們查看Registar的內容,發現這個是一個靜態類

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
        Registrar() {
        }

        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
        }

        public Set<Object> determineImports(AnnotationMetadata metadata) {
            return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
        }
    }

在這里插入圖片描述

其中,這個方法registerBeanDefinitions將bean注冊到容器中,我們想知道注冊的到底是什么,對這行代碼進行調試,

在這里插入圖片描述

點擊運行Spring Boot,

在這里插入圖片描述

通過調試信息我們知道,這個注解源頭在CaitApplication(Spring Boot 啟動類)

我們選擇getPackName()方法,鼠標右鍵選擇計算表達

在這里插入圖片描述
在這里插入圖片描述
**

結果證明:導入的包為com.cait.cait,也就是啟動類所在的包中所有的bean。這表明,

@AutoConfigurationPackage的實質是注冊啟動類所在包中所有標記的類為Bean

(將主配置類所在包下以及所有子包中所有的組件掃描到Spring容器)

那么問題來了EnableAutoConfiguration中也有一個Import,這個Import又是什么作用

@Import()給容器中導入括號內組件

點進AutoConfigurationImportSelector.class

由於本人使用的Spring Boot 2.X,與1略有不同,下方為1的老師講解

EnableAutoConfigurationImportSelector:導入哪些組件的選擇器🚗

選擇debug模式,查看

在這里插入圖片描述

通過名字了解到是很多自動配置類(XXXAutoConfiguration):就是給容器中導入這個場景需要的所有組件並配置好組件,使用不同的功能對應不同的配置文件

通過配置類,免去了我們手動編寫配置注入功能組件的工作!!!

SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,classLoader);

Spring Boot 在啟動的時候從類路徑下META-INF/spring.factories中獲取EnableAutoConfiguration指定的值,將這些值作為自動配置類導入到容器中,自動配置類就生效,幫我們進行自動配置工作以前我們需要自己配置的配置類,自動配置類都幫我們完成。J2EE的整體解決方案和自動配置都在spring-boot-autoconfigure-XXXX.jar中

6.使用Spring Initializer快速創建Spring Boot 項目

IDE都支持Spring Boot 的快速創建項目

SpringBoot學習第一步:搭建基礎

IDEA對SpringBoot的項目支持可以說是點擊就能完成基礎的搭建,方便的不得了,

流程如下

1.左上角File選項,New project,選擇Spring Initializr

在這里插入圖片描述

2.設置項目信息,Group 會自動創建Group文件夾,包含項目的代碼;Artifact 的名字必須使用小寫與下划線構成!!

在這里插入圖片描述

3.選擇web項目,右上角可以選擇SpringBoot 的版本,一定要使用relese版本(正式版),不要使用SNAPSHOT版本
在這里插入圖片描述

4.確定project的名字與位置,名字就是項目文件夾的名字

在這里插入圖片描述

點擊finish就好啦,一個SpringBoot+Maven項目就搞定了,最后創建三個基礎包,上車愉快!
在這里插入圖片描述

默認生成的Spring Boot 項目:

  • 主程序已經生成,我們只需要我們自己的邏輯
  • resouces文件夾中目錄結構
    • static:保存所有的靜態資源:js,css,images;
    • templates:保存所有的模板頁面:(Spring Boot 默認jar包嵌入式的Tomcat,默認不支持Jsp頁面);可以使用模板引擎(freemarker、thymeleaf);
    • application.properties:Spring Boot 應用默認配置文件
    • https://blog.csdn.net/weixin_44494373/article/details/102779187

二、Spring Boot 配置

CSDN博客

1. 配置文件

Spring Boot 默認使用兩種配置文件

  • application.properties
  • application.yml

配置文件的作用:修改Spring Boot自動配置的默認值;Spring Boot在底層自動配置好

YAML(YAML Ain’t Markup Language )語言的文本,

  • YAML A Markup Language :是一個標記語言
  • YAML isn’t Markup Language : 不是一個標記語言(XML類型標記文件)

標記語言:

  • 以前的標記語言大多數使用的是 XXXX.xml
  • YAML :以數據為中心,比json、xml等更適合做配置文件

2. YAML語法

1)基本語法

K:(空格)Value 表示一對鍵值對(空格必備)

以**空格**的縮進來控制層級關系;只要是左對齊的一列數據,都是同一個層級的

server:
	port: 8081
	path: /hello

屬性與值也是大小寫敏感的

2)值的寫法

字面量:普通的值(數字,字符串,布爾)

key: value (中間有空格)字面直接來寫;

  • 字符串默認不用加上單引號和雙引號
    • “”:雙引號不會轉義字符串里面的特殊字符;特殊字符會作為本身想表達的意思
      • example: “zhangsan \n list”:輸出:zhangshan (換行) list
    • ‘’:單引號 會轉義特殊字符,特殊字符最終只是一個普通的字符串數據
      • example: “zhangsan \n list”:輸出:zhangshan \n list

對象、Map(屬性和值)(鍵值對)

key: value

​ 對象還是key: value的方式

example: create a object with propertities : lastName age

friends:
	lastName: zhangshan
	age: 22

行內寫法:用一行表示

friends: {lastName: zhangshan , age: 18}

數組(List、Set):

用-值表示數組中的一個元素

pets:
	-cat
	-dog
	-pig

行內寫法

pets: [cat,dog,pig]

附:Spring Boot單元測試

可以在測試期間很方便的類似編碼一樣進行自動注入容器的功能

package com.cait.cait;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class CaitApplicationTests {

    @Autowired
    要進行測試的類
    @Test
    void contextLoads() {
        進行操作
    }

}


3. 通過配置文件注入

1) 數據綁定到基礎數據類型

@Value

String LastName;

是Spring 底層的一個注解,

對應的xml

<bean class="person">
	<property name="LastName" value="字面量/${key}/#{SpEL}"></property>
</bean>

2) 將數據綁定到類中 含配置文件與類綁定的方法

注: prefix必須全部為小寫,配置的類名不能使用大寫!!!!!,如 subMit是錯誤的!!

首先 類 與 配置文件要對應,在類前注釋 @ConfigurationProperties(prefix=“pets”)

  • @ConfigurationProperties 告訴SpringBoot將本類中的所有屬性和配置文件中相關屬性進行綁定;

  • perfix=“pets”:配置文件中,該前綴下面的所有屬性,進行一一映射

  • 若報錯,點擊提示,官方說明應該添加依賴

  • <dependency>
        <groupId>org.springframework.boot</groupId>   
        <artifactId>spring-boot-configuration-processor</artifactId> 
        <optional>true</optional>
    </dependency>
    
  • 只有這個組件是容器中的組件,才能使用容器提供的@ConfigurationProperties功能

  • 添加@Component注解加入容器組件

總結:導入配置文件處理器,以后進行配置文件操作就有提示,類中屬性的名要對應,類名可以不同,加入注解@ConfigurationProperties(prefix = “屬性的前綴”)

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

配置文件少用中文!!!

若輸出為亂碼,原因是idea使用的是utf8

打開Setting,搜索file encoding(文件編碼),選擇UTF8,同時選擇需要在運行時轉譯為ascii

  • Transparent native to ascii conversion

3) @Value 獲取值和 @ConfigurationProperties獲取值的比較

類型 @ConfigurationProperties @Value
功能 批量注入配置文件中的屬性 一個個指定
松散綁定(松散語法) supported unsupported,要嚴格對應名
SpEL unsupported supported
JSR303 supported unsupported
復雜類型封裝 supported unsupported, only simple type is permitted

配置文件yml於properties他們都能獲取到值

  • 如果說,我們只是在某個業務邏輯中需要獲取一下文件中的某項值,使用@Value
  • 如果單獨寫了一個JavaBean來匹配配置文件中的值,使用@ConfigurationProperties

4) 配置文件注入值數據校驗

5) @PropertySource & @ImportResource

  • @PropertySource:加載指定的配置文件(非默認的application.yml)
  • 參數Value指文件,encoding指編碼,也很重要!!!

注:@PropertySource默認只能支持properties文件!!!!,

解決方案:SpringBoot配置文件@PropertySource 同時支持properties文件與yaml(yml)

4)Spring配置文件注入

a. @ImportResource: 導入Spring配置文件,讓配置文件里面的內容生效;

想讓Spring的配置文件生效,加載進來;將@ImportResouce標注在一個類上

@ImportResource(locations= {"classpath:bean.xml"})

在這里插入圖片描述

導入Spring配置文件:beans.xml並使其生效

b. Spring Boot 推薦給容器添加組件的方式

  • 配置類=======Spring 配置文件

  • 使用@Bean添加

  • package com.jirath.springboot_learn.config;
    
    import com.jirath.springboot_learn.service.HelloService;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    /** * '@Configuratoin' point out that current object is a Configuration Class,which used to replace the Spring Configuration file before. */
    @Configuration
    public class MyConfig {
        /** * add current function's returned value to the ContextContainer, * the id of the module in this ContextContainer is the name of function; * @return the object which used to build a Bean */
        @Bean
        public HelloService helloService(){
            return new HelloService();
        }
    }
    
    

4.配置文件占位符

RandomValuePropertySource: 配置文件中可以使用隨機數

r a n d o m . v a l u e {random.value}、 {random.int}、 r a n d o m . l o n g {random.long}、 {random.int(10)}、 r a n d o m . i n t [ 1024 , 65536 ] {random.int[1024,65536]}、 {random.uuid}

屬性配置占位符

app.name=MyApp
app.description=${app.name} is a Spring Boot application
  • 可以在配置文件中引用前面配置過的屬性(優先級前面配置過的這里都能用)
  • ${app.name:defultValue}來指定找不到屬性時的默認值
  • 在這里插入圖片描述
  • 若引用的值不存在,SpringBoot會默認將{}中間的值作為value
  • 用${person.name:(defult)}可以設置默認為defult

5.Profile

在開發過程中會遇到開發和實際生產時項目的配置是不同的情況,應對這種情況,Spring設置了Profile,

Profile是Spring對不同環境提供不同配置功能的支持,可以通過激活、指定參數等方式快速切換環境

1)多Profile文件

我們在主配置文件編寫的時候,文件名可以是 application-(profile).properties/yml

!!!注意,文件的profile必須在三個字符以內,否自無法作為配置文件!!

默認使用application.properties的配置:

2)yml支持多文檔塊方式

spring:
	profiles:
		active: dev
---
server:
	port: 8081
spring:
	profiles: dev
	
---
server:
	port: 8081
spring:
	profiles: prod

3)激活指定Profile

  •  spring.profiles.active=dev
    
  • 命令行:

  • –spring.profile.active=dev

  • idea測試方法

  • 在這里插入圖片描述

  • 打包:

    • 打開maven選項,Lifecycle->package
    • 在這里插入圖片描述
  • 虛擬機方法:

    • -Dspring-profiles.active=dev
    • 在這里插入圖片描述

6.配置文件的加載位置

spring boot 啟動會掃描以下位置的application.properties / yml 文件作為Spring Boot的默認配置文件

  • file:./config
  • file:./
  • 以上兩種是在當前項目路徑下,即與src同級
  • classpath:/config/
  • classpath:/
  • 以上是按照優先級從高到低的順序,所有位置的文件都會被加載,
  • 高級優先配置內容會覆蓋低級優先配置相同的內容,同時滿足配置互補
  • 我們也可以通過配置spring.config.location來改變默認配置
    • 項目打包好了以后,我們可以使用命令行參數的形式,啟動項目的時候來指定配置文件的位置;指定配置文件和默認加載的這些配置文件共同起作用,形成互補配置
    • image-20191109135006909
    • 即可以在外部改變配置

7.Spring Boot 外部配置加載順序

Spring Boot也可以從以下位置加載配置;優先級從高到低

Spring Boot官方文檔

優先加載帶Profile,jar包外部的

  1. 命令行參數
  2. 來自java:comp/env的NDI屬性
  3. Java系統屬性(System.getProperties())
  4. 操作系統環境變量
  5. RandomValuePropertySource配置的random.*屬性值
  6. jar包外部的application-{profile}.properties或application.yml(帶spring.profile)配置文件
  7. jar包內部的application-{profile}.properties或application.yml(帶spring.profile)配置文件
  8. jar包外部的application.properties或application.yml(不帶spring.profile)配置文件
  9. jar包內部的application.properties或application.yml(不帶spring.profile)配置文件
  10. @Configuration注解類上的@PropertySource
  11. 通過SpringApplication.setDefaultProperties指定的默認屬性

8.Spring Boot 自動配置原理

配置文件能寫些什么?怎么寫?自動配置的原理?

Spring Boot官方文檔關於配置文件

自動配置原理:

  1. Spring Boot啟動的時候,加載主配置類,開啟了自動配置功能@EnableAutoConfiguration,

  2. @EnableAutoConfiguration的作用:

    • 利用EnableAutoConfigurationImportSelector給容器中導入一些組件

    • 可以查詢selectImports()方法的內容;

    • Listconfigurations=getCandidateConfigurations(annotation Metadata,attributes);獲取候選的配置

      • protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
                List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
                Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
                return configurations;
            }
        
      • public final class SpringFactoriesLoader {
            public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
            private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
            private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap();
        
            private SpringFactoriesLoader() {
            }
        
      • 掃描所有jar包類路徑下 META-INF/spring.factories

      • 把掃描到的這些文件的內容包裝成properties對象

      • 從properties中獲取到EnableAutoCongratulation.class類(類名)對應的值,然后把他們添加在容器中

將類路徑下 META-INF/spring.factories 里面配置的所有EnableAutoCongratulation的值加入到容器當中

位置:maven:spring-boot-autocongratulation->spring-boot-autoconfiguare-2X->META-INF->spring.factories

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveRestClientAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.rest.RestClientAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\
org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,\
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketRequesterAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketServerAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketStrategiesAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.rsocket.RSocketSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.saml2.Saml2RelyingPartyAutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration,\
org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration

  • 每一個這樣的XXXAutoConfrigulation類都是容器的一個組件,都加入到容器中;用他們來做自動配置;

  • 每一個自動配置類進行自動配置功能;

  • 以**HttpEncodingAutoConfiguration(Http編碼自動配置)**為例解釋自動配置原理;

  • @Configuration(
        proxyBeanMethods = false
    )//表示這是一個配置類,與之前編寫配置文件一樣,也可以給容器中添加組件
    @EnableConfigurationProperties({HttpProperties.class})//啟用ConfigurationProperties功能:將配置文件中對應的值和HttpEncodingProperties綁定起來;並把 HttpProperties 加入到Spring的ioc容器中來
    @ConditionalOnWebApplication(
        type = Type.SERVLET
    )//考慮webapp,Spring底層@Conditional注解,根據不同的條件,如果滿足指定的條件,整個配置類就會生效; 判斷當前應用是否是web應用,如果是,當前配置類才生效
    @ConditionalOnClass({CharacterEncodingFilter.class})//判斷當前項目有沒有這個類 CharacterEncodingFilter :SpringMVC進行亂碼解決的過濾器
    @ConditionalOnProperty(
        prefix = "spring.http.encoding",
        value = {"enabled"},
        matchIfMissing = true
    )//判斷配置文件中是否存在某個配置spring.http.encoding.enabled ; 如果不存在判斷也是成立的,即使配置文件中不配值,也是默認生效的;
    public class HttpEncodingAutoConfiguration {
        /** *他已經和SpringBoot的配置文件映射了 * **/
        private final Encoding properties;
    
        //只有一個有參構造器的情況下,參數的值就會從容器中拿
        public HttpEncodingAutoConfiguration(HttpProperties properties) {
            this.properties = properties.getEncoding();
        }
    
        @Bean//給容器中添加組件,這個組件的某些值需要從properties中獲取
        @ConditionalOnMissingBean
        public CharacterEncodingFilter characterEncodingFilter() {
            CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
            filter.setEncoding(this.properties.getCharset().name());//獲取編碼
            filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST));
            filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE));
            return filter;
        }
    }
    
    • 根據當前不同條件判斷,決定這個配置類是否生效?
    • SpringBoot1.X中屬性在HttpEncodingProperties中可以查看,2.X中無注釋
    • 一旦這個配置類生效;這個配置就會給容器中添加各種組件;這些組件的屬性是從對應的properties中獲取的,這些類中的每一個屬性又是和配置文件綁定的
  • 所有配置文件中能配置的屬性都是在xxxxProperties類中封裝着

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.boot.autoconfigure.http;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(
    prefix = "spring.http"
)//從配置文件中獲取指定的值和bean的屬性進行綁定
public class HttpProperties {
    private boolean logRequestDetails;
    private final HttpProperties.Encoding encoding = new HttpProperties.Encoding();

   //...................
}

```
  • #我們能配置的屬性都來源於這個功能的properties類
    spring.http.encoding.enabled=true
    #字符集
    spring.http.encoding.charset=utf-8
    #強制編碼 請求響應必須為UTF8
    spring.http.encoding.force=true
    

精髓

  1. SpringBoot啟動會加載大量自動配置類
  2. 我們看我們需要的功能有沒有SpringBoot默認寫好的自動配置類;
  3. 我們再來看這個自動配置類中到底配置了哪些組件;
    • 只要我們要用的組件存在,我們就不需要配置
  4. 給容器中自動配置類添加組件的時候,會從properties類中獲取某些屬性,我們就可以在配置文件中指定這些屬性的值

xxxxxAutoConfiguration:自動配置類;

給容器中添加組件

xxxxproperties:封裝配置文件中相關屬性;

for example:

  1. 全局搜索相關自動配置類在這里插入圖片描述
  2. 點擊查看XXXXProperties,類中屬性即為可以配置的屬性在這里插入圖片描述

3.自動配置原理(細節)

1)@Conditional派生注解(Spring注解版原生的@Conditional作用)

作用:必須是@Conditional指定的條件成立,才給容器中添加組件,配置配里面的內容才生效

@Conitional擴展注解 作用(判斷是否滿足當前指定條件)
@ConditionalOnJava 系統的Java版本是否符合要求
@ConditionalOnBean 容器中存在指定Bean
@ConditionalOnMissingBean 容器中不存在指定Bean
@ConditionalOnExpression 滿足SpEl表達式指定
@ConditionalOnClass 系統中有指定的類
@ConditionalOnMissingClass 系統中沒有指定的類
@ConditionalOnSingleCandidate 容器中只有一個指定的Bean,或者這個Bean是首選Bean
@ConditionalOnProperty 系統中指定的屬性是否有指定的值
@ConditionalOnResource 類路徑下是否存在指定資源文件
@ConditionalOnWebApplication 當前是web環境
@ConditionalOnNotWebApplication 當前不是web環境
@ConditionalOnJndi JNDI存在指定項

自動配置類必須在一定條件下生效🌶

我們怎么知道那些自動配置類生效?

在配置文件中編寫

#開啟SpringBoot的debug
debug=true

我們可以通過啟用debug=true屬性;來讓控制台打印自動生成報告,這樣我們就可以很方便的知道哪些自動配置類生效

Positive matches: 自動配置類啟用的

Negative matches: 未匹配到

三、Spring Boot 與日志

1、日志框架

小張在開發一個大型系統;

  1. System.out.println("");將關鍵信息打印在控制台;老板希望去掉;又想要用
  2. 做成一個框架記錄系統運行時的信息;
  3. 高大上的幾個功能:
    • 異步模式
    • 自動歸檔
    • XXXX
  4. 卸載老框架,換新框架,重新修改相關API;(很麻煩)
  5. JDBC—數據庫驅動啟發;
    • 寫了一個接口層:日志門面(日志的一個抽象層);logging-abstract.jar
    • 給項目中導入具體的日志實現就行了;我們之前的日志框架都是實現的抽象層

市面上的日志框架

JUL , JCL , Jboss-logging , logback , log4j , log4j2 , slf4j

日志門面(日志抽象層) 日志實現
JCL(Jakarta Commons Logging)、SLF4j(Simple Logging Facae For Java)、Jboss-logging log4j、JUL(java.util.logging)、log4j2、logback

左邊選一個門面(抽象層)、右邊來選一個實現

  • Jboss 太復雜 JCL 最后一次更新在2014年
  • SLF4j log4j logback出自同一人
  • Log4j2 Apache公司的全新日志框架

日志門面:SLF4J;

日志實現:Logback > log4j

SpringBoot :底層是Spring框架,Spring框架默認使用是JCL

SpringBoot 選用SLF4j和logback

2、SLF4j使用

SLF4j官網

1)如何在系統中使用SLF4j

以后在開發的時候,日志記錄方法的調用,不應該來直接調用日志的實現類,二傻調用日志抽象層里面的方法;

給系統里面導入slf4j的jar和logback的實現jar

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HelloWorld {
  public static void main(String[] args) {
    Logger logger = LoggerFactory.getLogger(HelloWorld.class);
    logger.info("Hello World");
  }
}

slf4j使用的情況

在這里插入圖片描述

SELF4j關聯了logback日志框架后的正確使用情況
在這里插入圖片描述

調用SELF4j的接口,SELF4j調用底層日志的jar包

使用log4j時,系統會使用適配層在這里插入圖片描述

通過適配層將兩者關聯

2)遺留問題

for example:a情況(slf4j+logback):Spring (commons-logging)、Hibernate( jBOSS-logging)、Mybatis…

統一日志記錄,即使 是別的框架,和我一起統一使用slf4j進行輸出

介紹

在這里插入圖片描述

  1. 將系統中其他日志框架先排出去
  2. 用中間包替換所有的日志框架
  3. 導入slf4j其他的實現

​ ----------------------SpringBoot就是這么實現的

3)SpringBoot日志關系

使用idea創建,選擇web,默認攜帶self4j+logback

在pom文件中,選擇導入的依賴,右鍵,選擇Diagrams->Show Dependencies,idea就可以用樹狀圖的方式展現依賴之間的關系,按住alt即可放大鏡查看在這里插入圖片描述

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

SpringBoot 使用他來做日志功能

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-logging</artifactId>
      <version>2.2.0.RELEASE</version>
      <scope>compile</scope>
    </dependency>

在這里插入圖片描述

在這里插入圖片描述

總結:

  1. 使用logback記錄日志
  2. 把其他日志轉為slf4j
  3. 導入抽象層
  4. 中間替換包(偷梁換柱)
  5. 如果我們要引入其他框架,一定要把這個框架的默認日志依賴移除
    1. 新版本的Spring不再使用commons-logging,所以新的Spring Boot中沒有去除該框架

SpringBoot能自動適配所有的日志,而且底層使用的是slf4j+logback的方式記錄日志,引入其他框架的時候,只需要把這個框架依賴的日志框架排除掉

3.日志使用

1)默認配置

SpringBoot 默認幫我們配置好了日志;

 //記錄器
    org.slf4j.Logger logger= org.slf4j.LoggerFactory.getLogger(getClass());

    @Test
    void contextLoads() {
        //日志的級別
        //由低到高 trace debug info warn error
        //可以調整需要輸出的日志級別,日志就只會在這個級別的更高級別生效
        logger.trace("這是trance日志");
        logger.debug("這是debug日志");
        //SpringBoot 默認使用的是info級別,沒有指定級別的就用SpringBoot默認規定的級別:root級別(info)
        logger.info("這是info日志");
        logger.warn("這是warn日志");
        logger.error("這是error日志");
    }

SpringBoot修改日志默認配置

path和file都存在,file為主

logging.level.com.jirath=trace
#在當前磁盤下新建Spring和log文件夾:使用spring.log作為默認文件
logging.file.path=/spring/log
#不指定路徑就在當前項目下生成日志
#logging.file.name=springboot.log
#可以指定完整路徑
logging.file.name=E:/springboot.log
#在控制台輸出的日志格式
logging.pattern.console================%n%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n
#指定文件中日志輸出格式
logging.pattern.file=

在這里插入圖片描述

SpringBoot 關於日志的其他默認設置在哪里?

位於

spring-boot-2.2.0.RELEASE.jar!/org/springframework/boot/logging/logback/中

打開即為SpringBoot針對LogBack的配置封裝。

for example:在這里插入圖片描述

其中,SpringBoot將默認級別(root)設置為了info

在配置文件中配置的屬性會被一個名為LoggingApplicationListener(單擊配置文件配置項即可查看)相似的類接收

LoggingApplicationListener同時與一個LoggingSystemProperties(在logback桶目錄下)的類對應在這里獲取輸入的值,進行解析

在base.xml中SpringBoot針對控制台與文件輸出有分別的定義,如圖

在這里插入圖片描述

在文件默認配置中,SpringBoot同時設置了文件在最大數值,當超出數值,日志就會自動分文件記錄,如:log1,log2在這里插入圖片描述

2)指定配置

在實際使用時,同時會遇到其他功能的需求,自動歸檔等等等等,

要想使用自己的配置文件,Spring.io中在特性下有一章節專門講述了SpringBoot配置默認日志的方法:在Spring目錄中放logback.xml即可

給類路徑下放上每個框架自己的配置文件即可:SpringBoot就不再使用默認配置在這里插入圖片描述

Spring Boot includes a number of extensions to Logback that can help with advanced configuration. You can use these extensions in your logback-spring.xml configuration file.

logback.xml:直接被日志框架識別

logback-spring.xml:日志就不直接加載日志配置項,由SpringBoot解析日志配置,就可以使用SpringBoot的高級Profile功能

<springProfile name="dev">
	<!--可以指定某段配置只在某個環境下生效-->
</springProfile>

3)切換日志框架

例子:使用log4j

  1. 打開依賴關系圖,選擇logback,exclude,將logback排除
  2. 去除log4j-over-slf4j(將log4j替換為slf4j的包)
  3. 導入slf4j-log4j12(無需寫版本),會自動的導入log4j
  4. 運行

但是,此行為無意義,log4j因為表現不理想,原作開發了logback;

使用其他日志框架:

  1. 排除stater-logging
  2. 引入stater-log4j2

四、SpringBoot與Web開發

Thymeleaf、web定制、容器定制

1、使用SpringBoot

  1. 創建SpringBoot應用,選中我們需要的模塊;
  2. SpringBoot已經默認將這些場景布置好,只需要在配置文件中指定少量配置就可以運行起來;
  3. 自己編寫業務代碼;

自動配置原理

這個場景SpringBoot幫我們配置了什么?能不能修改?能不能擴展?…

  1. XXXXAutoConfiguration:幫我們給容器中自動配置組件
  2. 在Spring-boot-autoconfigure中找到web模塊
  3. 自動配置類在其中
  4. XXXProperties:配置類來封裝配置文件的內容

2、SpringBoot對靜態頁面的映射規則

在WebMvcAutoConfiguration中

訪問靜態資源

第一種 導入的webjars

public void addResourceHandlers(ResourceHandlerRegistry registry) {
            if (!this.resourceProperties.isAddMappings()) {
                logger.debug("Default resource handling disabled");
            } else {
                Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
                CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
                if (!registry.hasMappingForPattern("/webjars/**")) {
                    this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{"/webjars/**"}).addResourceLocations(new String[]{"classpath:/META-INF/resources/webjars/"}).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
                }

                String staticPathPattern = this.mvcProperties.getStaticPathPattern();
                if (!registry.hasMappingForPattern(staticPathPattern)) {
                    this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{staticPathPattern}).addResourceLocations(WebMvcAutoConfiguration.getResourceLocations(this.resourceProperties.getStaticLocations())).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
                }

            }
        }

  1. 由上方代碼,所有/webjars/**,下的請求都會去classpath:/META-INF/resources/webjars/中尋找資源

    • webjars:以jar包的方式引入靜態資源;
    • webJars
    • 將常用的前端框架給了一個Maven依賴的方式
    • 選好要使用的版本,在pom文件中引入即可
    • 引入后在jar包中,設置中顯示隱藏文件夾即可打開,符合映射規則
  2. 代碼中同時設置了緩存時間,緩存時間可以在resourceProperties中設置

    • Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
      
    • @ConfigurationProperties(
          prefix = "spring.resources",
          ignoreUnknownFields = false
      )
      public class ResourceProperties {
      //可以設置與靜態資源有關的參數,緩存時間
      

第二種"/**"訪問當前項目的任何資源

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述

第二種規則:"/**"訪問當前項目的任何資源,

 String staticPathPattern = this.mvcProperties.getStaticPathPattern();
                if (!registry.hasMappingForPattern(staticPathPattern)) {
                    this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{staticPathPattern}).addResourceLocations(WebMvcAutoConfiguration.getResourceLocations(this.resourceProperties.getStaticLocations())).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
                }

進入getResoutceLocations

static String[] getResourceLocations(String[] staticLocations) {
        String[] locations = new String[staticLocations.length + SERVLET_LOCATIONS.length];
        System.arraycopy(staticLocations, 0, locations, 0, staticLocations.length);
        System.arraycopy(SERVLET_LOCATIONS, 0, locations, staticLocations.length, SERVLET_LOCATIONS.length);
        return locations;
    }

查看代碼是從其他方法中引入了參數(與1版本不同)

返回查看參數的來源

getStaticLocations
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"};

得總結

"/**"訪問當前項目的任何資源(靜態資源文件夾)都會在下方的文件夾中找內容

  • “classpath:/META-INF/resources/”,
  • “classpath:/resources/”,非默認的resources,而是新建的resources文件夾
  • “classpath:/static/”
  • “classpath:/public/”,(默認無,需要新建)
  • “/”:當前項目的路徑

歡迎頁配置

@Bean
        public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
            WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext), applicationContext, this.getWelcomePage(), this.mvcProperties.getStaticPathPattern());
            welcomePageHandlerMapping.setInterceptors(this.getInterceptors(mvcConversionService, mvcResourceUrlProvider));
            return welcomePageHandlerMapping;
        }
private Optional<Resource> getWelcomePage() {
            String[] locations = WebMvcAutoConfiguration.getResourceLocations(this.resourceProperties.getStaticLocations());
            return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst();
        }
private Resource getIndexHtml(String location) {
            return this.resourceLoader.getResource(location + "index.html");
        }

靜態資源文件夾下所有index.html頁面:被"/**"映射

圖標

所有的**/favicon.ico都是在靜態資源環境下找;

同時靜態資源的位置是可以改變的

spring.resources.static-location=位置

3、模板引擎

之前多使用jsp頁面,可以很方便的嵌入數據等,但SpringBoot 使用嵌入式的tomcat,不支持jsp頁面,只能支持HTML,

JSP、Velocity、Freemarker、Thymeleaf;

模板引擎的作用:在寫頁面時,將一些數據與渲染數據的模板結合輸出,JSP與其他的模板引擎都是這一原理

SpringBoot推薦的Thymeleaf;

語法更簡單,功能更強大;

引入thymeleaf

-   ````xml
    <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-thymeleaf</artifactId>
            </dependency>
    ````

-   詳情見SpringBoot官網文檔

-   布局功能的支持程序 thymeleaf3主程序 layout2以上版本

Thymeleaf使用&語法

-   在AutoConf...中有配置類,打開properties有默認自動配置項

-   ````java
        private static final Charset DEFAULT_ENCODING;
        public static final String DEFAULT_PREFIX = "classpath:/templates/";
        public static final String DEFAULT_SUFFIX = ".html";
        private boolean checkTemplate = true;
        private boolean checkTemplateLocation = true;
        private String prefix = "classpath:/templates/";
        private String suffix = ".html";
        private String mode = "HTML";
        private Charset encoding;
        private boolean cache;
        private Integer templateResolverOrder;
        private String[] viewNames;
        private String[] excludedViewNames;
        private boolean enableSpringElCompiler;
        private boolean renderHiddenMarkersBeforeCheckboxes;
        private boolean enabled;
        private final ThymeleafProperties.Servlet servlet;
        private final ThymeleafProperties.Reactive reactive;
    //只要我們把HTML文件放在classpath:/templates/,thymeleaf就能自動渲染
    //同時可以在配置文件中修改配置
    ````

-   [thymeleaf官網](https://www.thymeleaf.org/)

-   ![image-20191122231053533](image-20191122231053533.png)

-   現代化java服務端的模板引擎

-   ![image-20191122231204398](image-20191122231204398.png)

-   使用

````ht
導入開啟thymeleaf的語法空間,開啟語法提示
<html lang="en"  xmlns:th="http://www.thymeleaf.org">
````

````java 
@RequestMapping("/html")
    public String testHtml(Map<String,Object> map){
        map.put("hello", "這是Controller中返回的話");
        return "testPage";
    }
````

````html
<!DOCTYPE html>
<html lang="en"  xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>test</title>
</head>
<body>
<h1>這是一個測試文字</h1>
<!--將div里面的文本內容設置為,可以替換靜態的信息-->
<div th:text="${hello}">這是靜態的語句</div>
</body>
</html>
````

語法規則

詳見官方文檔:Attribute Precedence

使用${AttributeName}取值

1)、th:text 改變當前元素里面的內容

th:任意html屬性 替換原生屬性的值

在這里插入圖片描述

2)表達式

詳見thymeleaf文檔 expression

Simple expressions:(表達式語法)

Variable Expressions: ${…} 獲取變量值,OGNL;
  1. 獲取對象的屬性、調用方法
    1. 使用內置的基本對象
      1. #ctx : the context object.
      2. #vars: the context variables.
      3. #locale : the context locale.
      4. #request : (only in Web Contexts) the HttpServletRequest object.
      5. #response : (only in Web Contexts) the HttpServletResponse object.
      6. #session : (only in Web Contexts) the HttpSession object.
      7. #servletContext : (only in Web Contexts) the ServletContext object.例子:附錄中更詳細
      8. Established locale country: <span th:text="${#locale.country}">US</span>.
    2. 內置的一些工具對象
      #execInfo : information about the template being processed.
      #messages : methods for obtaining externalized messages inside variables expressions, in the same way as they would be obtained using #{…} syntax.
      #uris : methods for escaping parts of URLs/URIs
      #conversions : methods for executing the configured conversion service (if any).
      #dates : methods for java.util.Date objects: formatting, component extraction, etc.
      #calendars : analogous to
      #dates , but for java.util.Calendar objects. #numbers : methods for formatting numeric objects.
      #strings : methods for String objects: contains, startsWith, prepending/appending, etc.
      #objects : methods for objects in general.
      #bools : methods for boolean evaluation.
      #arrays : methods for arrays.
      #lists : methods for lists. #sets : methods for sets. #maps : methods for maps.
      #aggregates : methods for creating aggregates on arrays or collections.
      #ids : methods for dealing with id attributes that might be repeated (for example, as a result of an iteration).
      用法詳見附錄中的示例
      Selection Variable Expressions: *{…} 變量的選擇表達式,與${}在性質上是一樣的,補充:配合th:object 使用
    3. 例子:附錄中更詳細
Message Expressions: #{…} 獲取國際化內容
Link URL Expressions: @{…} 定義URL鏈接
Fragment Expressions: ~{…} 插入文檔
Literals(字面量)
  1. Text literals: ‘one text’ , ‘Another one!’ ,…
  2. Number literals: 0 , 34 , 3.0 , 12.3 ,…
  3. Boolean literals: true , false
  4. Null literal: null
  5. Literal tokens: one , sometext , main ,…
Text operations: (文本操作)
  1. String concatenation: +
  2. Literal substitutions: |The name is ${name}|
Arithmetic operations: (數學運算)
  1. Binary operators: + , - , * , / , %
  2. Minus sign (unary operator):
Boolean operations: (布爾運算)
  1. Binary operators: and , or
  2. Boolean negation (unary operator): ! , not
Comparisons and equality:(比較運算)
  1. Comparators: > , < , >= , <= ( gt , lt , ge , le )
  2. Equality operators: == , != ( eq , ne )
Conditional operators:(條件運算)(三元運算符也支持)
  1. If-then: (if) ? (then)
  2. If-then-else: (if) ? (then) : (else)
  3. Default: (value) ?: (defaultvalue)
Special tokens:(特殊)
  1. No-Operation: _ 不做處理
OGNL的例子
/* * Access to properties using the point (.). Equivalent to calling property getters. */ 
${person.father.name}
/* * Access to properties can also be made by using brackets ([]) and writing * the name of the property as a variable or between single quotes. */ 
${person['father']['name']}
/* * If the object is a map, both dot and bracket syntax will be equivalent to * executing a call on its get(...) method. */ ${countriesByCode.ES} ${personsByName['Stephen Zucchini'].age}
/* * Indexed access to arrays or collections is also performed with brackets, * writing the index without quotes. */ ${personsArray[0].name}
/* * Methods can be called, even with arguments. */ ${person.createCompleteName()} ${person.createCompleteNameWithSeparator('-')}

4、SpringMVC自動配置

SpringBoot官方文檔對SpringMVC的介紹

7.1.1. Spring MVC Auto-configuration

Spring Boot provides auto-configuration for Spring MVC that works well with most applications.

The auto-configuration adds the following features on top of Spring’s defaults:

以下是SpringBoot 對SpringMVC的默認配置:

  • Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.

    • 自動配置ViewResolver(視圖解析器:根據方法的返回值得到視圖對象(View),視圖對象決定如何渲染(轉發?重定向?))
    • 如何定制:我們可以自己給容器中添加一個視圖解析器;自動的將其整合進來;
  • Support for serving static resources, including support for WebJars (covered later in this document)). 靜態資源文件夾路徑和webjars

  • Automatic registration of Converter, GenericConverter, and Formatter beans.

    • 自動注冊了Converter, GenericConverter, and Formatter beans
    • Converter轉換器:SpringMVC中類型轉換使用Converter
    • Formatter格式化器:2017-11-3–>Date,不同國家的表示不同,按照一定格式轉化過來為轉化器
    • SpringBoot2.2.0與1版本不同,格式化器通過此方法注入容器
    		/** 在配置文件中配置日期格式化的規則 */
    		@Bean
            public FormattingConversionService mvcConversionService() {
                WebConversionService conversionService = new WebConversionService(this.mvcProperties.getDateFormat());
                this.addFormatters(conversionService);
                return conversionService;//日期格式化組件
            }
    
  • Support for HttpMessageConverters (covered later in this document).

    • HttpMessageConverters:SpringMVC中轉換Http請求和響應的;User類->json
    • HttpMessageConverters是從容器中確定的;獲取所有的HttpMessageConverter;
    • 自己給容器中添加HttpMessageConverter,只需要將自己的組件注冊容器中(@Bean、@Component)
  • Automatic registration of MessageCodesResolver (covered later in this document).定義錯誤代碼生成規則

  • Static index.html support. 靜態首頁訪問

  • Custom Favicon support (covered later in this document).

  • Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document).從容器中取出,故可以自己配一個ConfigurableWebBindingInitializer來替換默認的

    • 作用:初始化web數據綁定器WebDataBinder
    • 把請求數據綁定到JavaBean中

org.springframework.boot.autoconfigure.web :web中所有的自動配置

If you want to keep Spring Boot MVC features and you want to add additional MVC configuration (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc. If you wish to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver, you can declare a WebMvcRegistrationsAdapter instance to provide such components.

If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc.

擴展SpringMVC

參考:

If you want to keep Spring Boot MVC features and you want to add additional MVC configuration (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc. If you wish to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver, you can declare a WebMvcRegistrationsAdapter instance to provide such components.

編寫一個配置類(@Configuration),是WebMvcConfigurer類型;不能標注@EnableWebMvc

  • WebMvcConfigurer是SpringMVC的自動配置類
  • 在做其他自動配置的時候會導入@Import(EnableWebMvcConfiguration.class)
  • EnableWebMvcConfiguration繼承DelegationWebMvcConfiguration,從容器中獲取所有的WebMvcConfigure一起來起作用
  • 容器中所有的WebMvcConfigure都會起作用
  • 我們的配置類也會起作用

全面接管SpringMVC

If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc.

SpringBoot對MVC的自動配置不再需要,所有內容都是自己來配;

add your own @Configuration annotated with @EnableWebMvc.

效果:所有的SpringMVC的自動配置都失效了

原理:

  • 自動配置類是@ConditionOnMissingBean判斷是否自動配置的
  • 使用了這個注解將引入該類,導致判斷為容器中已經存在配置類,自動配置失效

源碼的原理與SpringMVC中相同,不再編寫

5、如何修改SpringBoot的默認配置

模式

1)由於SpringBoot大量使用了Condition注解,SpringBoot在自動配置很多組件的時候,先看容器中有沒有這個組件(通常為用戶自己配置的@Bean@Component),若沒有,再進行自動配置;如果有些組件可以有多個(如ViewResolver)將用戶的配置和自己的默認組合起來

2)在SpringBoot 中會有非常多的xxxxConfigure幫助我們進行擴展配置

3)在SpringBoot中會有很多的xxxCustomizer幫助我們進行定制配置

6、RestfulCRUD

1)、設置默認訪問首頁

  1. 將頁面命名為index.html置於"classpath:/META-INF/resources/", “classpath:/resources/”, “classpath:/static/”, "classpath:/public/"目錄下

  2. 編寫Handler攔截路徑"/“和”/index.html"返回到指定的視圖

  3. 在一個 WebMvcConfigurerAdapter(已經過時)中編寫一個addViewControllers完成一個視圖映射

    • 相關解決鏈接

    • 新方法繼承WebMvcConfigurer接口,需要@Bean注冊到容器(@Configuration中包含了@Bean)

    • package com.jirath.springboot_learn.config;
      
      import com.jirath.springboot_learn.service.HelloService;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
      import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
      
      /** * '@Configuration' point out that current object is a Configuration Class,which used to replace the Spring Configuration file before. */
      @Configuration
      public class MyConfig implements WebMvcConfigurer {
          /** * add current function's returned value to the ContextContainer, * the id of the module in this ContextContainer is the name of function; * @return the object which used to build a Bean */
          @Bean
          public HelloService helloService(){
              return new HelloService();
          }
      
          /** 在這里編寫一個ViewController */
          @Override
          public void addViewControllers(ViewControllerRegistry registry) {
              registry.addViewController("/").setViewName("index");
              registry.addViewController("/index.html").setViewName("index");
          }
      }
      
      

2)、國際化

SprigMVC的情況
  1. 編寫國際化配置文件
  2. 使用ResourceBundleMessageSource管理國際化資源文件
  3. 在頁面使用fmt:message取出國際化內容
SpringBoot模式

步驟:

  1. 編寫國際化配置文件,抽取頁面需要顯示的國際化信息

    • 新建文件夾,新建文件 頁面_國家代碼.properties
    • idea可以自動識別國際化配置文件,可以快速新建文件,如下圖
    • 在idea的國際化視圖中編寫屬性
  2. SpringBoot自動配置好了管理國際化資源文件的組件

    • MessageSourceAutoConfiguration

    • @Configuration(
          proxyBeanMethods = false
      )
      @ConditionalOnMissingBean(
          name = {"messageSource"},
          search = SearchStrategy.CURRENT
      )
      @AutoConfigureOrder(-2147483648)
      @Conditional({MessageSourceAutoConfiguration.ResourceBundleCondition.class})
      @EnableConfigurationProperties
      public class MessageSourceAutoConfiguration {
          private static final Resource[] NO_RESOURCES = new Resource[0];
      
          public MessageSourceAutoConfiguration() {
          }
      
          @Bean
          @ConfigurationProperties(
              prefix = "spring.messages"
          )
          public MessageSourceProperties messageSourceProperties() {
              return new MessageSourceProperties();
          }
      
          @Bean
          public MessageSource messageSource(MessageSourceProperties properties) {
              ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
              if (StringUtils.hasText(properties.getBasename())) {
                  //設置國際化管理的基礎名(去掉語言,國家代碼的)
                  messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(properties.getBasename())));
              }
      
              if (properties.getEncoding() != null) {
                  messageSource.setDefaultEncoding(properties.getEncoding().name());
              }
      
              messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale());
              Duration cacheDuration = properties.getCacheDuration();
              if (cacheDuration != null) {
                  messageSource.setCacheMillis(cacheDuration.toMillis());
              }
      
              messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat());
              messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage());
              return messageSource;
          }
      
          //我們的配置文件可以直接放在類路徑下叫messages.properties;
          protected static class ResourceBundleCondition extends SpringBootCondition {
              private static ConcurrentReferenceHashMap<String, ConditionOutcome> cache = new ConcurrentReferenceHashMap();
      
              protected ResourceBundleCondition() {
              }
      
              public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
                  String basename = context.getEnvironment().getProperty("spring.messages.basename", "messages");
                  ConditionOutcome outcome = (ConditionOutcome)cache.get(basename);
                  if (outcome == null) {
                      outcome = this.getMatchOutcomeForBasename(context, basename);
                      cache.put(basename, outcome);
                  }
      
                  return outcome;
              }
      
              private ConditionOutcome getMatchOutcomeForBasename(ConditionContext context, String basename) {
                  Builder message = ConditionMessage.forCondition("ResourceBundle", new Object[0]);
                  String[] var4 = StringUtils.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(basename));
                  int var5 = var4.length;
      
                  for(int var6 = 0; var6 < var5; ++var6) {
                      String name = var4[var6];
                      Resource[] var8 = this.getResources(context.getClassLoader(), name);
                      int var9 = var8.length;
      
                      for(int var10 = 0; var10 < var9; ++var10) {
                          Resource resource = var8[var10];
                          if (resource.exists()) {
                              return ConditionOutcome.match(message.found("bundle").items(new Object[]{resource}));
                          }
                      }
                  }
      
                  return ConditionOutcome.noMatch(message.didNotFind("bundle with basename " + basename).atAll());
              }
      
              private Resource[] getResources(ClassLoader classLoader, String name) {
                  String target = name.replace('.', '/');
      
                  try {
                      return (new PathMatchingResourcePatternResolver(classLoader)).getResources("classpath*:" + target + ".properties");
                  } catch (Exception var5) {
                      return MessageSourceAutoConfiguration.NO_RESOURCES;
                  }
              }
          }
      }
      
      
  3. 去頁面獲取國際化的值;

    1. 利用thymeleaf模板引擎,標准語法中的messages
    2. #{}獲取國際化信息,#{配置的文件頭(默認為message).屬性}

在這里插入圖片描述
在這里插入圖片描述

原理

國際化Locale(區域信息對象);LocaleResolver(獲取區域信息對象)

默認的區域信息解析器,根據請求頭的信息獲取Locale來確定,進行國際化

@Bean
        @ConditionalOnMissingBean
        @ConditionalOnProperty(
            prefix = "spring.mvc",
            name = {"locale"}
        )
        public LocaleResolver localeResolver() {
            //若為固定的就返回固定的,若不是國定的,就從請求頭中求得信息,
            if (this.mvcProperties.getLocaleResolver() == org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties.LocaleResolver.FIXED) {
                return new FixedLocaleResolver(this.mvcProperties.getLocale());
            } else {
                AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
                localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
                return localeResolver;
            }
        }

自訂的區域信息解析器(使用參數完成區域信息的解析)

  • 編寫類實現LocaleResolver接口,將這個類注冊到容器中

在這里插入圖片描述

可以直接在Configuration中編寫一個內部類完成

3)、登錄

編寫一個用戶登錄的接口,方法與SpringMVC大致相同,在參數前加@RequestParam("")可以確定該參數對應的數據名,且該參數必備。

idea在運行期間不會修改代碼

讓編寫的頁面修改實時生效:

  1. 禁用thymeleaf緩存

    • #禁用thymeleaf緩存
      spring.thymeleaf.cache=false
      
  2. 頁面修改完成后,使用ctrl+F9重新編譯

若登錄失敗,需要提升失敗信息

  • 使用thymeleaf模板引擎中的判斷標簽

  • 使用thymeleaf中對象的引入,引入String對象,利用String對象中的isEmpty判斷是否存在該信息(錯誤信息)

  • <p style="color: red" th:text="${mesg}" th:if="${not #strings.isEmpty(msg)}"></p>
    

使用MVC視圖解析跳轉會出現刷新重新提交表單的情況,為防止這種情況發生,我們可以使用重定向

  • 在跳轉的地址前加 redirect: 表示重定向
  • 在視圖解析器中添加解析器,將上方重定向的視圖綁定到所需的頁面

4)、攔截器進行登錄檢查

為了防止他人隨機訪問后台管理頁面,可以利用攔截器機制,做登錄檢查

即利用SpringMVC中的HandlerInterceptor

  • 新建類實現HandlerInterceptor接口
  • 重寫preHandle方法,利用request對象讀取session信息
  • 在webMvcConfigurater配置類中添加攔截器addInterceptors
  • 在這里插入圖片描述

5)、CRUD-員工列表

要求:

  • 1)、RestfulCRUD:CRUD滿足Rest風格

操作類型 普通CRUD(根據url來區分操作) RestfulURL
查詢 getEmp emp----GET
添加 addEmp?xxx emp----POST
修改 updateEmp?xxx emp/{id}----PUT
刪除 deleteEmp?id=1 emp/{id}----DELETE

實驗設計:

操作 請求URL 請求方式
查詢所有員工 emps GET
查詢某個員工(來到修改頁面) emps/{id} GET
來到添加頁面 emp GET
添加員工 emp POST
來到修改頁面(對員工進行信息回顯) emp/{id} PUT
修改員工 emp PUT
刪除員工 emp/{id} DELETE

7、錯誤處理機制

1)、SpringBoot默認的錯誤處理機制

瀏覽器訪問

在這里插入圖片描述

postman訪問接口,信息使用json的方式返回

在這里插入圖片描述

瀏覽器發送的請求頭中優先接受text/html,表明優先接受html頁面

在這里插入圖片描述

postman發送的請求頭中無優先級在這里插入圖片描述

相關自動配置在AutoConfigure下web中的error中配置

原理參照ErrorMvcAutoConfiguration中的錯誤自動配置

給容器中添加了以下組件

  • DefaultErrorAttributes
    • 作用:幫我們在頁面共享信息
    • 在這里插入圖片描述
    • 默認去找頁面: error/404 error/+錯誤狀態碼
    • 方法:拼接視圖名,若存在模板引擎,就用模板引擎解析返回
    • 若模板引擎不可用,則用resolve,在靜態資源文件夾下找errorViewName對應的頁面 error/404.html
  • BasicErrorController
    1. 處理默認的/error請求
    2. 打開該類,處理兩種請求
    3. image-20191201122142505
    4. 其中html返回ModelAndView,可以處理html類型的數據,瀏覽器發送的請求來到此處理
    5. 另一個返回的是ResponseEntity,可以處理json類型的數據,其他客戶端來到這里進行處理
  • ErrorMvcAutoConfiguration.ErrorPageCustomizer
    1. ErrorPageCustomizer將調用ErrorProperties獲取配置的error文件目錄
    2. 系統出現錯誤后來到error請求進行處理;(web.xml注冊的錯誤頁面規則)
    3. 在這里插入圖片描述
  • ErrorMvcAutoConfiguration.PreserveErrorControllerTargetClassPostProcessor

步驟:

一旦系統出現4xx-5xx之類的錯誤

  1. ErrorPageCustomizer就會生效(定制錯誤的響應規則)
  2. 來到/error請求
  3. 被BasicErrorController處理
    1. 響應頁面
    2. 在應對html的請求的方法中,首先拿到一些狀態碼,一些model數據,返回modelAndView,在resolveError中解析返回去哪個頁面為錯誤頁面;包含頁面的地址與頁面的內容。不再粘貼解析部分代碼。
    3. 響應頁面去哪個頁面是由**DefaultErrorViewResolver**解析得到的
      1. 解析方法:
      2. 注冊的DefaultErrorViewRsolver解析,查看源碼,靜態寫了一些狀態碼,客戶端4xx,服務端5xx,解析:

為什么瀏覽器接收的是html頁面

2)、定制錯誤響應:

  1. 如何定制錯誤的頁面;

    1. 有模板引擎的情況下;error/狀態碼,在templates下新建文件夾error,建立相應的頁面:錯誤狀態碼.html。
    2. 我們可以使用4xx、5xx作為錯誤頁面的文件名來匹配這種類型的所有錯誤,精確優先(優先尋找精確的狀態碼頁面)
    3. 頁面能獲取的信息:
      1. timestamp:時間戳
      2. status:狀態碼
      3. error:錯誤提示
      4. exception:異常對象
      5. message:異常消息
      6. errors:jsr303數據校驗錯誤
    4. 無模板引擎(模板引擎找不到這個錯誤頁面),在靜態資源文件下找;
    5. 模板引擎和靜態資源文件夾下都沒有,默認來到SpringBoor的默認空白頁面

    即在templates下新建error文件夾,里面放上錯誤代碼命名的html(b),即可

  2. 定制錯誤的Json數據;

    編寫一個錯誤處理Handler,新建Controller類,使用@ControllerAdvice注解,

    使用此方法將捕獲服務器產生的異常,然后返回設定的內容。

    @ControllerAdvice
    public class ExceptionHandler{
        @ResposeBody
        @ExceptionHandler(一個異常類.class)
        public Map<String,Object> handlerException(Exception e){
            Map<String,Object> map = new HashMap<>();
            map.put("code","狀態碼");
            map.put("message",e.getMessage());
            return map;
        }
    }
    

    該方法會設置網頁和json訪問都返回json數據

    將當前錯誤設置為自適應

    讓錯誤控制轉發到/error進行自適應效果處理

    @ControllerAdvice
    public class ExceptionHandler{
        @ExceptionHandler(一個異常類.class)
        public Map<String,Object> handlerException(Exception e){
            Map<String,Object> map = new HashMap<>();
            map.put("code","狀態碼");
            map.put("message",e.getMessage());
            //轉發到/error請求
            return "forward:/error";
        }
    }
    

    ps:Spring的轉發與重定向:在返回的視圖名前添加forward: 轉發,redirect: 重定向

    上述代碼存在頁面返回為空白的問題,思考SpringBoor 的錯誤處理流程得知,SpringBoot在處理頁面 請求的錯誤時,會從error請求中取出狀態碼,其對應的參數名在方法中可查,為javax.servlet.error.status_code在這里插入圖片描述

    protected HttpStatus getStatus(HttpServletRequest request) {
            Integer statusCode = (Integer)request.getAttribute("javax.servlet.error.status_code");
            if (statusCode == null) {
                return HttpStatus.INTERNAL_SERVER_ERROR;
            } else {
                try {
                    return HttpStatus.valueOf(statusCode);
                } catch (Exception var4) {
                    return HttpStatus.INTERNAL_SERVER_ERROR;
                }
            }
        }
    

    所以我們一定要傳入自己的狀態碼,在請求參數上加javax.servlet.error.status_code的Attribute

    改進后的代碼如下

    @ControllerAdvice
    public class ExceptionHandler{
        @ExceptionHandler(一個異常類.class)
        public Map<String,Object> handlerException(Exception e,HttpServletRequest request){
            Map<String,Object> map = new HashMap<>();
            //傳入自己的狀態碼,這里設置為777
            request.setAttribute("javax.servlet.error.status_code",777)
            map.put("code","狀態碼");
            map.put("message",e.getMessage());
            //轉發到/error請求
            return "forward:/error";
        }
    }
    

    這樣就能來到自己定制的777錯誤頁面

    問題:無法攜帶定制數據

    將我們定制的數據攜帶出去

    出現錯誤后,會來到/error請求,被BasicErrorController處理,響應出去可以獲取的數據是由getErrorAttributes得到的(是AbstractErrorController(ErrorController)規定的方法);

    1. 完全編寫一個ErrorController的實現類【或者繼承AbstractErrorController的子類】放在容器中;

    2. 兩種返回方法(頁面與json)都使用了getErrorAttributes來得到數據,我們可以自己編寫一個MyErrorAttributes,繼承DefaultErrorAttributes(下面方法位於的類),重寫getErrorAttributes,先調用父類方法,返回父類方法返回的map,也可以在map中添加自己的信息

      public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
              Map<String, Object> errorAttributes = new LinkedHashMap();
              errorAttributes.put("timestamp", new Date());
              this.addStatus(errorAttributes, webRequest);
              this.addErrorDetails(errorAttributes, webRequest, includeStackTrace);
              this.addPath(errorAttributes, webRequest);
              return errorAttributes;
          }
      

自定義

@Component
public class MyErrorAttributes extends DefultErrorAttributes{
    //返回值的map就是頁面和json能獲取的所有字段
    @Override
    public Map<String,Object>......{
        Map<String,Object> map=super.getErrorAttributes(....);
        map.put("team","ccc");//個性添加字段
        return map;
    }
}

程序異常–>轉發/error–>使用ErrorAttributes獲取異常值

ErrorAttributes返回值的map就是頁面和json能獲取的所有字段,該方法有一參數為requestAttributes可以使用此來獲取在異常處理時,放在Attribute中的信息。但是在取參數時有兩個參數,第一個為Key第二個為scope,相信見源碼中,0為request域。

五、配置嵌入式Servlet容器

在之前寫web應用時,需要先打包,然后部署在tomcat容器中,

SpringBoot默認使用的是嵌入式Servlet容器(Tomcat)

問題?

嵌入式Servlet容器配置

1、外置的Servlet容器如何定義和修改Servlet容器的相關配置;

​ 1)修改server相關配置(ServerProperties)

server.port=8080
server.context-path=

#通用的Servlet容器設置
server.xxx
#Tomcat的設置,(屬性中有一個為tomcat)
server.tomcat.xxx

​ 編寫一嵌入式的servlet容器定制器;來修改Servlet容器的配置,兩種方式任選一個,是同一個底層原理。

在MvcConfig中添加一個EmbeddedServletContainerCustomer在這里插入圖片描述

注冊Servlet容器三大組件

傳統web應用的目錄結構 webapp/WEB-INF/web.xml,在web.xml中注冊組件

SpringBoot如何注冊?

  • ServletRegistrationBean
  • FilerRegistrationBean
  • ServletListenerRegistrationBean

先編寫一個繼承HttpServlet的類

添加Bean,返回一個指定的注冊Bean,參數為寫好的類與映射,完成Servlet注冊

在這里插入圖片描述

實現接口Filter來編寫Filter類

在這里插入圖片描述

添加filter到容器中

在這里插入圖片描述

注冊ServletContextListener

在這里插入圖片描述

SpringBoot 幫我們啟動SpingMvc的時候,自動注冊Spring前端控制器;DispatcherServlet通過server.servlet-path來修改SpringMVC前端控制器,默認配置為"/"

使用其他Servlet容器

Jetty(長連接,如聊天)

Undertow(不支持JSP)

SpringBoot支持三個Servlet服務器

tomcat、Jetty、Undertow,默認使用Tomcat

  1. Web依賴中排除Tomcat
  2. 引入其他的Servlet容器依賴

嵌入式Servlet容器自動配置原理

自動配置jar,web->EmbeddedServletContainerAutoConfiguration(嵌入式Servlet容器自動配置)

Spring Boot 如何自動配置servlet容器

SpringBoot 2版本相對於1代在這里去掉了對於容器中是否有用戶自定義的Servlet工廠的判斷,

  
	/** *判斷當前是否引入Tomcat依賴,若引入就加載Tomcat配置 */
	@Configuration(
        proxyBeanMethods = false
    )
    @ConditionalOnClass({Tomcat.class, UpgradeProtocol.class})
    public static class TomcatWebServerFactoryCustomizerConfiguration {
        public TomcatWebServerFactoryCustomizerConfiguration() {
        }

        @Bean
        public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {
            return new TomcatWebServerFactoryCustomizer(environment, serverProperties);
        }
    }

Spring Boot是通過檢查項目中是否存在相關容器的依賴,然后加載對應的配置,啟動對應的Servlet容器。創建對應的工廠對象,加載配置的編碼,連接數等等等等參數設置。配置好以后返回並啟動

我們對嵌入式容器配置的修改如何生效

  1. ServerProperties、
  2. EmbeddedServletContainerCustomer自定的Servlet容器定制器
  3. ServerProperties實現了EmbeddedServletContainerCustomer接口,本質也是Servlet容器定制器。

所以必為EmbeddedServletContainerCustomer:定制器幫我們修改了Servlet容器的配置

步驟:

  1. SpringBoot根據導入的依賴情況,給容器中添加相應的EmbeddedServletContainerFactory
  2. 容器中某個組件要創建對象就會驚動后置處理器:EmbeddedServletContainerCustomizerBeanPostProcessor
  3. 只要是嵌入式的Servlet容器工廠,后置處理器就工作
  4. 后置處理器,從容器中獲取所有的EmbeddedServletContainerCustomizer調用定制器的定制方法

嵌入式Servlet容器啟動原理

什么時候創建嵌入式的Servlet容器工廠?什么時候獲取嵌入式的Servlet容器啟動Tomcat

  1. 獲取嵌入式Servlet容器工廠,啟動運行run方法
  2. refreshContext(context);SpringBoot刷新IOC容器(創建IOC容器對象,並舒適化容器,創建容器中的每一個組件);有判斷web應用,不同的容器
  3. onRefresh();web的ioc容器重寫了onRefresh方法
  4. webioc容器會創建嵌入式的Servlet容器;createEmbeddedServletContainer();
  5. 獲取嵌入式Servlet容器工廠:
    1. 從ioc容器中獲取EmbeddedServletContainerFactory組件;
    2. TomcatEmbeddedServletContainerFactory創建對象,后置處理器識別到這個對象,就獲取所有的定制器來定制Servlet容器的相關配置;
  6. 使用容器工廠獲取嵌入式的Servlet容器工廠獲取一個工廠
  7. 嵌入式容器創建對象並啟動Servlet容器
  8. 先啟動Servlet容器,再將ioc容器中剩下的沒有創建出的對象獲取出來

使用外置的Servlet容器

嵌入式Servlet:打為可執行jar

​ 優點:簡單、便攜

​ 缺點:默認不支持JSP、優化定制比較復雜(使用定制器【ServerProerties、自定義EmbeddedServletContainerCustomizer】,自己編寫嵌入式Servlet容器的創建工廠)

外置的Servlet容器:外面安裝Tomcat——應用war包的方式打包

  1. 新建SpringBoot項目,packaging選擇war
  2. 右上角項目結構在modules中修改,選擇web右側有web Resource Directories web資源目錄,選擇ok就能創建,上側有一個Deployment Descriptors部署描述(生成web.xml文件),其他與SSM相似

六、數據訪問

1、jdbc配置

idea新建項目勾選jdbc,mysql,web

idea生成的項目自動攜帶了jdbc的stater和jdbc依賴

配置properties

spring.datasource下進行

在這里插入圖片描述

使用測試類查看數據源信息

在這里插入圖片描述

效果:

  • 默認使用org.apache.tomcat.jdbc.pool.DataSource作為數據源(1版本),2版本默認使用com.zaxxer.hikari.HikarDataSource作為數據源
  • 數據源的相關配置都在DataSourceProperties里面

自動配置原理:

位置在autoconfigure中的jdbc包中

  1. 參考DataSourceConfiguration,根據配置創建數據源,默認使用Tomcat(2版本使用hikari)連接池;可以使用spring.datasource.type指定自定義的數據類型

  2. SpringBoot默認支持 Tomcat,hikari,Basic三種DataSource

  3. 自定義數據源類型,builder設計模式在這里插入圖片描述

  4. DataSourceInitializer: ApplicationListener

    • 作用:

      1. runSchemaScripts();運行建表語句
      2. runDataScript();運行插入數據的sql語句
    • 默認只需要將文件命名:

      • schema-*.sql data-*.sql
        
    • 失敗的在配置文件加initialization-mode: always(2版本的改動)

    • 若想配置自己定義的名字,在配置文件中datasource下配置schema,查看關聯的properties可以得知改項接收的為list,使用yml的list寫法即可 如:- classpath:department.sql

2.使用druid

視頻已經過時,就自己找來的文檔

Druid的簡介

Druid首先是一個數據庫連接池。Druid是目前最好的數據庫連接池,在功能、性能、擴展性方面,都超過其他數據庫連接池,包括DBCP、C3P0、BoneCP、Proxool、JBoss DataSource。Druid已經在阿里巴巴部署了超過600個應用,經過一年多生產環境大規模部署的嚴苛考驗。Druid是阿里巴巴開發的號稱為監控而生的數據庫連接池!

同時Druid不僅僅是一個數據庫連接池,它包括四個部分:

Druid是一個JDBC組件,它包括三個部分:

基於Filter-Chain模式的插件體系。

DruidDataSource 高效可管理的數據庫連接池。

SQLParser

Druid的功能

1、替換DBCP和C3P0。Druid提供了一個高效、功能強大、可擴展性好的數據庫連接池。

2、可以監控數據庫訪問性能,Druid內置提供了一個功能強大的StatFilter插件,能夠詳細統計SQL的執行性能,這對於線上分析數據庫訪問性能有幫助。

3、數據庫密碼加密。直接把數據庫密碼寫在配置文件中,這是不好的行為,容易導致安全問題。DruidDruiver和DruidDataSource都支持PasswordCallback。

4、SQL執行日志,Druid提供了不同的LogFilter,能夠支持Common-Logging、Log4j和JdkLog,你可以按需要選擇相應的LogFilter,監控你應用的數據庫訪問情況。

5、擴展JDBC,如果你要對JDBC層有編程的需求,可以通過Druid提供的Filter機制,很方便編寫JDBC層的擴展插件。

所以Druid可以:

1、充當數據庫連接池。
2、可以監控數據庫訪問性能
3、獲得SQL執行日志

配置Druid

新的Druid推出了stater進行配置,操作很方便

官方給的介紹

引入stater

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

在配置文件進行配置

spring:
  application:
    name: springboot-test-exam1
  datasource:
    # 使用阿里的Druid連接池
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    # 填寫你數據庫的url、登錄名、密碼和數據庫名
    url: jdbc:mysql://localhost:3306/databaseName?useSSL=false&characterEncoding=utf8
    username: root
    password: root
    druid:
      # 連接池的配置信息
      # 初始化大小,最小,最大
      initial-size: 5
      min-idle: 5
      maxActive: 20
      # 配置獲取連接等待超時的時間
      maxWait: 60000
      # 配置間隔多久才進行一次檢測,檢測需要關閉的空閑連接,單位是毫秒
      timeBetweenEvictionRunsMillis: 60000
      # 配置一個連接在池中最小生存的時間,單位是毫秒
      minEvictableIdleTimeMillis: 300000
      validationQuery: SELECT 1
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      # 打開PSCache,並且指定每個連接上PSCache的大小
      poolPreparedStatements: true
      maxPoolPreparedStatementPerConnectionSize: 20
      # 配置監控統計攔截的filters,去掉后監控界面sql無法統計,'wall'用於防火牆
      filters: stat,wall,slf4j
      # 通過connectProperties屬性來打開mergeSql功能;慢SQL記錄
      connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
      # 配置DruidStatFilter
      web-stat-filter:
        enabled: true
        url-pattern: "/*"
        exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
      # 配置DruidStatViewServlet
      stat-view-servlet:
        url-pattern: "/druid/*"
        # IP白名單(沒有配置或者為空,則允許所有訪問)
        allow: 127.0.0.1,192.168.163.1
        # IP黑名單 (存在共同時,deny優先於allow)
        deny: 192.168.1.73
        # 禁用HTML頁面上的“Reset All”功能
        reset-enable: false
        # 登錄名
        login-username: admin
        # 登錄密碼
        login-password: 123456

3、整合Jpa

Jpa(Spring Data Jpa)一款hibernate發展來的ORM框架,全自動持久層操作,不用書寫sql。

SpringBoot整合Spring Data Jpa

4、整合MyBatis

MyBatis原理

一、Mybatis工作原理圖

mybatis 原理圖如下所示:在這里插入圖片描述

二、工作原理解析

mybatis應用程序通過SqlSessionFactoryBuilder從mybatis-config.xml配置文件(也可以用Java文件配置的方式,需要添加@Configuration)來構建SqlSessionFactory(SqlSessionFactory是線程安全的);

然后,SqlSessionFactory的實例直接開啟一個SqlSession,再通過SqlSession實例獲得Mapper對象並運行Mapper映射的SQL語句,完成對數據庫的CRUD和事務提交,之后關閉SqlSession。

說明:SqlSession是單線程對象,因為它是非線程安全的,是持久化操作的獨享對象,類似jdbc中的Connection,底層就封裝了jdbc連接。

詳細流程如下:

1、加載mybatis全局配置文件(數據源、mapper映射文件等),解析配置文件,MyBatis基於XML配置文件生成Configuration,和一個個MappedStatement(包括了參數映射配置、動態SQL語句、結果映射配置),其對應着<select | update | delete | insert>標簽項。

2、SqlSessionFactoryBuilder通過Configuration對象生成SqlSessionFactory,用來開啟SqlSession。

3、SqlSession對象完成和數據庫的交互:
a、用戶程序調用mybatis接口層api(即Mapper接口中的方法)
b、SqlSession通過調用api的Statement ID找到對應的MappedStatement對象
c、通過Executor(負責動態SQL的生成和查詢緩存的維護)將MappedStatement對象進行解析,sql參數轉化、動態sql拼接,生成jdbc Statement對象
d、JDBC執行sql。

e、借助MappedStatement中的結果映射關系,將返回結果轉化成HashMap、JavaBean等存儲結構並返回。

mybatis層次圖:在這里插入圖片描述

配置

MyBatis也有SpringBoot的stater可以使用,很方便

引入依賴

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

在啟動類上注解——增加MapperScan注解路徑執行mapper接口路徑

注意這里是dao層接口的位置,不是xml文件的位置

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
 
@SpringBootApplication
@MapperScan("com.sun.houses.mapper")
public class HousesApplication {
 
	public static void main(String[] args) {
		SpringApplication.run(HousesApplication.class, args);
	}
}

或者可以在接口上注解@Mapper,個人覺得很繁瑣

配置文件進行配置

mybatis:
  #實體類位置,可以直接進行映射
  type-aliases-package: com.jirath.jirathblog2.pojo,com.jirath.jirathblog2.query
  #mybatis-mapper xml文件位置
  mapper-locations: classpath:mybatis-mapping/*Mapper.xml
  #MyBatis配置
  configuration:
  	#開啟駝峰,配置有很多,不在多寫,使用默認
    map-underscore-to-camel-case: true

注解模式進行開發

在接口方法上增加注解

@Select("SELECT * FROM test")

@Update("UPDATE test SET test1=#{data}")

@Delete("DELETE FROM test WHERE id=#{id}")

@Insert("INSERT INTO test(...) values(...)")

七、創建SpringBootApplication

為啟動main方法打上斷點進行調試

啟動流程:

  1. 創建SpringApplication對象
    1. 調用initialize方法,2.x已經棄用,在構造方法中完成在這里插入圖片描述
    2. 在這里插入圖片描述
    3. 保存主配置類
    4. 判斷是否web應用
    5. 從類路徑下找META-INF /spring.factories 配置的所有ApplicationContextInitalizer然后保存
    6. 從類路徑下找META-INF /spring.factories 配置的所有ApplicationContextListener然后保存
    7. 多個配置類中找main方法所在的配置類
  2. 執行run方法
    1. 創建StopWatch(開始、停止的監聽)
    2. 聲明ioc容器(ConfigurableApplicationContext)
    3. ConfigureHeadlessProperty()跟awt相關操作
    4. 獲取SpringApplicationRunListener
    5. 回調所有的獲取的SpringApplicationRunListener的starting方法
    6. 封裝命令行參數
    7. 准備環境
    8. 配置環境
    9. 回調所有的listener的prepare environment方法
    10. Banner 打印Banner
    11. 創建ioc容器
    12. 准備上下文,回調之前准備的ApplicationContextInitalizer的initalizer方法
    13. 回調listener的contextPrepare方法,等所有的Listener都准備好了以后回調ContextLoaded
    14. 刷新容器,web版加入tomcat容器;spring注解版(會掃描到配置注解)
    15. 從ioc容器中獲取所有的ApplicationRunner回調然后CommandLineRunner進行回調
    16. 所有的SpringApplicationRunListener回調finish方法
    17. 整個應用啟動完成后返回ioc容器

在這里插入圖片描述

八、緩存

  1. 系統中高頻使用的數據,存儲在動態緩存區中,不需要打開數據庫進行操作,緩存中沒有的打開數據庫,可以再保存在緩存中。
  2. 臨時信息如驗證碼等等

1、統一的緩存開發規范:J2EE——JSR107

Java Cache定義了五個接口

  1. CachingProvider 定義了創建、配置、獲取、管理和控制多個CacheManager。一個應用可以在運行期訪問多個CachingProvider
  2. CacheManage 定義了創建、配置、獲取、管理和控制多個唯一命名的Cache,Cache存在於CacheManager上下文。一個CacheManager僅被一個CacheProvider擁有
  3. Cache 一個類似Map的數據結構並臨時存儲以Key為索引的值,一個Cache僅被一個CacheManager擁有
  4. Entry 一個存儲在Cache中的key-value對
  5. Expiry 一個條目的有效期,可以通過ExpiryPolicy設置。

在這里插入圖片描述

2、配置

使用JSR107需要導入依賴

<dependency>
	<groupId>javax.cache</groupId>
	<artifactId>cache-api</artifactId>
</dependency>

依賴就提供了注解與上面的接口,接口需要進行實現,但是較為麻煩,Spring提供了自己的緩存抽象,定義了類似的注解與概念

2、Spring緩存抽象

Spring從3.1開始定義了org.springframework.cache.Cache和org.springframework.cache.CacheManager接口來統一不同的緩存技術,並支持JCache(JSR-107)注解簡化開發

在這里插入圖片描述

重要概念&緩存注解

Cache 緩存接口、定義緩存操作、實現有:RedisCache、EhCacheCahe、ConcurrentMapCache等
CacheManager 緩存管理器,管理各種緩存(Cache)組件,對緩存真正CRUD操作在Cache組件中,每一個緩存組件有自己的名字
@Cacheable 主要針對方法配置、能根據方法的請求參數對其結果進行緩存
@CacheEvict 清空緩存
@CachePut 保證方法被調用、又希望結果被緩存(緩存更新)
@EnableCaching 開啟基於注解的緩存
keyGenerator 緩存數據時key生成策略
serialize 緩存數據時value序列化策略

配置環境

  1. 引入Cache模塊
  2. 配置基礎的持久層
  3. 開啟使用注解的緩存,@EnableCaching
  4. 給方法注解參數

@Cacheable屬性配置

針對方法配置、能根據方法的請求參數對其結果進行緩存,以后再查詢相同的數據,直接從緩存中獲取,不再調用方法

cacheNames/value

文檔如下
在這里插入圖片描述

指定緩存組件的名字

key

在這里插入圖片描述

緩存的數據使用的key: 可以用他來指定。默認使用方法參數的值

指定key的時候可以使用SpEL表達式

SpEL表達式
名字 位置 描述 示例
methodName root object 當前被調用的方法名 #root.methodName
method root object 當前被調用的方法 #root.method.name
target root object 當前被調用的目標對象 #root.target
targetClass root object 當前被調用的目標對象類 #root.targetClass
args root object 當前被調用的方法的參數列表 #root.args[0]
caches root object 當前方法調用使用的緩存列表(如@Cacheable(value={“cache1”, “cache2”})),則有兩個cache #root.caches[0].name
argument name evaluation context 方法參數的名字. 可以直接 #參數名 ,也可以使用 #p0或#a0 的形式,0代表參數的索引; #iban 、 #a0 、 #p0
result evaluation context 方法執行后的返回值(僅當方法執行之后的判斷有效,如‘unless’,’cache put’的表達式 ’cache evict’的表達式beforeInvocation=false) #result

keyGenerator

在這里插入圖片描述
key的生成器(keyGenerator),可以指定KeyGenerator的生成器組件id

key與keyGenerator二選一使用

在這里插入圖片描述在這里插入圖片描述

cacheManager

在這里插入圖片描述

condition

在這里插入圖片描述
緩存的條件,可以為空,使用 SpEL 編寫,返回 true 或者 false,只有為 true 才進行緩存/清除緩存,在調用方法之前之后都能判斷

#a0>1 第一個參數值大於1

unless

在這里插入圖片描述

用於否決緩存的,不像condition,該表達式只在方法執行之后判斷,此時可以拿到返回值result進行判斷。條件為true不會緩存,fasle才緩存

例如:
@Cacheable(value=”testcache”,unless=”#result == null”)

sync

在這里插入圖片描述

是否開啟異步,開啟異步則unless不支持

@CachePut

既調用方法,又更新緩存數據在這里插入圖片描述
參數與@Cacheable相同

可以利用@CachePut更新@Cacheable控制的緩存,達到實時更新

@CacheEvict

緩存清除,在清除數據庫數據時可以通過@CacheEvict同時清除緩存

key、keyGenerator與以上兩個相同

condition

為真時清除

allEntries

在這里插入圖片描述

清空緩存

beforeInvocation

在這里插入圖片描述

緩存的清除是否在方法的之前執行,默認代表方法之后執行,若出現了異常就不會清除

修改為true為在方法之前,無論方法運行情況,都清除

@Caching

@Caching是為了應對組合緩存注解使用的,相當於上面上注解的總和
在這里插入圖片描述

@CacheConfig

Cache的公共配置注解,類上配置了此注解后,類中的緩存注解就有了一個公共配置

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

原理

  1. 自動配置類:CacheAutoConfiguration

    1. 加入自定義配置
    2. 導入緩存配置模塊如下在這里插入圖片描述
  2. 哪個配置類默認生效:

    配置文件開啟debug=true就能在啟動的時候打開自動配置報告

    可以看到SimpleCacheConfiguration生效了

  3. 給容器注冊了一個CacheManager:ConcurrentMapManager

  4. 可以獲取和創建ConcurrentMapCache類型的緩存組件:將數據保存在ConcurrentMap中

運行流程

  1. 方法運行之前,先去查詢Cache(緩存組件),按照cacheNames指定的名字獲取(CacheManager先獲取相應的緩存)
  2. 若無緩存則新建
  3. 在Cache中查找緩存的內容,使用一個key,默認是方法的參數,
    1. key是按照某種策略生成的:使用KeyGenerator生成(默認為SimpleKeyGenerator生成)
  4. 沒有查到緩存就調用目標方法
  5. 將目標方法返回的結果放進緩存之中

@Cacheable標注的方法執行之前先檢查緩存中有沒有數據,默認按照參數的值作為key去查緩存,如果沒有就運行方法並將結果緩存

3、整合redis緩存

SpringBoot默認使用的是ConcurrentMapCacheManager:將數據保存在ConcurrentMap中(HashMap)

開發中使用緩存中間件:redis、excache等等

Redis中文網

Redis 是完全開源免費的,遵守BSD協議,是一個高性能的key-value數據庫。

Redis的安裝與使用不再贅述

整合

引入stater:spring-boot-stater-data-redis

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

配置配置文件

# Redis數據庫索引(默認為0 redis有16個庫)
spring.redis.database=0  
# Redis服務器地址
spring.redis.host=127.0.0.1
# Redis服務器連接端口
spring.redis.port=6379  (取自意大利歌女Alessia Merz的名字)
# Redis服務器連接密碼(默認為空)
spring.redis.password=  
# 連接池最大連接數(使用負值表示沒有限制)
spring.redis.pool.max-active=8  
# 連接池最大阻塞等待時間(使用負值表示沒有限制)
spring.redis.pool.max-wait=-1  
# 連接池中的最大空閑連接
spring.redis.pool.max-idle=8  
# 連接池中的最小空閑連接
spring.redis.pool.min-idle=0  
# 連接超時時間(毫秒)
spring.redis.timeout=0

查看RedisAutoConfiguratoin得知,redis會自動向項目注入RedisTemplate

RedisTemplate可以直接使用依賴注入的方法進行調用,操作redis

/** * Standard Redis configuration. */
@Configuration
protected static class RedisConfiguration {
 
    @Bean
    @ConditionalOnMissingBean(name = "redisTemplate")
    public RedisTemplate<Object, Object> redisTemplate(
            RedisConnectionFactory redisConnectionFactory)
                    throws UnknownHostException {
        RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
 
    @Bean
    @ConditionalOnMissingBean(StringRedisTemplate.class)
    public StringRedisTemplate stringRedisTemplate(
            RedisConnectionFactory redisConnectionFactory)
                    throws UnknownHostException {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
}
  • RedisTemplate<Object,Object> 可以對Redis中key和value都為object類型的數據進行操作,默認會將對象使用JdkSerializationRedisSerializer進行序列化
  • StringRedisTemplate可以對Redis中key和value都是String類型的數據進行操作

Redis使用自制序列化器

RedisTemplate默認保存對象是使用的Java序列化機制

原理:改寫RedisTemplate

默認情況下,template.setDefaultSerializer是java序列化器,查看該方法,得知該方法需要的是一個redis序列化器接口的實現類,假如我們使用了Jackson2,該插件會自帶一個json序列化器,更換為該序列化器即可。

在這里插入圖片描述

更多需求,查看源碼改寫

九、Spring Boot 與消息

1、消息隊列是什么以及why消息隊列

消息最經典的三個場景異步、削峰、解耦

Tip:這三個場景也是消息隊列的經典場景,大家基本上要爛熟於心那種,就是一說到消息隊列你腦子就要想到異步、削峰、解耦,條件反射那種。

1、異步:

在普通情況下,我們所做的程序都是流水線形式的,一條一條逐步執行。

在處理需要大量操作的請求時,響應時間會激增

在在e響應時間)在200ms內,超出的全部優化,我現在所負責的系統QPS也是9W+就是抖動一下網絡集群都可能炸鍋那種,RT基本上都要求在50ms以內。

img

你們可以看到這才加了三個,我可以斬釘截鐵的告訴你真正的下單流程涉及的系統絕對在10個以上(主流電商),越大的越多。

這個鏈路這樣下去,時間長得一批,用戶發現我買個東西你特么要花幾十秒,垃圾電商我不在你這里買了,不過要是都像並夕夕這么便宜,真香

但是我們公司沒有夕夕的那個經濟實力啊,那只能優化系統了。

Tip:我之前在的電商老東家要求所有接口的RtResponseTime響應時間)在200ms內,超出的全部優化,我現在所負責的系統QPS也是9W+就是抖動一下網絡集群都可能炸鍋那種,RT基本上都要求在50ms以內。

img

大家感受一下這個QPS。

嗯不錯,鏈路長了就慢了,那你怎么解決的?

解決方案:異步

你對比一下是不是發現,這樣子最多只用100毫秒用戶知道下單成功了,至於短信你遲幾秒發給他他根本不在意是吧。

img

why異步,而不是線程池?

原因就是另一個特性

2、解耦:

  1. 在系統復雜的情況下,增加接口進行擴展操作是麻煩的,添加新功能需要重新修改項目並重新發布
  2. 同時所有功能寫在一個模塊,不單單是耦合這一個問題,出問題排查也麻煩,流程里面隨便一個地方出問題搞不好會影響到其他的點,小伙伴說我每個流程都try catch不就行了,相信我別這么做,這樣的代碼就像個定時炸彈💣,你不知道什么時候爆炸,平時不炸偏偏在你做活動的時候炸,你就領個P0故障收拾書包提前回家過年吧。

Tip:P0—PN 是互聯網大廠經常用來判定事故等級的機制,P0是最高等級了。

用了消息隊列,耦合這個問題就迎刃而解。

你下單了,你就把你支付成功的消息告訴別的系統,他們收到了去處理就好了,你只用走完自己的流程,把自己的消息發出去,那后面要接入什么系統簡單,直接訂閱你發送的支付成功消息,你支付成功了我監聽就好了

img

那你的流程走完了,你不用管別人是否成功么?比如你下單了積分沒加,優惠券沒扣怎么辦?

問題是個好問題,但是沒必要考慮,業務系統本身就是自己的開發人員維護的,你積分扣失敗關我下單的什么事情?你管好自己下單系統的就好了。

Tip:話是這么說,但是這其實是用了消息隊列的一個缺點,涉及到分布式事務的知識點,我下面會提到。

3、削峰:

平時流量很低,但是你要做秒殺活動00 :00的時候流量瘋狂懟進來,你的服務器,RedisMySQL各自的承受能力都不一樣,你直接全部流量照單全收肯定有問題啊,直接就打掛了。

解決高壓訪問

簡單,把請求放到隊列里面,然后至於每秒消費多少請求,就看自己的服務器處理能力,你能處理5000QPS你就消費這么多,可能會比正常的慢一點,但是不至於打掛服務器,等流量高峰下去了,你的服務也就沒壓力了。

設置消息隊列容納數量,超出額度的返回異常頁面,同時應用於秒殺,可以判斷先后

你看阿里雙十一12:00的時候這么多流量瞬間涌進去,他有時候是不是會慢一點,但是人家沒掛啊,或者降級給你個友好的提示頁面,等高峰過去了服務器可以繼續正常工作。

為了這個圖特意打高一台服務的流量

使用了消息隊列的問題?

沒錯面試官,我使用他是因為他帶給我們很多好處,但是使用之后問題也是接踵而至

同樣的暖男我呀,也從三個點介紹他主要的缺點:

系統復雜性

本來蠻簡單的一個系統,我代碼隨便寫都沒事,現在你憑空接入一個中間件在那,我是不是要考慮去維護他,而且使用的過程中是不是要考慮各種問題,比如消息重復消費消息丟失消息的順序消費等等,反正用了之后就是賊煩。

數據一致性

這個其實是分布式服務本身就存在的一個問題,不僅僅是消息隊列的問題,但是放在這里說是因為用了消息隊列這個問題會暴露得比較嚴重一點。

就像我開頭說的,你下單的服務自己保證自己的邏輯成功處理了,你成功發了消息,但是優惠券系統,積分系統等等這么多系統,他們成功還是失敗你就不管了?

我說了保證自己的業務數據對的就好了,其實還是比較不負責任的一種說法,這樣就像個渣男,沒有格局這樣呀你的路會越走越窄的

所有的服務都成功才能算這一次下單是成功的,那怎么才能保證數據一致性呢?

分布式事務:把下單,優惠券,積分。。。都放在一個事務里面一樣,要成功一起成功,要失敗一起失敗。

Tip:分布式事務在互聯網公司里面實在常見,我也不在這里大篇幅介紹了,后面都會專門說的。

可用性

你搞個系統本身沒啥問題,你現在突然接入一個中間件在那放着,萬一掛了怎么辦?我下個單MQ掛了,優惠券不扣了,積分不減了,這不是殺一個程序員能搞定的吧,感覺得殺一片。

至於怎么保證高可用,還是那句話也不在這里展開討論了,我后面一樣會寫,像寫Redis那樣寫出來的。

放心敖丙我不是渣男來的,我肯定會對你們負責的。點贊!

技術選型(選擇中間件)?

目前在市面上比較主流的消息隊列中間件主要有,Kafka、ActiveMQ、RabbitMQ、RocketMQ 等這幾種。

但是,ActiveMQRabbitMQ這兩者因為吞吐量還有GitHub的社區活躍度的原因,在各大互聯網公司都已經基本上絕跡了,業務體量一般的公司會是有在用的,但是越來越多的公司更青睞RocketMQ這樣的消息中間件了。

KafkaRocketMQ一直在各自擅長的領域發光發亮,不過寫這篇文章的時候我問了螞蟻金服,字節跳動和美團的朋友,好像大家用的都有點不一樣,應該都是各自的中間件,可能做過修改,也可能是自研的,大多沒有開源

基於KafkaRocketMQ兩者的優點自研的消息隊列中間件,吞吐量、可靠性、時效性等都很可觀。

我們回歸正題,我這里用網上找的對比圖讓大家看看差距到底在哪里:

img

大家其實一下子就能看到差距了,就拿吞吐量來說,早期比較活躍的ActiveMQRabbitMQ基本上不是后兩者的對手了,在現在這樣大數據的年代吞吐量是真的很重要

比如現在突然爆發了一個超級熱點新聞,你的APP注冊用戶高達億數,你要想辦法第一時間把突發全部推送到每個人手上,你沒有大吞吐量的消息隊列中間件用啥去推?

再說這些用戶大量涌進來看了你的新聞產生了一系列的附帶流量,你怎么應對這些數據,很多場景離開消息隊列基本上難以為繼

部署方式而言前兩者也是大不如后面兩個天然分布式架構的哥哥,都是高可用的分布式架構,而且數據多個副本的數據也能做到0丟失。

我們再聊一下RabbitMQ這個中間件其實還行,但是這玩意開發語言居然是erlang,我敢說絕大部分工程師肯定不會為了一個中間件去刻意學習一門語言的,開發維護成本你想都想不到,出個問題查都查半天。

至於RocketMQ(阿里開源的),git活躍度還可以。基本上你push了自己的bug確認了有問題都有阿里大佬跟你試試解答並修復的,我個人推薦的也是這個,他的架構設計部分跟同樣是阿里開源的一個RPC框架是真的很像(Dubbo)可能是因為師出同門的原因吧。

Tip:Dubbo等我寫到RPC我會詳細介紹的。

Kafka這是個大哥,大數據領域,公司的日志采集,實時計算等場景,都離不開他的身影,他基本上算得上是世界范圍級別的消息隊列標桿

2、重要概念

1、兩個重要概念

消息代理(message broker)

目的地(destination)

2、消息隊列的兩種模式

消息隊列包括兩種模式,點對點模式(point to point, queue)和發布/訂閱模式(publish/subscribe,topic)。

3.1 點對點模式

點對點模式下包括三個角色:

  • 消息隊列
  • 發送者 (生產者)
  • 接收者(消費者)

img

消息發送者生產消息發送到queue中,然后消息接收者從queue中取出並且消費消息。消息被消費以后,queue中不再有存儲,所以消息接收者不可能消費到已經被消費的消息。

點對點模式特點

  • 每個消息只有一個接收者(Consumer)(即一旦被消費,消息就不再在消息隊列中);
  • 發送者和接收者間沒有依賴性,發送者發送消息之后,不管有沒有接收者在運行,都不會影響到發送者下次發送消息;
  • 接收者在成功接收消息之后需向隊列應答成功,以便消息隊列刪除當前接收的消息;

3.2 發布/訂閱模式

發布/訂閱模式下包括三個角色:

  • 角色主題(Topic)
  • 發布者(Publisher)
  • 訂閱者(Subscriber)

img

發布者將消息發送到Topic,系統將這些消息傳遞給多個訂閱者

發布/訂閱模式特點

  • 每個消息可以有多個訂閱者
  • 發布者和訂閱者之間有時間上的依賴性。針對某個主題(Topic)的訂閱者,它必須創建一個訂閱者之后,才能消費發布者的消息。
  • 為了消費消息,訂閱者需要提前訂閱該角色主題,並保持在線運行

3、RabbitMQ

a.簡介

由erlang開發的AMQP(Advanced Message Queue Protocol)的開源實現

b.核心概念

Message

發送者給消息代理(消息服務器)發送的內容

Publisher

消息的生產者,也是一個向交換器發布消息的客戶端程序。

Exchange

交換器,用來接收生產者發送的消息並將這些消息路由給服務器中的隊列。

Exchange有4中類型:direct(默認, 點對點)、fanout,topic和headers,不同類似轉發策略不同

Queue

消息隊列,用來保存消息直到發給消費者。它是消費者的容器,也是消息的終點。一個消息可以投入一個或多個隊列。消息一直在隊列里面,等待消費者連接到這個隊列將其取走。

Binding

綁定,用於消息隊列和交換器之間關聯。一個綁定就是基於路由鍵將交換器和消息隊列連接起來的路由規則,所以可以將交換器理解成一個由綁定構成的路由表

消息隊列與交換器可以是多對多

Connection、

網絡連接,比如一個TCP連接。

Channel

信道,多路復用連接中的一條獨立的雙向數據流通道。信道是建立在真實的TCP連接內的虛擬連接,AMQP命令都是通過信道發出去的。所有的動作都通過信道完成

對於操作系統來說建立和銷毀TCP都是非常昂貴的開銷,所有引入信道的概念,以復用一條TCP連接

Consumer

消息的消費者,表示一個從消息隊列中取得消息的客戶端應用程序。

Virtual Host

虛擬主機,表示一批交換器、消息隊列和相關對象。虛擬主機是共享相同的身份認證和加密環境的獨立服務器域。每個 vhost 本質上就是一個 mini 版的 RabbitMQ 服務器,擁有自己的隊列、交換器、綁定和權限機制。vhost 是 AMQP 概念的基礎,必須在連接時指定,RabbitMQ 默認的 vhost 是 /

Broker

表示消息隊列服務器實體

就是服務器

在這里插入圖片描述

c.Exchange類型

Direct

消息中的路由鍵(routing key)如果和 Binding 中的 binding key 一致, 交換器就將消息發到對應的隊列中。路由鍵與隊列名完全匹配,如果一個隊列綁定到交換機要求路由鍵為“dog”,則只轉發 routing key 標記為“dog”的消息,不會轉發“dog.puppy”,也不會轉發“dog.guard”等等。它是完全匹配、單播的模式。

在這里插入圖片描述

fanout

每個發到 fanout 類型交換器的消息都會分到所有綁定的隊列上去。fanout 交換器不處理路由鍵,只是簡單的將隊列綁定到交換器上,每個發送到交換器的消息都會被轉發到與該交換器綁定的所有隊列上很像子網廣播,每台子網內的主機都獲得了一份復制的消息。fanout 類型轉發消息是最快的

在這里插入圖片描述

topic

topic 交換器通過模式匹配分配消息的路由鍵屬性,將路由鍵和某個模式進行匹配,此時隊列需要綁定到一個模式上。它將路由鍵和綁定鍵的字符串切分成單詞,這些單詞之間用點隔開。它同樣也會識別兩個通配符:符號“#”和符號“#匹配0個或多個單詞*匹配一個單詞

在這里插入圖片描述

d.RabbitMQ安裝測試

用docker搜索鏡像安裝,帶有management的是含有管理界面的RabbitMQ

使用管理界面操作

4、RabbitMQ整合

先引入依賴(可以勾選idea項目選項快速完成)

在這里插入圖片描述

自動配置類:

  1. RabbitAutoConfiguration
  2. 有配置了自動連接工廠 ConnectionFactory
  3. RabbitProperties封裝了RabbitMQ的所有配置
  4. 在這里插入圖片描述
  5. 默認的端口5672,默認虛擬機名 /
  6. 為容器增加RabbitTemplate:給RabbitMQ發送和接收消息
  7. AmqpAdmin:RabbitMQ系統管理組件,可以增加隊列等等等等管理功能

應用RabbitTemplate操作舉例

簡單操作

在這里插入圖片描述
在這里插入圖片描述

自定序列化機制

在這里插入圖片描述

SpringAPI:RabbitTemplate

監聽場景@RabbitListener @EnableRabbit

@EnableRabbit注解在啟動類上,開啟RabbitMQ注解

@RabbitListener(queues = “name”) 監聽固定隊列

在這里插入圖片描述

下面是官方API,有耐心可以仔細看看參考
在這里插入圖片描述

AmqpAdmin的應用

利用程序創建Exchange等等
在這里插入圖片描述

下面是AmqpAdmin的api文檔

在這里插入圖片描述

十、SpringBoot與檢索

我們的應用經常需要添加檢索功能,開源的 ElasticSearch 是目前全文搜索引擎的首選。他可以快速的存儲、搜索和分析海量數據。Spring Boot通過整合Spring Data ElasticSearch為我們提供了非常便捷的檢索功能支持;Elasticsearch是一個分布式搜索服務,提供Restful API,底層基於Lucene,采用多shard(分片)的方式保證數據安全,並且提供自動resharding的功能,github等大型的站點也是采用了ElasticSearch作為其搜索服務,

ElasticSearch官方文檔

  1. 員工文檔 的形式存儲為例:一個文檔代表一個員工數據。存儲數據到 ElasticSearch 的行為叫做 索引 ,但在索引一個文檔之前,需要確定將文檔存儲在哪里。
  2. 一個 ElasticSearch 集群可以 包含多個 索引 ,相應的每個索引可以包含多個 類型 。 這些不同的類型存儲着多個 文檔 ,每個文檔又有 多個 屬性
  3. 類似關系:
    • –索引-數據庫
    • –類型-表
    • –文檔-表中的記錄
    • –屬性-列

在這里插入圖片描述

ES需要額外運行項目,暫時先不用

十一、Spring Boot 與任務

異步任務

默認情況下,程序一步步完成所有操作然后響應請求。為了讓任務互不影響,使用多線程來處理復雜的任務。

使用異步@Async

  1. 在啟動類上開啟異步注解@EnableAsync
  2. 在方法上注解@Async

定時任務

項目開發中經常需要執行一些定時任務,比如需要在每天凌晨時候,分析一次前一天的日志信息。Spring為我們提供了異步執行任務調度的方式,提供TaskExecutor 、TaskScheduler 接口。

兩個注解:@EnableScheduling、@Scheduled

開啟定時任務

  1. 在啟動類注解@EnableScheduling
  2. 在需要定時的方法上注解@Scheduled
  3. 為@Scheduled指定運行時間,參數如下
  4. 總共六位,每位之間用空格分離,按順序為 秒 分 時 日 月 星期幾
  5. “0 * * * * MON-FRI"意為工作日的每分鍾
  6. 使用"*“表示全部 ; 逗號”,"表示並列的關系 ; "1-5"表示1到5中的每一個 "0/4"表示每4秒/時(單位)一次
  7. "?"用於沖突匹配:日期與星期的沖突
  8. 星期可以使用縮寫或數字,0,7都是周日
  9. 1#2表示第二個星期一
  10. 更多參數見Spring文檔
    在這里插入圖片描述在這里插入圖片描述

郵件任務

郵件發送需要引入spring-boot-starter-mail

配置

引入依賴

spring-boot-starter-mail

配置文件

QQ郵箱密碼使用的是臨時授權碼,可以防止密碼泄露

QQ的郵件需要ssl安全連接,需要在配置文件中開啟

在這里插入圖片描述

自動配置類

查看自動配置類,SpringBoot默認配置了JavaMailSenderImpl類,直接注入此類就能利用發送文件

發送郵件

發送一個簡單的郵件

在這里插入圖片描述

發送一個復雜消息郵件

setText有一個參數是是否為html,傳送Html需要設置為true,

發送文件中參數可以為File或流,文件名可以新起。

在這里插入圖片描述在這里插入圖片描述

十二、Spring Boor與安全

兩個安全框架Shiro與SpringSecurity,

Shiro簡單,SpringSecurity復雜但功能強大,Spring Boot底層使用Spring Security

Shiro暫時不再贅述

SpringSecurity概述

幾個類:

WebSecurityConfigurerAdapter:自定義Security策略

AuthenticationManagerBuilder:自定義認證策略

@EnableWebSecurity:開啟WebSecurity模式

基礎概念

  • 應用程序的兩個主要區域是“認證”和“授權”(或者訪問控制)。這兩個主要區域是Spring Security 的兩個目標。
  • “認證”(Authentication),是建立一個他聲明的主體的過程(一個“主體”一般是指用戶,設備或一些可以在你的應用程序中執行動作的其他系統)。
  • “授權”(Authorization)指確定一個主體是否允許在你的應用程序執行一個動作的過程。為了抵達需要授權的店,主體的身份已經有認證過程建立。
  • 這個概念是通用的而不只在Spring Security中。

開啟SpringSecurity

首先引入依賴

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

編寫安全配置類

package com.example.securingweb;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
			.authorizeRequests()//無權限403 access deined
				.antMatchers("/", "/home").permitAll()
            	.antMatchers("/secret", "/allll").hasRole("MySelf")//需要角色MySelf才能訪問兩個目錄
				.anyRequest().authenticated()
				.and()
            //開啟登錄功能,如果未登錄,就會來到登錄頁面,Spring Security可以自動生成登錄頁面
			.formLogin()
				.loginPage("/login")
				.permitAll()
				.and()
			.logout()
				.permitAll();
	}
	//配置用戶信息,默認保存在內存中
	@Bean
	@Override
	public UserDetailsService userDetailsService() {
		UserDetails user =
			 User.withDefaultPasswordEncoder()
				.username("user")
				.password("password")
				.roles("USER")
				.build();

		return new InMemoryUserDetailsManager(user);
	}
    //jdbc形式的認證
    @Autowired
	private DataSource dataSource;

	@Autowired
	public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    // ensure the passwords are encoded properly
    UserBuilder users = User.withDefaultPasswordEncoder();
    auth
        .jdbcAuthentication()
            .dataSource(dataSource)
            .withDefaultSchema()
            .withUser(users.username("user").password("password").roles("USER"))    		.withUser(users.username("admin").password("password").roles("USER","ADMIN"));
}
}

更多功能查看官方文檔

十三、Spring Boot與分布式

在分布式系統中,國內常用zookeeper+dubbo組合,而Spring Boot推薦使用全棧的Spring,Spring Boot+Spring Cloud。

1、分布式概述

大型項目不斷升級,改動越來越麻煩,采用了分離的辦法,將不同的模塊部署在不同的機器運行

一個項目希望得到另一個項目的數據,就需要RPC遠程過程調用

但是,使用代碼調用過於麻煩,不能解耦,就需求一個RPC框架,也就是Dubbo和SpringCloud

有了RPC框架,又牽扯到一個概念——注冊中心幫助不同模塊進行聯系就像我們的通訊錄,服務可以通過查詢這個通訊錄來得知該使用哪個模塊。Zookeeper解決了這一需求
在這里插入圖片描述

  • ZooKeeper

    ZooKeeper 是一個分布式的開放源碼的分布式應用程序協調服務。它是一個為分布式應用提供一致性服務的軟件,提供的功能包括:配置維護、域名服務、分布式同步、組服務等。

  • Dubbo

    Dubbo是Alibaba開源的分布式服務框架,它最大的特點是按照分層的方式來架構,使用這種方式可以使各個層之間解耦合(或者最大限度地松耦合)。從服務模型的角度來看,Dubbo采用的是一種非常簡單的模型,要么是提供方提供服務,要么是消費方消費服務,所以基於這一點可以抽象出服務提供方(Provider)和服務消費方(Consumer)兩個角色。

Dubbo工作圖

在這里插入圖片描述

  1. Container: Dubbo運行的容器,在啟動的時候,負責啟動、加載、運行Provider
  2. Provider在啟動過程中會將自己能提供的信息注冊注冊中心
  3. 消費者在啟動時會從注冊中心訂閱自己需要的服務,注冊中心將他需要的地址列表返回,服務如果有變更,注冊中心會使用基於長連接的方式推送給消費者相當於消費者擁有實時服務提供者名單
  4. 消費者根據信息進行接口的調用若失敗,則從列表中取出另一個進行調用
  5. 調用的信息定義發送監控中心進行監控

安裝Zookeeper

使用docker進行安裝,

參考文章

https://blog.csdn.net/myNameIssls/article/details/81561975

整合Zookeeper、Dubbo、SpringBoot

1、引入依賴

Zookeeper+Dubbo 生產者、消費者 都要引入

在這里插入圖片描述

2、配置dubbo

名稱:自定的模塊名

地址:Zookeeper服務器地址

掃描包:發布的服務
在這里插入圖片描述

3、編寫服務

@ServiceDubbo的Service用於發布服務,項目啟動后,dubbo就會將配置的包下所有的@Service注解的服務注冊在配置的Zookeeper注冊中心

在這里插入圖片描述

4、消費者

同第二步配置文件,不需要掃描包配置

復制服務接口到消費者項目,要求全類名相同

編寫實現類調用接口,接口注解**@Reference** Dubbo會自動根據全類名搜索服務

完成。

在這里插入圖片描述

四種架構

  • l單一應用架構

    當網站流量很小時,只需一個應用,將所有功能都部署在一起,以減少部署節點和成本。此時,用於簡化增刪改查工作量的數據訪問框架(ORM)是關鍵。

  • l垂直應用架構

    當訪問量逐漸增大,單一應用增加機器帶來的加速度越來越小,將應用拆成互不相干的幾個應用,以提升效率。此時,用於加速前端頁面開發的Web框架(MVC)是關鍵。

  • l分布式服務架構

    當垂直應用越來越多,應用之間交互不可避免,將核心業務抽取出來,作為獨立的服務,逐漸形成穩定的服務中心,使前端應用能更快速的響應多變的市場需求。此時,用於提高業務復用及整合的分布式服務框架(RPC)是關鍵。

  • l流動計算架構

    當服務越來越多,容量的評估,小服務資源的浪費等問題逐漸顯現,此時需增加一個調度中心基於訪問壓力實時管理集群容量,提高集群利用率。此時,用於提高機器利用率的資源調度和治理中心(SOA)是關鍵。

十四、Docker

Docker是一種容器技術,

Docker 是一個開源的應用容器引擎,讓開發者可以打包他們的應用以及依賴包到一個可移植的鏡像中,然后發布到任何流行的 Linux或Windows 機器上,也可以實現虛擬化。容器是完全使用沙箱機制,相互之間不會有任何接口。

基礎

在這里插入圖片描述
在這里插入圖片描述

使用Docker的步驟

  1. 安裝Docker
  2. 去Docker倉庫中找到軟件對應的鏡像
  3. 使用Docker運行這個鏡像,這個鏡像就會產生一個Docker容器
  4. 對容器的啟動停止就是對軟件的啟動停止

安裝Docker

步驟:

  1. 檢查內核版本,3.10及以上
    1. uname -r
  2. 安裝docker
    1. yum install docker

docker常用指令

更換國內的源
安裝 epel
yum install -y epel-release
wget -O /etc/yum.repos.d/epel-7.repo http://mirrors.aliyun.com/repo/epel-7.repo
yum clean all
yum makecache

yum update //更新yum
yum install docker //安裝docker
systemctl enable docker //開機自動啟動docker
service docker start //開啟docker
service docker stop //停止docker
docker build -t python-test . 構造鏡像
docker run 創建並啟動一個容器,在run后面加上-d參數,則會創建一個守護式容器在后台運行。
docker ps -a 查看已經創建的容器
docker ps -s 查看已經啟動的容器
docker start con_name 啟動容器名為con_name的容器
docker stop con_name 停止容器名為con_name的容器
docker rm con_name 刪除容器名為con_name的容器
docker rm (docker images -q) 刪除所有鏡像
docker rename old_name new_name 重命名一個容器
docker attach con_name 將終端附着到正在運行的容器名為con_name的容器的終端上面去,前提是創建該容器時指定了相應的sh
執行這個命令后,按下回車鍵,會進入容器的命令行Shell中。
docker logs con_name 獲取容器名為con_name的容器日志
docker inspect 查看容器的詳細信息
docker top con_name 查看容器名為con_name的容器內部的進程
docker exec 可以用來在容器中運行一個進程
docker exec -it con_name /bin/bash //進入容器
docker run -di --name test -p 8000:8000 django-test映射端口


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM