1. 程式人生 > 實用技巧 >《大資料背景下大學生就業服務的資訊化建設研究》隨筆

《大資料背景下大學生就業服務的資訊化建設研究》隨筆

SpringBoot

Spring是為了解決企業級應用開發的複雜性而建立的,簡化開發。

Spring是如何簡化Java開發的

為了降低Java開發的複雜性,Spring採用了以下4種關鍵策略:

1、基於POJO的輕量級和最小侵入性程式設計,所有東西都是bean;

2、通過IOC,依賴注入(DI)和麵向介面實現鬆耦合;

3、基於切面(AOP)和慣例進行宣告式程式設計;

4、通過切面和模版減少樣式程式碼,RedisTemplate,xxxTemplate

SpringBoot約定大於配置的核心思想

建立專案方式

Spring官方提供了非常方便的工具讓我們快速構建應用

Spring Initializr:

https://start.spring.io/

專案建立方式一:使用Spring Initializr 的 Web頁面建立專案

1、開啟 https://start.spring.io/

2、填寫專案資訊

3、點選”Generate Project“按鈕生成專案;下載此專案

4、解壓專案包,並用IDEA以Maven專案匯入,一路下一步即可,直到專案匯入完畢。

5、如果是第一次使用,可能速度會比較慢,包比較多、需要耐心等待一切就緒。

專案建立方式二:使用 IDEA 直接建立專案

1、建立一個新專案

2、選擇spring initalizr , 可以看到預設就是去官網的快速構建工具那裡實現

3、填寫專案資訊

4、選擇初始化的元件(初學勾選 Web 即可)

5、填寫專案路徑

6、等待專案構建成功

專案結構分析:

通過上面步驟完成了基礎專案的建立。就會自動生成以下檔案。

1、程式的主啟動類

2、一個 application.properties 配置檔案

3、一個 測試類

4、一個 pom.xml

編寫一個http介面

1、在主程式的同級目錄下,新建一個controller包,一定要在同級目錄下,否則識別不到

2、在包中新建一個HelloController類

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

@RestController
public class HelloController {
    //介面  http://localhost:8080/hello
    @RequestMapping("/hello")
    public String hello() {
        //呼叫介面。接受前端引數
        return "Hello World";
    }
}

更改啟動時顯示的字元拼成的字母,SpringBoot呢?也就是 banner 圖案;

只需一步:到專案下的 resources 目錄下新建一個banner.txt 即可。

圖案可以到:https://www.bootschool.net/ascii 這個網站生成,然後拷貝到檔案中即可

專案結構

Spring Boot專案的 pom.xml 依賴:

父依賴

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

點進去,發現還有一個父依賴

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

這裡才是真正管理SpringBoot應用裡面所有依賴版本的地方,SpringBoot的版本控制中心

啟動器 spring-boot-starter

 <!-- web依賴  tomcat,dispacherServlet, xml -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

springboot-boot-starter-xxx:就是spring-boot的場景啟動器

spring-boot-starter-web:幫我們匯入了web模組正常執行所依賴的元件;

SpringBoot將所有的功能場景都抽取出來,做成一個個的starter (啟動器),只需要在專案中引入這些starter即可,所有相關的依賴都會匯入進來

springboot單元測試

<!-- springboot單元測試 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <!-- 剔除依賴 -->
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

剔除依賴

   <!-- 剔除依賴 -->
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>

打包外掛

    <build>
        <plugins>
            <!-- 打包外掛 -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

主啟動類

/@SpringBootApplication 來標註一個主程式類
//說明這是一個Spring Boot應用
@SpringBootApplication
public class SpringbootApplication {

   public static void main(String[] args) {
     //以為是啟動了一個方法,沒想到啟動了一個服務
      SpringApplication.run(SpringbootApplication.class, args);
   }

}

@SpringBootApplication

作用:標註在某個類上說明這個類是SpringBoot的主配置類 , SpringBoot就應該執行這個類的main方法來啟動SpringBoot應用

@ComponentScan

這個註解在Spring中很重要 ,它對應XML配置中的元素。

作用:自動掃描並載入符合條件的元件或者bean , 將這個bean定義載入到IOC容器中

@SpringBootConfiguration

作用:SpringBoot的配置類 ,標註在某個類上 , 表示這是一個SpringBoot的配置類;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}

這裡的 @Configuration,說明這是一個配置類 ,配置類就是對應Spring的xml 配置檔案;

裡面的 @Component 這就說明,啟動類本身也是Spring中的一個元件而已,負責啟動應用!

SpringApplication.run分析

分析該方法主要分兩部分,一部分是SpringApplication的例項化,二是run方法的執行;

SpringApplication

這個類主要做了以下四件事情:

1、推斷應用的型別是普通的專案還是Web專案

2、查詢並載入所有可用初始化器 , 設定到initializers屬性中

3、找出所有的應用程式監聽器,設定到listeners屬性中

4、推斷並設定main方法的定義類,找到執行的主類

配置檔案

SpringBoot使用一個全域性的配置檔案 , 配置檔名稱是固定的

  • application.properties

    • 語法結構 :key=value
  • application.yaml

    • 語法結構 :key:空格 value

yaml概述

YAML是 "YAML Ain't a Markup Language" (YAML不是一種標記語言)的遞迴縮寫。在開發的這種語言時,YAML 的意思其實是:"Yet Another Markup Language"(仍是一種標記語言)

這種語言以資料為中心,而不是以標記語言為重點!

以前的配置檔案,大多數都是使用xml來配置;比如一個簡單的埠配置,我們來對比下yaml和xml

傳統xml配置:

<server> 
   <port>8081<port>
</server>

yaml配置:

server:
  prot: 8080

yaml基礎語法

說明:語法要求嚴格!

1、空格不能省略

2、以縮排來控制層級關係,只要是左邊對齊的一列資料都是同一個層級的。

3、屬性和值的大小寫都是十分敏感的

字面量:普通的值 [ 數字,布林值,字串 ]

字面量直接寫在後面就可以 , 字串預設不用加上雙引號或者單引號;

k: v

注意:

  • “ ” 雙引號,不會轉義字串裡面的特殊字元 , 特殊字元會作為本身想表示的意思;

    比如 :name: "kuang \n shen" 輸出 :kuang 換行 shen

  • '' 單引號,會轉義特殊字元 , 特殊字元最終會變成和普通字元一樣輸出

    比如 :name: ‘kuang \n shen’ 輸出 :kuang \n shen

物件、Map(鍵值對)

#物件、Map格式k:     v1:    v2:

在下一行來寫物件的屬性和值得關係,注意縮排;比如:

student: 
   name: qinjiang    
    age: 3

行內寫法

student: {name: qinjiang,age: 3}

陣列( List、set )

用 - 值表示陣列中的一個元素,比如:

pets: 
  - cat
  - dog
  - pig

行內寫法

pets: [cat,dog,pig]

yaml注入配置檔案

spring原生註解方式

@Component  //註冊bean到容器中
public class Dog {
    @Value("阿黃")
    private String name;
    @Value("3")
    private Integer age;
    //有參無參構造、get、set方法、toString()方法  
}

在測試類下測試

@SpringBootTest
class SpringbootYamlApplicationTests {
    @Autowired //將狗狗自動注入進來
         private Dog dog;
    @Test
    void contextLoads() {
        System.out.println(dog);
    }

}

通過yaml賦值

@configurationProperties:預設從全域性配置檔案中獲取值;

/*
@ConfigurationProperties作用:
將配置檔案中配置的每一個屬性的值,對映到這個元件中;
告訴SpringBoot將本類中的所有屬性和配置檔案中相關的配置進行繫結
引數 prefix = “person” : 將配置檔案中的person下面的所有屬性一一對應
*/
@Component //註冊bean
@ConfigurationProperties(prefix = "person")
public class Person {
    private String name;
    private Integer age;
    private Boolean happy;
    private Date birth;
    private Map<String,Object> maps;
    private List<Object> lists;
    private Dog dog;
    //有參無參構造、get、set方法、toString()方法  
}

pom.xml

加了@ConfigurationProperties後會報紅,可以加入下面的maven依賴解決

https://docs.spring.io/spring-boot/docs/2.1.9.RELEASE/reference/html/configuration-metadata.html#configuration-metadata-annotation-processor

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

在測試類下測試

@Autowired //將preson自動注入進來
    private Person preson;
    @Test
    void contextLoads() {
        System.out.println(preson);
    }

指定配置檔案

新建一個xxx.prorerties的配置檔案

name=longpang

pojo:

@PropertySource :載入指定的配置檔案;

@Component //註冊bean
@PropertySource(value = "classpath:application.properties") //載入指定配置檔案
public class Person {
    //通過SPEL表示式獲取值
    @Value("${name}")
    private String name;
    //有參無參構造、get、set方法、toString()方法  
}

測試

@Autowired //將preson自動注入進來
    private Person preson;
    @Test
    void contextLoads() {
        System.out.println(preson);
    }

配置檔案佔位符

person:
  name: 龍胖不下鍋${random.uuid} # 隨機uuid
  age: ${random.int}  # 隨機int
  happy: true
  birth: 2000/1/1
  maps: {haha: vv,jj: hh}
  lists:
    - code
    - music
    - gril
  dog:
     name: ${person.heello:hoo}_旺財  #如果person.heello存在 則值為 person.heello的值_旺財  若不存在則為hoo_旺財
     age: 4

.properties和.yaml對比

@ConfigurationProperties @Value
功能 批量注入檔案中的屬性 一個一個指訂
鬆散繫結(雜湊語法) 支援 不支援
SPEL 不支援 支援
JSR303資料校驗 支援 不支援
複雜型別封裝 支援 不支援

1、@ConfigurationProperties只需要寫一次即可 , @Value則需要每個欄位都新增

2、鬆散繫結:這個什麼意思呢? 比如我的yml中寫的last-name,這個和lastName是一樣的, - 後面跟著的字母預設是大寫的。這就是鬆散繫結。可以測試一下

3、JSR303資料校驗 , 這個就是我們可以在欄位是增加一層過濾器驗證 , 可以保證資料的合法性

4、複雜型別封裝,yml中可以封裝物件 , 使用value就不支援

鬆散繫結

pojo

@Component
@ConfigurationProperties(prefix = "cat")
public class Cat {
    private String finallyName;
    private Integer age;
    //有參無參構造、get、set方法、toString()方法  
}    

application.yaml

Cat:
  finally-name: 加菲
  age: 99

測試

@Autowired //將cat自動注入進來
    private Cat cat;
    @Test
    void contextLoads() {
        System.out.println(cat);
    }
JSR303資料校驗

pom.xml

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

pojo

@Component
@ConfigurationProperties(prefix = "jsr303")
@Validated  //資料校驗
public class Jsr303 {
     @Email()
   // @Email(message = "郵箱格式錯誤")
    private String type; 
     //有參無參構造、get、set方法、toString()方法  
}    

application.yaml

Jsr303:
  type: 資料型別

測試

  @Autowired
    private Jsr303 jsr303;
    @Test
    void contextLoads() {
        System.out.println(jsr303);
    }

常見引數

@NotNull(message="名字不能為空")
private String userName;
@Max(value=120,message="年齡最大不能查過120")
private int age;
@Email(message="郵箱格式錯誤")
private String email;

空檢查
@Null       驗證物件是否為null
@NotNull    驗證物件是否不為null, 無法查檢長度為0的字串
@NotBlank   檢查約束字串是不是Null還有被Trim的長度是否大於0,只對字串,且會去掉前後空格.
@NotEmpty   檢查約束元素是否為NULL或者是EMPTY.
    
Booelan檢查
@AssertTrue     驗證 Boolean 物件是否為 true  
@AssertFalse    驗證 Boolean 物件是否為 false  
    
長度檢查
@Size(min=, max=) 驗證物件(Array,Collection,Map,String)長度是否在給定的範圍之內  
@Length(min=, max=) string is between min and max included.

日期檢查
@Past       驗證 Date 和 Calendar 物件是否在當前時間之前  
@Future     驗證 Date 和 Calendar 物件是否在當前時間之後  
@Pattern    驗證 String 物件是否符合正則表示式的規則

多環境配置及配置檔案位置

配置檔案載入位置

官方外部配置檔案說明參考文件:

https://docs.spring.io/spring-boot/docs/2.2.11.RELEASE/reference/html/spring-boot-features.html#boot-features-external-config

file:./config/     file指專案路徑  專案下的config檔案下 配置檔案

file:./                          專案下的 配置檔案例如:application.yaml
 
classpath:/config/               專案下的src的config檔案下 配置檔案  

classpath:/                      專案下的src下的resources或java下配置檔案

springboot 啟動會掃描以下位置的application.properties或者application.yml檔案作為Spring boot的預設配置檔案:

優先順序1:專案路徑下的config資料夾配置檔案
優先順序2:專案路徑下配置檔案
優先順序3:資源路徑下的config資料夾配置檔案
優先順序4:資源路徑下配置檔案

優先順序由高到底,高優先順序的配置會覆蓋低優先順序的配置;

多配置檔案

主配置檔案編寫的時候,檔名可以是 application-{profile}.properties/yml , 用來指定多個環境版本

application-test.properties 代表測試環境配置

server.port=8081

application-dev.properties 代表開發環境配置

server.port=8082

application.properties 預設使用application.properties主配置檔案


可以在預設application.properties 通過一個配置來選擇需要啟用的環境

# springboot的多環境配置,可以選擇啟用哪一個配置檔案
spring.profiles.active=dev

yaml的多文件塊

server:
  port: 8081
#選擇要啟用那個環境塊
spring:
  profiles:
    active: prod

---
server:
  port: 8083
spring:
  profiles: dev #配置環境的名稱


---

server:
  port: 8084
spring:
  profiles: prod  #配置環境的名稱

yml和properties同時都配置了埠, 預設會使用properties配置檔案的

自動配置原理

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

spring.factories:

org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
//表示這是一個配置類,和以前編寫的配置檔案一樣,也可以給容器中新增元件;
@Configuration 

//啟動指定類的ConfigurationProperties功能;
  //進入這個HttpProperties檢視,將配置檔案中對應的值和HttpProperties繫結起來;
  //並把HttpProperties加入到ioc容器中
@EnableConfigurationProperties({HttpProperties.class}) 

//Spring底層@Conditional註解
  //根據不同的條件判斷,如果滿足指定的條件,整個配置類裡面的配置就會生效;
  //這裡的意思就是判斷當前應用是否是web應用,如果是,當前配置類生效
@ConditionalOnWebApplication(
    type = Type.SERVLET
)

//判斷當前專案有沒有這個類CharacterEncodingFilter;SpringMVC中進行亂碼解決的過濾器;
@ConditionalOnClass({CharacterEncodingFilter.class})

//判斷配置檔案中是否存在某個配置:spring.http.encoding.enabled;
  //如果不存在,判斷也是成立的
  //即使我們配置檔案中不配置pring.http.encoding.enabled=true,也是預設生效的;
@ConditionalOnProperty(
    prefix = "spring.http.encoding",
    value = {"enabled"},
    matchIfMissing = true
)

public class HttpEncodingAutoConfiguration {
    //他已經和SpringBoot的配置檔案映射了
    private final Encoding properties;
    //只有一個有參構造器的情況下,引數的值就會從容器中拿
    public HttpEncodingAutoConfiguration(HttpProperties properties) {
        this.properties = properties.getEncoding();
    }
    
    //給容器中新增一個元件,這個元件的某些值需要從properties中獲取
    @Bean
    @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;
    }
    //。。。。。。。
}

根據當前不同的條件判斷,決定這個配置類是否生效!

  • 一但這個配置類生效;這個配置類就會給容器中新增各種元件;
  • 這些元件的屬性是從對應的properties類中獲取的,這些類裡面的每一個屬性又是和配置檔案繫結的
  • 所有在配置檔案中能配置的屬性都是在xxxxProperties類中封裝著
  • 配置檔案能配置什麼就可以參照某個功能對應的這個屬性類
//從配置檔案中獲取指定的值和bean的屬性進行繫結
@ConfigurationProperties(prefix = "spring.http") 
public class HttpProperties {
    // .....
}

精髓

1、SpringBoot啟動會載入大量的自動配置類

2、我們看我們需要的功能有沒有在SpringBoot預設寫好的自動配置類當中;

3、我們再來看這個自動配置類中到底配置了哪些元件;(只要我們要用的元件存在在其中,我們就不需要再手動配置了)

4、給容器中自動配置類新增元件的時候,會從XXXproperties類中獲取某些屬性。我們只需要在配置檔案中指定這些屬性的值即可;

xxxxAutoConfigurartion:自動配置類;給容器中新增元件

xxxxProperties:封裝配置檔案中相關屬性;

@Conditional

自動配置類必須在一定的條件下才能生效;

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

作用:必須是@Conditional指定的條件成立,才給容器中新增元件,配置配裡面的所有內容才生效

如何知道哪些自動配置類生效?

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

#開啟springboot的除錯類
debug=true

Positive matches:(自動配置類啟用的:正匹配)

Negative matches:(沒有啟動,沒有匹配成功的自動配置類:負匹配)

Unconditional classes: (沒有條件的類)

Web開發

Web開發靜態資源處理

靜態資源對映規則

  • ​ SpringBoot中,SpringMVC的web配置都在 WebMvcAutoConfiguration 這個配置類裡面;

    package org.springframework.boot.autoconfigure.web.servlet;  ---》》WebMvcAutoConfiguration
    

    有一個WebMvcAutoConfigurationAdapter

    	public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
    	//......
    	}
    

    有一個方法:addResourceHandlers 新增資源處理

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        //如果自己定義了靜態資源,直接return
        if (!this.resourceProperties.isAddMappings()) {
            // 已禁用預設資源處理
            logger.debug("Default resource handling disabled");
            return;
        }
        // 快取控制
        Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
        CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
        // webjars 配置
        if (!registry.hasMappingForPattern("/webjars/**")) {
            customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
                                                 .addResourceLocations("classpath:/META-INF/resources/webjars/")
                                                 .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
        }
        // 靜態資源(staticPathPattern)配置
        String staticPathPattern = this.mvcProperties.getStaticPathPattern();
        if (!registry.hasMappingForPattern(staticPathPattern)) {
            customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
                                                 .addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
                                                 .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
        }
    }
    

第一種靜態資源對映規則(webjars)

webjars官網:https://www.webjars.org/

原始碼:

    // webjars 配置
    if (!registry.hasMappingForPattern("/webjars/**")) {
        customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
                                             .addResourceLocations("classpath:/META-INF/resources/webjars/")
                                             .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
    }

pom.xm

  <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>jquery</artifactId>
            <version>3.5.1</version>
        </dependency>

只要是靜態資源,SpringBoot就會去對應的路徑尋找資源

請求路徑:http://localhost:8080/webjars/jquery/3.5.1/jquery.js

第二種靜態資源對映規則(staticPathPattern)

原始碼:

// 靜態資源(staticPathPattern)配置
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
			if (!registry.hasMappingForPattern(staticPathPattern)) {
				customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
						.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
						.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
			}
private String staticPathPattern = "/**";

staticPathPattern發現第二種對映規則 :/** , 訪問當前的專案任意資源,它會去找 resourceProperties 這個類

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

ResourceProperties 可以設定和我們靜態資源有關的引數;這裡面指向了它會去尋找資源的資料夾,即上面陣列的內容。

所以得出結論,以下四個目錄存放的靜態資源可以被我們識別:

"classpath:/META-INF/resources/"
"classpath:/resources/"
"classpath:/static/"
"classpath:/public/"

優先順序"classpath:/META-INF/resources/" > "classpath:/resources/" > "classpath:/static/" > "classpath:/public/"

可以在resources根目錄下新建對應的資料夾,都可以存放我們的靜態檔案 如在 "classpath:/resources/" 下建一個g.html

請求路徑:http://localhost:8080/g.html

自定義靜態資源路徑

原始碼:

  //如果自己定義了靜態資源,直接return
    if (!this.resourceProperties.isAddMappings()) {
        // 已禁用預設資源處理
        logger.debug("Default resource handling disabled");
        return;
    }

通過配置檔案來指定一下,哪些資料夾是需要我們放靜態資原始檔的,在application.properties中配置;

spring.resources.static-locations=classpath:/coding/,classpath:/it/

一旦自己定義了靜態資料夾的路徑,原來的自動配置就都會失效了!

首頁如何定製

@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
				FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
			WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
					new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),this.mvcProperties.getStaticPathPattern());//this.mvcProperties.getStaticPathPattern()自定義的
			welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
			welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations());
			return welcomePageHandlerMapping;
		}

		private Optional<Resource> getWelcomePage() {//獲得歡迎頁
            //this.resourceProperties.getStaticLocations() 靜態資源目錄
			String[] locations = getResourceLocations(this.resourceProperties.getStaticLocations());
			return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst();
		}

		private Resource getIndexHtml(String location) {
            //從靜態資源目錄下找個index.html頁面
			return this.resourceLoader.getResource(location + "index.html");
		}
"classpath:/resources/"
"classpath:/static/"
"classpath:/public/"
下定義index.html 訪問http://localhost:8080 可以以請求到
圖示定製
	public static class FaviconConfiguration implements ResourceLoaderAware {
	//。。。。
	}

關閉SpringBoot預設圖示

#關閉預設圖示
spring.mvc.favicon.enabled=false

2、自己放一個圖示在靜態資源目錄下,放在"classpath:/resources/" | "classpath:/static/" |"classpath:/public/" 目錄下

3、清除瀏覽器快取!重新整理網頁,發現圖示已經變成自己的了!

或者

新版本html中引入

<link rel="icon" href="favicon.ico">

訪問:http://localhost:8080/

模板引擎 (Thymeleaf)

模板引擎

  • 前端交給我們的頁面,是html頁面。如果是我們以前開發,我們需要把他們轉成jsp頁面,jsp好處就是當我們查出一些資料轉發到JSP頁面以後,我們可以用jsp輕鬆實現資料的顯示,及互動等。
  • jsp支援非常強大的功能,包括能寫Java程式碼,但是呢,我們現在的這種情況,SpringBoot這個專案首先是以jar的方式,不是war,像第二,我們用的還是嵌入式的Tomcat,所以呢,他現在預設是不支援jsp的

SpringBoot推薦你可以來使用模板引擎:

  • 模板引擎,我們其實大家聽到很多,其實jsp就是一個模板引擎,還有用的比較多的freemarker,包括SpringBoot給我們推薦的Thymeleaf,模板引擎有非常多,但再多的模板引擎,他們的思想都是一樣的

  • 模板引擎的作用就是我們來寫一個頁面模板,比如有些值呢,是動態的,我們寫一些表示式。而這些值,從哪來呢,就是我們在後臺封裝一些資料。然後把這個模板和這個資料交給我們模板引擎,模板引擎按照我們這個資料幫你把這表示式解析、填充到我們指定的位置,然後把這個資料最終生成一個我們想要的內容給我們寫出去,這就是我們這個模板引擎,不管是jsp還是其他模板引擎,都是這個思想。只不過呢,就是說不同模板引擎之間,他們可能這個語法有點不一樣。

  • Thymeleaf模板引擎,這模板引擎呢,是一個高階語言的模板引擎,他的這個語法更簡單。而且呢,功能更強大。

引入Thymeleaf

Thymeleaf 官網:https://www.thymeleaf.org/

Thymeleaf 在Github 的主頁:https://github.com/thymeleaf/thymeleaf

Spring官方文件:找到我們對應的版本

https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/reference/htmlsingle/#using-boot-starter

pom依賴:

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

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
		<dependency>
			<groupId>org.thymeleaf</groupId>
			<artifactId>thymeleaf-spring5</artifactId>
		</dependency>
		<dependency>
			<groupId>org.thymeleaf.extras</groupId>
			<artifactId>thymeleaf-extras-java8time</artifactId>
		</dependency>

Thymeleaf的自動配置類:ThymeleafProperties

@ConfigurationProperties(
    prefix = "spring.thymeleaf"
)
public class ThymeleafProperties {
    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;
}

可以在其中看到預設的字首和字尾!

html頁面放在類路徑下的templates下,dhothymeleaf就可以幫我們自動渲染了。

使用thymeleaf什麼都不需要配置,只需要將他放在指定的資料夾下即可!

測試:

colntroller:

//在templates目錄下的所有頁面只能通過contller來跳轉
@Controller
public class IndexController {
    @RequestMapping("/a")
    public String index(){
        return "index";
    }
}

新建一個 在類路徑下的templates下,controller中return的頁面,dhothymeleaf就可以幫我們自動渲染

訪問:http://localhost:8080/a

thymeleaf語法

Simple expressions:(表示式語法)
Variable Expressions: ${...}:獲取變數值;OGNL;
    1)、獲取物件的屬性、呼叫方法
    2)、使用內建的基本物件:#18
         #ctx : the context object.
         #vars: the context variables.
         #locale : the context locale.
         #request : (only in Web Contexts) the HttpServletRequest object.
         #response : (only in Web Contexts) the HttpServletResponse object.
         #session : (only in Web Contexts) the HttpSession object.
         #servletContext : (only in Web Contexts) the ServletContext object.

    3)、內建的一些工具物件:
      #execInfo : information about the template being processed.
      #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.
==================================================================================

  Selection Variable Expressions: *{...}:選擇表示式:和${}在功能上是一樣;
  Message Expressions: #{...}:獲取國際化內容
  Link URL Expressions: @{...}:定義URL;
  Fragment Expressions: ~{...}:片段引用表示式

Literals(字面量)
      Text literals: 'one text' , 'Another one!' ,…
      Number literals: 0 , 34 , 3.0 , 12.3 ,…
      Boolean literals: true , false
      Null literal: null
      Literal tokens: one , sometext , main ,…
      
Text operations:(文字操作)
    String concatenation: +
    Literal substitutions: |The name is ${name}|
    
Arithmetic operations:(數學運算)
    Binary operators: + , - , * , / , %
    Minus sign (unary operator): -
    
Boolean operations:(布林運算)
    Binary operators: and , or
    Boolean negation (unary operator): ! , not
    
Comparisons and equality:(比較運算)
    Comparators: > , < , >= , <= ( gt , lt , ge , le )
    Equality operators: == , != ( eq , ne )
    
Conditional operators:條件運算(三元運算子)
    If-then: (if) ? (then)
    If-then-else: (if) ? (then) : (else)
    Default: (value) ?: (defaultvalue)
    
Special tokens:
    No-Operation: _

測試:

//在templates目錄下的所有頁面只能通過contller來跳轉
@Controller
public class IndexController {
    @RequestMapping("/a")
    public String index(Model model){
        //存入資料
        model.addAttribute("msg","<h1>Hello</h1>");
        model.addAttribute("users", Arrays.asList("龍胖不下鍋","龍胖"));
        return "index";
    }
}

<!DOCTYPE html>
<html lang="en"  xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
 <link rel="icon" href="favicon.ico">
</head>
<body>
<!--不轉義-->
<div th:text="${msg}"></div>
<!--轉義-->
<div th:utext="${msg}"></div>

<!--遍歷資料-->
<!--th:each每次遍歷都會生成當前這個標籤:官網#9-->
<h4 th:each="user :${users}" th:text="${user}"></h4>

<h4>
    <!--行內寫法:官網#12-->
    <span th:each="user:${users}">[[${user}]]</span>
</h4>
</body>
</html>

MVC自動配置原理

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:

Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.

Support for serving static resources, including support for WebJars (covered later in this document)).

Automatic registration of Converter, GenericConverter, and Formatter beans.

Support for HttpMessageConverters (covered later in this document).

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).
If you want to keep those Spring Boot MVC customizations and make more MVC customizations (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc.

If you want to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver, and still keep the Spring Boot MVC customizations, you can declare a bean of type WebMvcRegistrations and use it to provide custom instances of those components.

If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc, or alternatively add your own @Configuration-annotated DelegatingWebMvcConfiguration as described in the Javadoc of @EnableWebMvc.

翻譯:

Spring Boot為Spring MVC提供了自動配置,可與大多數應用程式完美配合。

自動配置在Spring的預設值之上添加了以下功能:

包含ContentNegotiatingViewResolver和BeanNameViewResolver。

支援提供靜態資源,包括對WebJars的支援(在本文件的後面部分中有介紹)。

自動註冊Converter,GenericConverter和Formatter豆類。

支援HttpMessageConverters(在本文件後面介紹)。

自動註冊MessageCodesResolver(在本文件後面介紹)。

靜態index.html支援。

定製Favicon支援(在本文件後面部分中介紹)。

自動使用ConfigurableWebBindingInitializerbean(在本文件後面部分中介紹)。

如果要保留這些Spring Boot MVC定製並進行更多的MVC定製(攔截器,格式化程式,檢視控制器和其他功能),則可以新增自己@Configuration的type類,WebMvcConfigurer但不新增 @EnableWebMvc。

如果你想提供的定製情況RequestMappingHandlerMapping,RequestMappingHandlerAdapter或者ExceptionHandlerExceptionResolver,仍然保持彈簧引導MVC自定義,你可以宣告型別的豆WebMvcRegistrations,並用它來提供這些元件的定製例項。

如果你想利用Spring MVC中的完全控制,你可以新增自己的@Configuration註解為@EnableWebMvc,或者新增自己的@Configuration-annotatedDelegatingWebMvcConfiguration中的Javadoc中所述@EnableWebMvc。

ContentNegotiatingViewResolver 內容協商檢視解析器

自動配置了ViewResolver,就是我們之前學習的SpringMVC的檢視解析器;

即根據方法的返回值取得檢視物件(View),然後由檢視物件決定如何渲染(轉發,重定向)。

我們去看看這裡的原始碼:我們找到 WebMvcAutoConfiguration , 然後搜尋ContentNegotiatingViewResolver。找到如下方法!

@Bean
		@ConditionalOnBean(ViewResolver.class)
		@ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
		public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
			ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
			resolver.setContentNegotiationManager(beanFactory.getBean(ContentNegotiationManager.class));
			// ContentNegotiatingViewResolver uses all the other view resolvers to locate
			// a view so it should have a high precedence
              // ContentNegotiatingViewResolver使用所有其他檢視解析器來定位檢視,因此它應該具有較高的優先順序
			resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
			return resolver;
		}

點選ViewResolver.class,來到ViewResolver介面

public interface ViewResolver {

	@Nullable
	View resolveViewName(String viewName, Locale locale) throws Exception;

}

選擇實現ViewResolver介面的 ContentNegotiatingViewResolver

@Nullable // 註解說明:@Nullable 即引數可為null
public View resolveViewName(String viewName, Locale locale) throws Exception {
    RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
    Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
    List<MediaType> requestedMediaTypes = this.getMediaTypes(((ServletRequestAttributes)attrs).getRequest());
    if (requestedMediaTypes != null) {
        // 獲取候選的檢視物件
        List<View> candidateViews = this.getCandidateViews(viewName, locale, requestedMediaTypes);
        // 選擇一個最適合的檢視物件,然後把這個物件返回
        View bestView = this.getBestView(candidateViews, requestedMediaTypes, attrs);
        if (bestView != null) {
            return bestView;
        }
    }
    // .....
}

測試自定義:

//擴充套件springmvc
@Configuration
public class MySpringMvcCongig  implements WebMvcConfigurer {
    //WebMvcConfigurer是一個介面
    //ViewResolver實現了檢視解析器的類,就可以看作是檢視解析器

    @Bean //放到bean中
    public ViewResolver myViewResolver(){
        return new MyViewResolver();
    }
    //我們寫一個靜態內部類,檢視解析器就需要實現ViewResolver介面
    private static class MyViewResolver implements ViewResolver {
        @Override
        public View resolveViewName(String s, Locale locale) throws Exception {
            return null;
        }
    }
}

DispatcherServlet .java中的 doDispatch方法 加個斷點進行除錯一下,因為所有的請求都會走到這個方法中

	doDispatch(request, response);

我們如果想要使用自己定製化的東西,我們只需要給容器中新增這個元件就好了!剩下的事情SpringBoot就會幫我們做了!

轉換器和格式化器

WebMvcAutoConfiguration下的 格式化轉換器:

@Bean
@Override
public FormattingConversionService mvcConversionService() {
    // 拿到配置檔案中的格式化規則
    WebConversionService conversionService = 
        new WebConversionService(this.mvcProperties.getDateFormat());
    addFormatters(conversionService);
    return conversionService;
}

點選getDateFormat()

public String getDateFormat() { 
	return this.dateFormat;
}
/*** Date format to use. For instance, `dd/MM/yyyy`. 預設的 */
private String dateFormat;

在Properties檔案中,可以進行自動配置它

spring.mvc.date-format=

修改SpringBoot的預設配置

SpringBoot在自動配置很多元件的時候,先看容器中有沒有使用者自己配置的(如果使用者自己配置@bean),如果有就用使用者配置的,如果沒有就用自動配置的;

如果有些元件可以存在多個,比如我們的檢視解析器,就將使用者配置的和自己預設的組合起來!

擴充套件使用SpringMVC

//應為型別要求為WebMvcConfigurer,所以我們實現其介面
//可以使用自定義類擴充套件MVC的功能
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        // 瀏覽器傳送/test , 就會跳轉到test頁面;
        registry.addViewController("/test").setViewName("test");
    }
}

原理:

1、WebMvcAutoConfiguration 是 SpringMVC的自動配置類,裡面有一個類WebMvcAutoConfigurationAdapter

2、這個類上有一個註解,在做其他自動配置時會匯入:@Import(EnableWebMvcConfiguration.class)

3、我們點進EnableWebMvcConfiguration這個類看一下,它繼承了一個父類:DelegatingWebMvcConfiguration

public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
    private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
    
  // 從容器中獲取所有的webmvcConfigurer
    @Autowired(required = false)
    public void setConfigurers(List<WebMvcConfigurer> configurers) {
        if (!CollectionUtils.isEmpty(configurers)) {
            this.configurers.addWebMvcConfigurers(configurers);
        }
    }
}

WebMvcAutoConfiguration類中

@Configuration
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {
//。。
}

@ConditionalOnMissingBean({WebMvcConfigurationSupport.class}) 當這個WebMvcConfigurationSupport.class不存在會向下繼續執行。

然而@EnableWebMvc中

@Import(DelegatingWebMvcConfiguration.class)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
    //。。
}

因為DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport 所以不會向下繼續執行

所以官網中說的

If you want to keep those Spring Boot MVC customizations and make more MVC customizations (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc.
如果要保留這些Spring Boot MVC定製並進行更多的MVC定製(攔截器,格式化程式,檢視控制器和其他功能),則可以新增自己@Configuration的type類,WebMvcConfigurer但不新增 @EnableWebMvc。

頁面國際化

  • 首頁配置,注意所有的靜態資源介面用thymeleaf接管

  • 先在IDEA中統一設定properties的編碼問題!

  • 在resources資原始檔下新建一個i18n目錄,存放國際化配置檔案

    建立一個login.properties檔案,還有一個login_zh_CN.properties;login.properties

    idea左下角有個Resource Bundle 點選可以便捷輸入

    login.properties :預設

    login.btn=登陸~
    login.password=密碼~
    login.remember=記住我~
    login.tip=請登陸~
    login.username=使用者名稱~
    

    英文:

    login.btn=Sign In
    login.password=Password
    login.remember=Remember Me
    login.tip=Please sign in
    login.username=UserName
    

    中文:

    login.btn=登陸
    login.password=密碼
    login.remember=記住我
    login.tip=請登入
    login.username=使用者名稱
    

配置檔案生效探究

  • SpringBoot對國際化的自動配置!這裡又涉及到一個類:MessageSourceAutoConfiguration

  • SpringBoot已經自動配置好了管理我們國際化資原始檔的元件 ResourceBundleMessageSource;

    // 獲取 properties 傳遞過來的值進行判斷
    @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;
    }
    

配置頁面國際化值

去頁面獲取國際化的值,檢視Thymeleaf的文件,找到message取值操作為:#{...}

<h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign in</h1>
<button class="btn btn-lg btn-primary btn-block" type="submit" th:value="#{login.btn}">Sign in</button>

配置國際化解析

在Spring中有一個國際化的Locale (區域資訊物件);裡面有一個叫做LocaleResolver (獲取區域資訊物件)的解析器

AcceptHeaderLocaleResolver 這個類有一個方法resolveLocale

public Locale resolveLocale(HttpServletRequest request) {
    Locale defaultLocale = this.getDefaultLocale();
    // 預設的就是根據請求頭帶來的區域資訊獲取Locale進行國際化
    if (defaultLocale != null && request.getHeader("Accept-Language") == null) {
        return defaultLocale;
    } else {
        Locale requestLocale = request.getLocale();
        List<Locale> supportedLocales = this.getSupportedLocales();
        if (!supportedLocales.isEmpty() && !supportedLocales.contains(requestLocale)) {
            Locale supportedLocale = this.findSupportedLocale(request, supportedLocales);
            if (supportedLocale != null) {
                return supportedLocale;
            } else {
                return defaultLocale != null ? defaultLocale : requestLocale;
            }
        } else {
            return requestLocale;
        }
    }
}

WebMvcAutoConfiguration 檔案WebMvcAutoConfigurationAdapter中有一個localeResolver

@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
public LocaleResolver localeResolver() {
    // 容器中沒有就自己配,有的話就用使用者配置的
    if (this.mvcProperties.getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
        return new FixedLocaleResolver(this.mvcProperties.getLocale());
    }
    // 接收頭國際化分解
    AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
    localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
    return localeResolver;
}

現在想點選連結讓我們的國際化資源生效,就需要讓我們自己的Locale生效!

我們去自己寫一個自己的LocaleResolver,可以在連結上攜帶區域資訊!

修改一下前端頁面的跳轉連線:

<!-- 這裡傳入引數不需要使用 ?使用 (key=value)-->
<a class="btn btn-sm" th:href="@{/index.html(l='zh_CN')}">中文</a>
<a class="btn btn-sm" th:href="@{/index.html(l='en_US')}">English</a>

WebMvcAutoConfiguration 檔案WebMvcAutoConfigurationAdapter中有一個localeResolver

點選 AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();發現是實現了LocaleResolver

public class AcceptHeaderLocaleResolver implements LocaleResolver {

}

那麼類似於springmvc擴充套件。也寫一個

package com.it.myConfig;

import org.springframework.util.StringUtils;
import org.springframework.web.servlet.LocaleResolver;


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;

//可以在連結上攜帶區域資訊
public class MyLocaleResolver implements LocaleResolver {

    //解析請求
    @Override
    public Locale resolveLocale(HttpServletRequest request) {

        String language = request.getParameter("l");
        Locale locale = Locale.getDefault(); // 如果沒有獲取到就使用系統預設的
        //如果請求連結不為空
        if (!StringUtils.isEmpty(language)){
            //分割請求引數
            String[] split = language.split("_");
            //國家,地區
            locale = new Locale(split[0],split[1]);
        }
        return locale;
    }

    @Override
    public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {

    }
}

為了讓我們的區域化資訊能夠生效,我們需要再配置一下這個元件!在我們自己的MvcConofig下新增bean;

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index.html").setViewName("index");
    }
    /*為了讓我們的區域化資訊能夠生效,我們需要再配置一下這個元件!在我們自己的MvcConofig下新增bean;*/
    @Bean
    public LocaleResolver localeResolver(){
        return new MyLocaleResolver();
    }
}

測試

  1. 編寫實體類,模擬資料庫

  2. 國際化

  3. 登入+攔截

  4. 查詢

    提取公共頁面

    <div th:fragment="topbar"></div>
    <div th:replace="~{commons/commons::topbar}"></div>
    

    傳遞引數可以用()

    <div th:replace="~{commons/commons::sidebar(active='main.html')}"></div>
    

    列表展示

    <table class="table table-striped table-sm">
    							<thead>
    								<tr>
    									<th>id</th>
    									<th>lastName</th>
    									<th>email</th>
    									<th>gender</th>
    									<th>department</th>
    									<th>birth</th>
    									<th>操作</th>
    								</tr>
    							</thead>
    							<tbody>
    							<tr th:each="emp:${emps}">
    							<td th:text="${emp.getId()}"></td>
    								<td >[[${{emp.getLastName()}}]]</td>
    								<td th:text="${emp.getEmail()}"></td>
    								<td th:text="${emp.getGender()==0?'男':'女'}"></td>
    								<td th:text="${emp.getDepartment().getDepartmentName()}"></td>
    								<td th:text="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm:ss')}"></td>
    								<td>
    									<button class="btn btn-sm btn-primary" >編輯</button>
    									<button class="btn btn-sm btn-danger" >刪除</button>
    								</td>
    							</tr>
    							</tbody>
    						</table>
    

    新增員工

    1.按鈕提交

    2.跳轉到新增

    3.新增員工成功

    4.返回首頁

    #時間日期格式化
    spring.mvc.format.date=yyyy-MM-dd HH:mm:ss
    
    	<form th:action="@{/emp}" method="post">
    
    						<div class="form-group">
    							<label>LastName</label>
    							<input name="lastName" type="text" class="form-control" placeholder="zhangsan" >
    						</div>
    						<div class="form-group">
    							<label>Email</label>
    							<input name="email" type="email" class="form-control" placeholder="[email protected]" >
    						</div>
    						<div class="form-group">
    							<label>Gender</label><br/>
    							<div class="form-check form-check-inline">
    								<input class="form-check-input" type="radio" name="gender" value="1" >
    								<label class="form-check-label">男</label>
    							</div>
    							<div class="form-check form-check-inline">
    								<input class="form-check-input" type="radio" name="gender" value="0" >
    								<label class="form-check-label">女</label>
    							</div>
    						</div>
    						<div class="form-group">
    							<label>department</label>
    							<!--提交的是部門的id-->
    							<select class="form-control" name="department.id">
    								<option th:each="dept:${departments}" th:text="${dept.getDepartmentName()}" th:value="${dept.getId()}"></option>
    							</select>
    						</div>
    						<div class="form-group">
    							<label>Birth</label>
    							<input name="birth" type="text" class="form-control" placeholder="2020-11-11 14:50:33" >
    						</div>
    						<button type="submit" class="btn btn-primary">新增</button>
    					</form>
    
    	<!--需要區分是員工修改還是新增;-->
    					<form th:action="@{/emp}" method="post">
    						<!--傳送put請求修改員工資料-->
    						<!--
    						1、SpringMVC中配置HiddenHttpMethodFilter;(SpringBoot自動配置好的)
    						2、頁面建立一個post表單
    						3、建立一個input項,name="_method";值就是我們指定的請求方式
    						-->
    						<input type="hidden" name="_method" value="put" th:if="${emp!=null}"/>
    						<input type="hidden" name="id" th:if="${emp!=null}" th:value="${emp.getId()}">
    						<div class="form-group">
    							<label>LastName</label>
    							<input name="lastName" type="text" class="form-control" placeholder="zhangsan" th:value="${emp!=null}?${emp.lastName}">
    						</div>
    						<div class="form-group">
    							<label>Email</label>
    							<input name="email" type="email" class="form-control" placeholder="[email protected]" th:value="${emp!=null}?${emp.email}">
    						</div>
    						<div class="form-group">
    							<label>Gender</label><br/>
    							<div class="form-check form-check-inline">
    								<input class="form-check-input" type="radio" name="gender" value="1" th:checked="${emp!=null}?${emp.gender==1}">
    								<label class="form-check-label">男</label>
    							</div>
    							<div class="form-check form-check-inline">
    								<input class="form-check-input" type="radio" name="gender" value="0" th:checked="${emp!=null}?${emp.gender==0}">
    								<label class="form-check-label">女</label>
    							</div>
    						</div>
    						<div class="form-group">
    							<label>department</label>
    							<!--提交的是部門的id-->
    							<select class="form-control" name="department.id">
    								<option th:each="dept:${departments}" th:text="${dept.getDepartmentName()}" th:value="${dept.getId()}"></option>
    							</select>
    						</div>
    						<div class="form-group">
    							<label>Birth</label>
    							<input name="birth" type="text" class="form-control" placeholder="yyyy-MM-dd HH:mm" th:value="${emp!=null}?${#dates.format(emp.getBirth(), 'yyyy-MM-dd HH:mm:ss')}">
    						</div>
    						<button type="submit" class="btn btn-primary" th:text="${emp!=null}?'修改':'新增'">新增</button>
    					</form>
    
    

Data

對於資料訪問層,無論是 SQL(關係型資料庫) 還是 NOSQL(非關係型資料庫),Spring Boot 底層都是採用 Spring Data 的方式進行統一處理。

Spring Boot 底層都是採用 Spring Data 的方式進行統一處理各種資料庫,Spring Data 也是 Spring 中與 Spring Boot、Spring Cloud 等齊名的知名專案。

Sping Data 官網:https://spring.io/projects/spring-data

資料庫相關的啟動器 :可以參考官方文件:

https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/reference/htmlsingle/#using-boot-starter

pom.xml

 <!--JDBC-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <!--MYSQL驅動-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>

yaml

spring:
  datasource:
    username: root
    password: root
      #?serverTimezone=UTC解決時區的報錯
      #url: jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    url: jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver

測試

package com.it;

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

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

@SpringBootTest
class SpringbootDataApplicationTests {
    //DI注入資料來源
    @Autowired
    DataSource dataSource;
    @Test
    void contextLoads() throws SQLException {
        //看一下預設資料來源    class com.zaxxer.hikari.HikariDataSource
        System.out.println(dataSource.getClass());
        //獲得連線
        Connection connection = dataSource.getConnection();
        System.out.println(connection);
        //關閉連線
        connection.close();
    }

}

我們可以看到他預設給我們配置的資料來源為 : class com.zaxxer.hikari.HikariDataSource

找到資料來源的所有自動配置都在 :DataSourceAutoConfiguration檔案中

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {
//。。。
}

點選yaml中,發現原始碼在DataSourceProperties中,

@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {
//。。。。
}

而以前版本,如 Spring Boot 1.5 預設使用 org.apache.tomcat.jdbc.pool.DataSource 作為資料來源;

HikariDataSource 號稱 Java WEB 當前速度最快的資料來源,相比於傳統的 C3P0 、DBCP、Tomcat jdbc 等連線池更加優秀;

可以使用 spring.datasource.type 指定自定義的資料來源型別,值為 要使用的連線池實現的完全限定名。

整合JDBC

JDBCTemplate

1、有了資料來源(com.zaxxer.hikari.HikariDataSource),然後可以拿到資料庫連線(java.sql.Connection),有了連線,就可以使用原生的 JDBC 語句來操作資料庫;

2、即使不使用第三方第資料庫操作框架,如 MyBatis等,Spring 本身也對原生的JDBC 做了輕量級的封裝,即JdbcTemplate。

3、資料庫操作的所有 CRUD 方法都在 JdbcTemplate 中。

4、Spring Boot 不僅提供了預設的資料來源,同時預設已經配置好了 JdbcTemplate 放在了容器中,程式設計師只需自己注入即可使用

5、JdbcTemplate 的自動配置是依賴 org.springframework.boot.autoconfigure.jdbc 包下的 JdbcTemplateConfiguration 類

JdbcTemplate主要提供以下幾類方法:

  • execute方法:可以用於執行任何SQL語句,一般用於執行DDL語句;
  • update方法及batchUpdate方法:update方法用於執行新增、修改、刪除等語句;batchUpdate方法用於執行批處理相關語句;
  • query方法及queryForXXX方法:用於執行查詢相關語句;
  • call方法:用於執行儲存過程、函式相關語句。

測試

 <!--web依賴-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
DROP TABLE IF EXISTS `employee`;
CREATE TABLE `employee` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `lastName` varchar(255) DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  `gender` int(2) DEFAULT NULL,
  `d_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

DROP TABLE IF EXISTS `department`;
CREATE TABLE `department` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `departmentName` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
package com.it.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/jdbc")
public class JdbcController {
    /**
     * Spring Boot 預設提供了資料來源,預設提供了 org.springframework.jdbc.core.JdbcTemplate
     * JdbcTemplate 中會自己注入資料來源,用於簡化 JDBC操作
     * 還能避免一些常見的錯誤,使用起來也不用再自己來關閉資料庫連線
     */
    @Autowired
    JdbcTemplate jdbcTemplate;
    //查詢employee表中所有資料
    //List 中的1個 Map 對應資料庫的 1行資料
    //Map 中的 key 對應資料庫的欄位名,value 對應資料庫的欄位值
    @GetMapping("/list")
    public List<Map<String, Object>> userList(){
        String sql = "select * from employee";
        List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
        System.out.println(maps);
        return maps;
    }

    //新增一個使用者
    @GetMapping("/add")
    public String addUser(){
        //插入語句,注意時間問題
        String sql = "insert into employee(lastName, email,gender,d_id)" +
                " values ('龍胖不下鍋','[email protected]',1,101)";
        jdbcTemplate.update(sql);
        //查詢
        return "addOk";
    }

    //修改使用者資訊
    @GetMapping("/update/{id}")
    public String updateUser(@PathVariable("id") int id){
        //插入語句
        String sql = "update employee set lastName=?,email=? where id="+id;
        //資料
        Object[] objects = new Object[2];
        objects[0] = "pp";
        objects[1] = "[email protected]";
        jdbcTemplate.update(sql,objects);
        //查詢
        return "updateOk";
    }

    //刪除使用者
    @GetMapping("/delete/{id}")
    public String delUser(@PathVariable("id") int id){
        //插入語句
        String sql = "delete from employee where id=?";
        jdbcTemplate.update(sql,id);
        //查詢
        return "deleteOk";
    }

}

訪問:http://localhost:8080/jdbc/list 查詢

http://localhost:8080/jdbc/add 新增

http://localhost:8080/jdbc/update/2 修改

http://localhost:8080/jdbc/delete/2 刪除

整合Druid

Druid簡介

Java程式很大一部分要操作資料庫,為了提高效能操作資料庫的時候,又不得不使用資料庫連線池。

Druid 是阿里巴巴開源平臺上一個資料庫連線池實現,結合了 C3P0、DBCP 等 DB 池的優點,同時加入了日誌監控

Druid 可以很好的監控 DB 池連線和 SQL 的執行情況,天生就是針對監控而生的 DB 連線池。

Druid已經在阿里巴巴部署了超過600個應用,經過一年多生產環境大規模部署的嚴苛考驗。

Spring Boot 2.0 以上預設使用 Hikari 資料來源,可以說 Hikari 與 Driud 都是當前 Java Web 上最優秀的資料來源,我們來重點介紹 Spring Boot 如何整合 Druid 資料來源,如何實現資料庫監控。

Github地址:https://github.com/alibaba/druid/

com.alibaba.druid.pool.DruidDataSource 基本配置引數

配置 預設值 說明
name 配置這個屬性的意義在於,如果存在多個數據源,監控的時候可以通過名字來區分開來。 如果沒有配置,將會生成一個名字,格式是:"DataSource-" + System.identityHashCode(this)
jdbcUrl 連線資料庫的url,不同資料庫不一樣。例如: mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto
username 連線資料庫的使用者名稱
password 連線資料庫的密碼。如果你不希望密碼直接寫在配置檔案中,可以使用ConfigFilter。詳細看這裡:https://github.com/alibaba/druid/wiki/使用ConfigFilter
driverClassName 根據url自動識別 這一項可配可不配,如果不配置druid會根據url自動識別dbType,然後選擇相應的driverClassName(建議配置下)
initialSize 0 初始化時建立物理連線的個數。初始化發生在顯示呼叫init方法,或者第一次getConnection時
maxActive 8 最大連線池數量
maxIdle 8 已經不再使用,配置了也沒效果
minIdle 最小連線池數量
maxWait 獲取連線時最大等待時間,單位毫秒。配置了maxWait之後,預設啟用公平鎖,併發效率會有所下降,如果需要可以通過配置useUnfairLock屬性為true使用非公平鎖。
poolPreparedStatements false 是否快取preparedStatement,也就是PSCache。PSCache對支援遊標的資料庫效能提升巨大,比如說oracle。在mysql下建議關閉。
maxOpenPreparedStatements -1 要啟用PSCache,必須配置大於0,當大於0時,poolPreparedStatements自動觸發修改為true。在Druid中,不會存在Oracle下PSCache佔用記憶體過多的問題,可以把這個數值配置大一些,比如說100
validationQuery 用來檢測連線是否有效的sql,要求是一個查詢語句。如果validationQuery為null,testOnBorrow、testOnReturn、testWhileIdle都不會其作用。
testOnBorrow true 申請連線時執行validationQuery檢測連線是否有效,做了這個配置會降低效能。
testOnReturn false 歸還連線時執行validationQuery檢測連線是否有效,做了這個配置會降低效能
testWhileIdle false 建議配置為true,不影響效能,並且保證安全性。申請連線的時候檢測,如果空閒時間大於timeBetweenEvictionRunsMillis,執行validationQuery檢測連線是否有效。
timeBetweenEvictionRunsMillis 有兩個含義: 1) Destroy執行緒會檢測連線的間隔時間2) testWhileIdle的判斷依據,詳細看testWhileIdle屬性的說明
numTestsPerEvictionRun 不再使用,一個DruidDataSource只支援一個EvictionRun
minEvictableIdleTimeMillis
connectionInitSqls 物理連線初始化的時候執行的sql
exceptionSorter 根據dbType自動識別 當資料庫丟擲一些不可恢復的異常時,拋棄連線
filters 屬性型別是字串,通過別名的方式配置擴充套件外掛,常用的外掛有: 監控統計用的filter:stat日誌用的filter:log4j防禦sql注入的filter:wall
proxyFilters 型別是List<com.alibaba.druid.filter.Filter>,如果同時配置了filters和proxyFilters,是組合關係,並非替換關係

配置資料來源

  1. 新增上 Druid 資料來源依賴

    <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.2.3</version>
    </dependency>
    
  2. 切換資料來源;之前已經說過 Spring Boot 2.0 以上預設使用 com.zaxxer.hikari.HikariDataSource 資料來源,但可以 通過 spring.datasource.type 指定資料來源。

    spring:
      datasource:
        username: root
        password: root
        #?serverTimezone=UTC解決時區的報錯
        url: jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
        driver-class-name: com.mysql.cj.jdbc.Driver
        type: com.alibaba.druid.pool.DruidDataSource
    
        #Spring Boot 預設是不注入這些屬性值的,需要自己繫結
        #druid 資料來源專有配置
        initialSize: 5
        minIdle: 5
        maxActive: 20
        maxWait: 60000
        timeBetweenEvictionRunsMillis: 60000
        minEvictableIdleTimeMillis: 300000
        validationQuery: SELECT 1 FROM DUAL
        testWhileIdle: true
        testOnBorrow: false
        testOnReturn: false
        poolPreparedStatements: true
    
        #配置監控統計攔截的filters,stat:監控統計、log4j:日誌記錄、wall:防禦sql注入
        #如果允許時報錯  java.lang.ClassNotFoundException: org.apache.log4j.Priority
        #則匯入 log4j 依賴即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
        filters: stat,wall,log4j
        maxPoolPreparedStatementPerConnectionSize: 20
        useGlobalDataSourceStat: true
        connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
    
  3. 匯入Log4j 的依賴

    <!-- https://mvnrepository.com/artifact/log4j/log4j -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    
  4. 現在需要程式設計師自己為 DruidDataSource 繫結全域性配置檔案中的引數,再新增到容器中,而不再使用 Spring Boot 的自動生成了;我們需要 自己新增 DruidDataSource 元件到容器中,並繫結屬性;

    package com.it;
    
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    
    import javax.sql.DataSource;
    import java.sql.Connection;
    import java.sql.SQLException;
    
    @SpringBootTest
    class SpringbootDataApplicationTests {
        //DI注入資料來源
        @Autowired
        DataSource dataSource;
        @Test
        void contextLoads() throws SQLException {
            //看一下預設資料來源    class com.alibaba.druid.pool.DruidDataSource
            System.out.println(dataSource.getClass());
            //獲得連線
            Connection connection = dataSource.getConnection();
            System.out.println(connection);
    
            //關閉連線
            connection.close();
        }
    
    }
    
    
  5. 測試

    package com.it;
    
    import com.alibaba.druid.pool.DruidDataSource;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    
    import javax.sql.DataSource;
    import java.sql.Connection;
    import java.sql.SQLException;
    
    @SpringBootTest
    class SpringbootDataApplicationTests {
        //DI注入資料來源
        @Autowired
        DataSource dataSource;
    
        @Test
        void contextLoads() throws SQLException {
            //看一下預設資料來源    class com.alibaba.druid.pool.DruidDataSource
            System.out.println(dataSource.getClass());
            //獲得連線
            Connection connection = dataSource.getConnection();
            System.out.println(connection);
            DruidDataSource druidDataSource = (DruidDataSource) dataSource;
            System.out.println("druidDataSource 資料來源最大連線數:" + druidDataSource.getMaxActive());
            System.out.println("druidDataSource 資料來源初始化連線數:" + druidDataSource.getInitialSize());
           /*druidDataSource 資料來源最大連線數:20
              druidDataSource 資料來源初始化連線數:5*/
            //關閉連線
            connection.close();
        }
    
    }
    
    
    
  6. crud

    訪問:http://localhost:8080/jdbc/list 查詢

    http://localhost:8080/jdbc/add 新增

    http://localhost:8080/jdbc/update/2 修改

    http://localhost:8080/jdbc/delete/2 刪除

  7. 配置Druid資料來源監控

    package com.it.config;
    
    import com.alibaba.druid.pool.DruidDataSource;
    import com.alibaba.druid.support.http.StatViewServlet;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.boot.web.servlet.ServletRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import javax.sql.DataSource;
    import java.util.HashMap;
    import java.util.Map;
    
    @Configuration
    public class DruidConfig {
        /*
           將自定義的 Druid資料來源新增到容器中,不再讓 Spring Boot 自動建立
           繫結全域性配置檔案中的 druid 資料來源屬性到 com.alibaba.druid.pool.DruidDataSource從而讓它們生效
           @ConfigurationProperties(prefix = "spring.datasource"):作用就是將 全域性配置檔案中
           字首為 spring.datasource的屬性值注入到 com.alibaba.druid.pool.DruidDataSource 的同名引數中
         */
        @ConfigurationProperties(prefix = "spring.datasource")
        @Bean
        public DataSource druidDataSource() {
            return new DruidDataSource();
        }
    
        //配置 Druid 監控管理後臺的Servlet;
    //內建 Servlet 容器時沒有web.xml檔案,所以使用 Spring Boot 的註冊 Servlet 方式
        @Bean
        public ServletRegistrationBean statViewServlet() {
            ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
    
            // 這些引數可以在 com.alibaba.druid.support.http.StatViewServlet
            // 的父類 com.alibaba.druid.support.http.ResourceServlet 中找到
            Map<String, String> initParams = new HashMap<>();
            initParams.put("loginUsername", "admin"); //後臺管理介面的登入賬號
            initParams.put("loginPassword", "123456"); //後臺管理介面的登入密碼
    
            //後臺允許誰可以訪問
            //initParams.put("allow", "localhost"):表示只有本機可以訪問
            //initParams.put("allow", ""):為空或者為null時,表示允許所有訪問
            initParams.put("allow", "");
            //deny:Druid 後臺拒絕誰訪問
            //initParams.put("kuangshen", "192.168.1.20");表示禁止此ip訪問
    
            //設定初始化引數
            bean.setInitParameters(initParams);
            return bean;
        }
    
    }
    
    

    配置完畢後,我們可以選擇訪問 :http://localhost:8080/druid/login.html

  8. 配置 Druid web 監控 filter 過濾器

    package com.it.config;
    
    import com.alibaba.druid.pool.DruidDataSource;
    import com.alibaba.druid.support.http.StatViewServlet;
    import com.alibaba.druid.support.http.WebStatFilter;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.boot.web.servlet.ServletRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import javax.sql.DataSource;
    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.Map;
    
    @Configuration
    public class DruidConfig {
        /*
           將自定義的 Druid資料來源新增到容器中,不再讓 Spring Boot 自動建立
           繫結全域性配置檔案中的 druid 資料來源屬性到 com.alibaba.druid.pool.DruidDataSource從而讓它們生效
           @ConfigurationProperties(prefix = "spring.datasource"):作用就是將 全域性配置檔案中
           字首為 spring.datasource的屬性值注入到 com.alibaba.druid.pool.DruidDataSource 的同名引數中
         */
        @ConfigurationProperties(prefix = "spring.datasource")
        @Bean
        public DataSource druidDataSource() {
            return new DruidDataSource();
        }
    
        //配置 Druid 監控管理後臺的Servlet;
    //內建 Servlet 容器時沒有web.xml檔案,所以使用 Spring Boot 的註冊 Servlet 方式
        @Bean
        public ServletRegistrationBean statViewServlet() {
            ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
    
            // 這些引數可以在 com.alibaba.druid.support.http.StatViewServlet
            // 的父類 com.alibaba.druid.support.http.ResourceServlet 中找到
            Map<String, String> initParams = new HashMap<>();
            initParams.put("loginUsername", "admin"); //後臺管理介面的登入賬號
            initParams.put("loginPassword", "123456"); //後臺管理介面的登入密碼
    
            //後臺允許誰可以訪問
            //initParams.put("allow", "localhost"):表示只有本機可以訪問
            //initParams.put("allow", ""):為空或者為null時,表示允許所有訪問
            initParams.put("allow", "");
            //deny:Druid 後臺拒絕誰訪問
            //initParams.put("kuangshen", "192.168.1.20");表示禁止此ip訪問
    
            //設定初始化引數
            bean.setInitParameters(initParams);
            return bean;
        }
        //配置 Druid 監控 之  web 監控的 filter
    //WebStatFilter:用於配置Web和Druid資料來源之間的管理關聯監控統計
        @Bean
        public FilterRegistrationBean webStatFilter() {
            FilterRegistrationBean bean = new FilterRegistrationBean();
            /*設定過濾器*/
            bean.setFilter(new WebStatFilter());
    
            //exclusions:設定哪些請求進行過濾排除掉,從而不進行統計
            Map<String, String> initParams = new HashMap<>();
            initParams.put("exclusions", "*.js,*.css,/druid/*,/jdbc/*");
            bean.setInitParameters(initParams);
    
            //"/*" 表示過濾所有請求
            bean.setUrlPatterns(Arrays.asList("/*"));
            return bean;
        }
    }
    
    

整合MyBatis

官方文件:http://mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/

Maven倉庫地址:https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter/2.1.1

mybatis-spring-boot-starter文件: http://mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/

測試

匯入 MyBatis 所需要的依賴

<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.3</version>
</dependency>

配置資料庫連線資訊

spring:
  datasource:
    username: root
    password: root
    #?serverTimezone=UTC解決時區的報錯
    url: jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

    #Spring Boot 預設是不注入這些屬性值的,需要自己繫結
    #druid 資料來源專有配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true

    #配置監控統計攔截的filters,stat:監控統計、log4j:日誌記錄、wall:防禦sql注入
    #如果允許時報錯  java.lang.ClassNotFoundException: org.apache.log4j.Priority
    #則匯入 log4j 依賴即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

#整合mybatis
mybatis:
  type-aliases-package: com.it.pojo
  mapper-locations: classpath:mybatis/mapper/*.xml

實體類

package com.it.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Department {

    private Integer id;
    private String departmentName;
}

Mapper 介面

package com.it.mapper;

import com.it.pojo.Department;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

import java.util.List;

/*@Mapper : 表示本類是一個 MyBatis 的 Mapper
* 或者在主啟動類下加@MapperScan("包路徑")掃描包,加了後接口被掃描進去*/
@Mapper
@Repository
public interface DepartmentMapper {

    // 獲取所有部門資訊
    List<Department> getDepartments();

    // 通過id獲得部門
    Department getDepartment(Integer id);

}

DepartmentMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.it.mapper.DepartmentMapper">

    <select id="getDepartments" resultType="department">
       select * from department;
    </select>

    <select id="getDepartment" resultType="department" parameterType="int">
       select * from department where id = #{id};
    </select>

</mapper>

Controller

DepartmentController.java

package com.it.controller;

import com.it.mapper.DepartmentMapper;
import com.it.pojo.Department;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class DepartmentController {

    @Autowired
    DepartmentMapper departmentMapper;

    // 查詢全部部門
    @GetMapping("/getDepartments")
    public List<Department> getDepartments(){
        return departmentMapper.getDepartments();
    }

    // 查詢全部部門
    @GetMapping("/getDepartment/{id}")
    public Department getDepartment(@PathVariable("id") Integer id){
        return departmentMapper.getDepartment(id);
    }

}

maven配置資源過濾問題

<resources>
    <resource>
        <directory>src/main/java</directory>
        <includes>
            <include>**/*.xml</include>
        </includes>
        <filtering>true</filtering>
    </resource>
</resources>

訪問:

http://localhost:8080/getDepartment/101 根據id查詢

http://localhost:8080/getDepartments 查詢