狂神說spring-boot系列一_基礎篇


注解和反射

springboot 是基於注解的框架,因此先復習一下注解,了解注解如何通過反射獲取注解的值、

 

 

package Reflection;

import java.lang.annotation.*;
import java.lang.reflect.Field;

/**
 * author liulei
 * data  5.22
 * since 1.8
 * version 1.0
 * Description  反射獲取注解
 */
public class Test10 {
    public static void main(String[] args) throws Exception {
        Class aClass = Class.forName("Reflection.student2");
        Annotation[] annotations = aClass.getAnnotations();
        for (Annotation a:annotations){
            System.out.println(a);//屬性是私有的,所以沒有輸出屬性的注解
        }
        //獲得注解的value的值
        table t = (table) aClass.getAnnotation(table.class);
        String value = t.value();
        System.out.println(value);
        //獲得類指定的注解
        Field name = aClass.getDeclaredField("name");
        zidingyi annotation = name.getAnnotation(zidingyi.class);
        System.out.println(annotation.columnName());
        System.out.println(annotation.type());
        System.out.println(annotation.length());
    }
}
@table("db_student")
class student2{
    @zidingyi(columnName = "db_id",type = "int",length = 10)
    private int id;
    @zidingyi(columnName = "db_id",type = "int",length = 10)
    private int age;
    @zidingyi(columnName = "db_id",type = "int",length = 10)
    private String name;

    public student2() {
    }

    public student2(int id, int age, String name) {
        this.id = id;
        this.age = age;
        this.name = name;
    }

    public void setId(int id) {
        this.id = id;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

    public int getId() {
        return id;
    }

    public int getAge() {
        return age;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "student2{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface zidingyi{
    String columnName();
    String type();
    int length();
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface table{
    String value();
}

 Class.getAnnotations() 獲取所有的注解,包括自己聲明的以及繼承的
Class.getAnnotation(Class< A > annotationClass) 獲取指定的注解,該注解可以是自己聲明的,也可以是繼承的
Class.getDeclaredAnnotations() 獲取自己聲明的注解

springboot簡介

回顧什么是Spring

Spring是一個開源框架,2003 年興起的一個輕量級的Java 開發框架,作者:Rod Johnson  。

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

 

Spring是如何簡化Java開發的

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

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

2、通過IOC,依賴注入(DI)和面向接口實現松耦合;

3、基於切面(AOP)和慣例進行聲明式編程;

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

什么是SpringBoot

學過javaweb的同學就知道,開發一個web應用,從最初開始接觸Servlet結合Tomcat, 跑出一個Hello Wolrld程序,是要經歷特別多的步驟;后來就用了框架Struts,再后來是SpringMVC,到了現在的SpringBoot,過一兩年又會有其他web框架出現;你們有經歷過框架不斷的演進,然后自己開發項目所有的技術也不斷的變化、改造嗎?建議都可以去經歷一遍;言歸正傳,什么是SpringBoot呢,就是一個javaweb的開發框架,和SpringMVC類似,對比其他javaweb框架的好處,官方說是簡化開發,約定大於配置,  you can "just run",能迅速的開發web應用,幾行代碼開發一個http接口。所有的技術框架的發展似乎都遵循了一條主線規律:從一個復雜應用場景 衍生 一種規范框架,人們只需要進行各種配置而不需要自己去實現它,這時候強大的配置功能成了優點;發展到一定程度之后,人們根據實際生產應用情況,選取其中實用功能和設計精華,重構出一些輕量級的框架;之后為了提高開發效率,嫌棄原先的各類配置過於麻煩,於是開始提倡“約定大於配置”,進而衍生出一些一站式的解決方案。是的這就是Java企業級應用->J2EE->spring->springboot的過程。隨着 Spring 不斷的發展,涉及的領域越來越多,項目整合開發需要配合各種各樣的文件,慢慢變得不那么易用簡單,違背了最初的理念,甚至人稱配置地獄。Spring Boot 正是在這樣的一個背景下被抽象出來的開發框架,目的為了讓大家更容易的使用 Spring 、更容易的集成各種常用的中間件、開源軟件;Spring Boot 基於 Spring 開發,Spirng Boot 本身並不提供 Spring 框架的核心特性以及擴展功能,只是用於快速、敏捷地開發新一代基於 Spring 框架的應用程序。也就是說,它並不是用來替代 Spring 的解決方案,而是和 Spring 框架緊密結合用於提升 Spring 開發者體驗的工具。Spring Boot 以約定大於配置的核心思想,默認幫我們進行了很多設置,多數 Spring Boot 應用只需要很少的 Spring 配置。同時它集成了大量常用的第三方庫配置(例如 Redis、MongoDB、Jpa、RabbitMQ、Quartz 等等),Spring Boot 應用中這些第三方庫幾乎可以零配置的開箱即用。簡單來說就是SpringBoot其實不是什么新的框架,它默認配置了很多框架的使用方式,就像maven整合了所有的jar包,spring boot整合了所有的框架 。Spring Boot 出生名門,從一開始就站在一個比較高的起點,又經過這幾年的發展,生態足夠完善,Spring Boot 已經當之無愧成為 Java 領域最熱門的技術。

Spring Boot的主要優點:

  • 為所有Spring開發者更快的入門

  • 開箱即用,提供各種默認配置來簡化項目配置

  • 內嵌式容器簡化Web項目

  • 沒有冗余代碼生成和XML配置的要求

真的很爽,我們快速去體驗開發個接口的感覺吧!



Hello,World

 

准備工作

我們將學習如何快速的創建一個Spring Boot應用,並且實現一個簡單的Http請求處理。通過這個例子對Spring Boot有一個初步的了解,並體驗其結構簡單、開發快速的特性。

我的環境准備:

  • java version "1.8.0_181"

  • Maven-3.6.1

  • SpringBoot 2.x 最新版

開發工具:

  • IDEA

     

創建基礎項目說明

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

pom.xml 分析

打開pom.xml,看看Spring Boot項目的依賴:

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

<dependencies>
    <!-- web場景啟動器 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- 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>
</dependencies>

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

編寫一個http接口

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

2、在包中新建一個HelloController類

@RestController
public class HelloController {

    @RequestMapping("/hello")
    public String hello() {
        return "Hello World";
    }
    
}

3、編寫完畢后,從主程序啟動項目,瀏覽器發起請求,看頁面返回;控制台輸出了 Tomcat 訪問的端口號!

 

 

 

 

 

簡單幾步,就完成了一個web接口的開發,SpringBoot就是這么簡單。所以我們常用它來建立我們的微服務項目!

 

將項目打成jar包,點擊 maven的 package

 

 

 

如果遇到以上錯誤,可以配置打包時 跳過項目運行測試用例

<!--
    在工作中,很多情況下我們打包是不想執行測試用例的
    可能是測試用例不完事,或是測試用例會影響數據庫數據
    跳過測試用例執
    -->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
        <!--跳過項目運行測試用例-->
        <skipTests>true</skipTests>
    </configuration>
</plugin>

如果打包成功,則會在target目錄下生成一個 jar 包

 

 

 

打成了jar包后,就可以在任何地方運行了!OK

 

彩蛋

 

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

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

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

 

 

 

yaml配置注入

 

配置文件

 

SpringBoot使用一個全局的配置文件 , 配置文件名稱是固定的

 

  • application.properties

    • 語法結構 :key=value

  • application.yml

    • 語法結構 :key:空格 value

 

配置文件的作用 :修改SpringBoot自動配置的默認值,因為SpringBoot在底層都給我們自動配置好了;

 

比如我們可以在配置文件中修改Tomcat 默認啟動的端口號!測試一下!

 

server.port=8081

 

 

 

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]

修改SpringBoot的默認端口號

配置文件中添加,端口號的參數,就可以切換端口;

server: port: 8082


yaml文件更強大的地方在於,他可以給我們的實體類直接注入匹配值!

yaml注入配置文件

1、在springboot項目中的resources目錄下新建一個文件 application.yml

2、編寫一個實體類 Dog;

package com.kuang.springboot.pojo;

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

3、思考,我們原來是如何給bean注入屬性值的!@Value,給狗狗類測試一下:

@Component //注冊bean
public class Dog {
    @Value("阿黃")
    private String name;
    @Value("18")
    private Integer age;
}

4、在SpringBoot的測試類下注入狗狗輸出一下

@SpringBootTest
class DemoApplicationTests {

    @Autowired //將狗狗自動注入進來
    Dog dog;

    @Test
    public void contextLoads() {
        System.out.println(dog); //打印看下狗狗對象
    }

}

結果成功輸出,@Value注入成功,這是我們原來的辦法對吧。

 

 

 

5、我們在編寫一個復雜一點的實體類:Person 類

@Component //注冊bean到容器中
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()方法  
}

6、我們來使用yaml配置的方式進行注入,大家寫的時候注意區別和優勢,我們編寫一個yaml配置!

person:
  name: qinjiang
  age: 3
  happy: false
  birth: 2000/01/01
  maps: {k1: v1,k2: v2}
  lists:
   - code
   - girl
   - music
  dog:
    name: 旺財
    age: 1

7、我們剛才已經把person這個對象的所有值都寫好了,我們現在來注入到我們的類中!

/*
@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;
}

8、IDEA 提示,springboot配置注解處理器沒有找到,讓我們看文檔,我們可以查看文檔,找到一個依賴!

 

 

 

 

 

 

<!-- 導入配置文件處理器,配置文件進行綁定就會有提示,需要重啟 -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-configuration-processor</artifactId>
  <optional>true</optional>
</dependency>

9、確認以上配置都OK之后,我們去測試類中測試一下:

@SpringBootTest
class DemoApplicationTests {

    @Autowired
    Person person; //將person自動注入進來

    @Test
    public void contextLoads() {
        System.out.println(person); //打印person信息
    }

}

結果:所有值全部注入成功!

 

 

 

yaml配置注入到實體類完全OK!

課堂測試:

1、將配置文件的key 值 和 屬性的值設置為不一樣,則結果輸出為null,注入失敗

2、在配置一個person2,然后將 @ConfigurationProperties(prefix = "person2") 指向我們的person2;

加載指定的配置文件

@PropertySource :加載指定的配置文件;

@configurationProperties:默認從全局配置文件中獲取值;

1、我們去在resources目錄下新建一個person.properties文件

name=kuangshen

2、然后在我們的代碼中指定加載person.properties文件

@PropertySource(value = "classpath:person.properties")
@Component //注冊bean
public class Person {

    @Value("${name}")
    private String name;

    ......  
}

 

 

 

配置文件占位符

配置文件還可以編寫占位符生成隨機數

person:
    name: qinjiang${random.uuid} # 隨機uuid
    age: ${random.int}  # 隨機int
    happy: false
    birth: 2000/01/01
    maps: {k1: v1,k2: v2}
    lists:
      - code
      - girl
      - music
    dog:
      name: ${person.hello:other}_旺財
      age: 1

回顧properties配置

我們上面采用的yaml方法都是最簡單的方式,開發中最常用的;也是springboot所推薦的!那我們來嘮嘮其他的實現方式,道理都是相同的;寫還是那樣寫;配置文件除了yml還有我們之前常用的properties , 我們沒有講,我們來嘮嘮!

【注意】properties配置文件在寫中文的時候,會有亂碼 , 我們需要去IDEA中設置編碼格式為UTF-8;

settings-->FileEncodings 中配置;

 

 

 

測試步驟:

1、新建一個實體類User

@Component //注冊beanpublic class User { private String name; private int age; private String sex;}

2、編輯配置文件 user.properties

user1.name=kuangshenuser1.age=18user1.sex=男

3、我們在User類上使用@Value來進行注入!

@Component //注冊bean@PropertySource(value = "classpath:user.properties")public class User { //直接使用@value @Value("${user.name}") //從配置文件中取值 private String name; @Value("#{9*2}") // #{SPEL} Spring表達式 private int age; @Value("男") // 字面量 private String sex;}

4、Springboot測試

@SpringBootTestclass DemoApplicationTests {
@Autowired User user;
@Test public void contextLoads() { System.out.println(user); }
}

結果正常輸出:

 

 

 

對比小結

@Value這個使用起來並不友好!我們需要為每個屬性單獨注解賦值,比較麻煩;我們來看個功能對比圖

 

 

 

1、@ConfigurationProperties只需要寫一次即可 , @Value則需要每個字段都添加

2、松散綁定:這個什么意思呢? 比如我的yml中寫的last_name,這個和lastName是一樣的, - 后面跟着的字母默認是大寫的。這就是松散綁定。可以測試一下

3、JSR303數據校驗 , 這個就是我們可以在字段是增加一層過濾器驗證 , 可以保證數據的合法性

4、復雜類型封裝,yml中可以封裝對象 , 使用value就不支持

結論:

配置yml和配置properties都可以獲取到值 , 強烈推薦 yml;

如果我們在某個業務中,只需要獲取配置文件中的某個值,可以使用一下 @value;

如果說,我們專門編寫了一個JavaBean來和配置文件進行一一映射,就直接@configurationProperties,不要猶豫!

JSR303數據校驗及多環境切換

JSR303數據校驗

 

先看看如何使用

Springboot中可以用@validated來校驗數據,如果數據異常則會統一拋出異常,方便異常中心統一處理。我們這里來寫個注解讓我們的name只能支持Email格式;

@Component //注冊bean
@ConfigurationProperties(prefix = "person")
@Validated  //數據校驗
public class Person {

    @Email(message="郵箱格式錯誤") //name必須是郵箱格式
    private String name;
}

運行結果 :default message [不是一個合法的電子郵件地址];

 

 

使用數據校驗,可以保證數據的正確性;

引入注解

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

常見參數

 

@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 對象是否符合正則表達式的規則

.......等等
除此以外,我們還可以自定義一些數據校驗規則


多環境切換

profile是Spring對不同環境提供不同配置功能的支持,可以通過激活不同的環境版本,實現快速切換環境;

多配置文件

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

例如:

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

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

但是Springboot並不會直接啟動這些配置文件,它默認使用application.properties主配置文件

我們需要通過一個配置來選擇需要激活的環境:

#比如在配置文件中指定使用dev環境,我們可以通過設置不同的端口號進行測試;#我們啟動SpringBoot,就可以看到已經切換到dev下的配置了;spring.profiles.active=dev

 

 

 

yaml的多文檔塊

和properties配置文件中一樣,但是使用yml去實現不需要創建多個配置文件,更加方便了 !

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

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


---

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

注意:如果yml和properties同時都配置了端口,並且沒有激活其他環境 , 默認會使用properties配置文件的!

 
        

配置文件加載位置

外部加載配置文件的方式十分多,我們選擇最常用的即可,在開發的資源文件中進行配置!

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

 

 

springboot 啟動會掃描以下位置的application.properties或者application.yml文件作為Spring boot的默認配置文件:

優先級1:項目路徑下的config文件夾配置文件
優先級2:項目路徑下配置文件
優先級3:資源路徑下的config文件夾配置文件
優先級4:資源路徑下配置文件

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

SpringBoot會從這四個位置全部加載主配置文件;互補配置;

我們在最低級的配置文件中設置一個項目訪問路徑的配置來測試互補問題;

#配置項目的訪問路徑server.servlet.context-path=/kuang

 

拓展,運維小技巧

指定位置加載配置文件

我們還可以通過spring.config.location來改變默認的配置文件位置

項目打包好以后,我們可以使用命令行參數的形式,啟動項目的時候來指定配置文件的新位置;這種情況,一般是后期運維做的多,相同配置,外部指定的配置文件優先級最高

java -jar spring-boot-config.jar --spring.config.location=F:/application.propert

Web開發靜態資源處理

簡介

好的,同學們,那么接下來呢,我們開始學習SpringBoot與Web開發,從這一章往后,就屬於我們實戰部分的內容了;

其實SpringBoot的東西用起來非常簡單,因為SpringBoot最大的特點就是自動裝配。

使用SpringBoot的步驟:

1、創建一個SpringBoot應用,選擇我們需要的模塊,SpringBoot就會默認將我們的需要的模塊自動配置好

2、手動在配置文件中配置部分配置項目就可以運行起來了

3、專注編寫業務代碼,不需要考慮以前那樣一大堆的配置了。

要熟悉掌握開發,之前學習的自動配置的原理一定要搞明白!

比如SpringBoot到底幫我們配置了什么?我們能不能修改?我們能修改哪些配置?我們能不能擴展?

  • 向容器中自動配置組件 :*** Autoconfiguration

  • 自動配置類,封裝配置文件的內容:***Properties

沒事就找找類,看看自動裝配原理!

我們之后來進行一個單體項目的小項目測試,讓大家能夠快速上手開發

首先,我們搭建一個普通的SpringBoot項目,回顧一下HelloWorld程序!

寫請求非常簡單,那我們要引入我們前端資源,我們項目中有許多的靜態資源,比如css,js等文件,這個SpringBoot怎么處理呢?

如果我們是一個web應用,我們的main下會有一個webapp,我們以前都是將所有的頁面導在這里面的,對吧!但是我們現在的pom呢,打包方式是為jar的方式,那么這種方式SpringBoot能不能來給我們寫頁面呢?當然是可以的,但是SpringBoot對於靜態資源放置的位置,是有規定的!

我們先來聊聊這個靜態資源映射規則:

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

我們可以去看看 WebMvcAutoConfigurationAdapter 中有很多配置方法;有一個方法:addResourceHandlers 添加資源處理

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    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));
    }
    // 靜態資源配置
    String staticPathPattern = this.mvcProperties.getStaticPathPattern();
    if (!registry.hasMappingForPattern(staticPathPattern)) {
        customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
                                             .addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
                                             .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
    }
}

讀一下源代碼:比如所有的 /webjars/** , 都需要去 classpath:/META-INF/resources/webjars/ 找對應的資源;

什么是webjars 呢?

Webjars本質就是以jar包的方式引入我們的靜態資源 , 我們以前要導入一個靜態資源文件,直接導入即可。

使用SpringBoot需要使用Webjars,我們可以去搜索一下:

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

要使用jQuery,我們只要要引入jQuery對應版本的pom依賴即可!

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

導入完畢,查看webjars目錄結構,並訪問Jquery.js文件!

 

 

 訪問:只要是靜態資源,SpringBoot就會去對應的路徑尋找資源,我們這里訪問:http://localhost:8080/webjars/jquery/3.4.1/jquery.js

 

 

 

第二種靜態資源映射規則

那我們項目中要是使用自己的靜態資源該怎么導入呢?我們看下一行代碼;

我們去找staticPathPattern發現第二種映射規則 :/** , 訪問當前的項目任意資源,它會去找 resourceProperties 這個類,我們可以點進去看一下分析:

// 進入方法
public String[] getStaticLocations() {
    return this.staticLocations;
}
// 找到對應的值
private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
// 找到路徑
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/"

我們可以在resources根目錄下新建對應的文件夾,都可以存放我們的靜態文件;

比如我們訪問 http://localhost:8080/1.js , 他就會去這些文件夾中尋找對應的靜態資源文件;

 在下面文件里面放靜態資源文件

 

/**是classpath下面的所有文件包括子文件。

 

 

 優先訪問resource下的文件,找到了其他的目錄就不再去找了

一般public優先級最低只放公共的資源,static放圖片

我們可以修改靜態資源的默認位置,修改后之前位置的資源就不能再被訪問到了

 

 

 

 

自定義靜態資源路徑

private Optional<Resource> getWelcomePage() {
    String[] locations = getResourceLocations(this.resourceProperties.getStaticLocations());
    // ::是java8 中新引入的運算符
    // Class::function的時候function是屬於Class的,應該是靜態方法。
    // this::function的funtion是屬於這個對象的。
    // 簡而言之,就是一種語法糖而已,是一種簡寫
    return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst();
}
// 歡迎頁就是一個location下的的 index.html 而已
private Resource getIndexHtml(String location) {
    return this.resourceLoader.getResource(location + "index.html");
}

 

我們也可以自己通過配置文件來指定一下,哪些文件夾是需要我們放靜態資源文件的,在application.properties中配置;

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

首頁處理

靜態資源文件夾說完后,我們繼續向下看源碼!可以看到一個歡迎頁的映射,就是我們的首頁!點進去繼續看

@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
                                                           FormattingConversionService mvcConversionService,
                                                           ResourceUrlProvider mvcResourceUrlProvider) {
    WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
        new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(), // getWelcomePage 獲得歡迎頁
        this.mvcProperties.getStaticPathPattern());
    welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
    return welcomePageHandlerMapping;
}

歡迎頁,靜態資源文件夾下的所有 index.html 頁面;被 /** 映射。

比如我訪問  http://localhost:8080/ ,就會找靜態資源文件夾下的 index.html

新建一個 index.html ,在我們上面的3個目錄中任意一個;然后訪問測試  http://localhost:8080/  看結果!

關於網站圖標說明

 

 

 與其他靜態資源一樣,Spring Boot在配置的靜態內容位置中查找 favicon.ico。如果存在這樣的文件,它將自動用作應用程序的favicon。

 

1、關閉SpringBoot默認圖標

#關閉默認圖標
spring.mvc.favicon.enabled=false

2、自己放一個圖標在靜態資源目錄下,我放在 public 目錄下

3、清除瀏覽器緩存!刷新網頁,發現圖標已經變成自己的了!

 

 

Thymeleaf模板引擎

模板引擎

前端交給我們的頁面,是html頁面。如果是我們以前開發,我們需要把他們轉成jsp頁面,jsp好處就是當我們查出一些數據轉發到JSP頁面以后,我們可以用jsp輕松實現數據的顯示,及交互等。

jsp支持非常強大的功能,包括能寫Java代碼,但是呢,我們現在的這種情況,SpringBoot這個項目首先是以jar的方式,不是war,像第二,我們用的還是嵌入式的Tomcat,所以呢,他現在默認是不支持jsp的

那不支持jsp,如果我們直接用純靜態頁面的方式,那給我們開發會帶來非常大的麻煩,那怎么辦呢?

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

模板引擎,我們其實大家聽到很多,其實jsp就是一個模板引擎,還有用的比較多的freemarker,包括SpringBoot給我們推薦的Thymeleaf,模板引擎有非常多,但再多的模板引擎,他們的思想都是一樣的,什么樣一個思想呢我們來看一下這張圖:

 

 

模板引擎的作用就是我們來寫一個頁面模板,比如有些值呢,是動態的,我們寫一些表達式。而這些值,從哪來呢,就是我們在后台封裝一些數據。然后把這個模板和這個數據交給我們模板引擎,模板引擎按照我們這個數據幫你把這表達式解析、填充到我們指定的位置,然后把這個數據最終生成一個我們想要的內容給我們寫出去,這就是我們這個模板引擎,不管是jsp還是其他模板引擎,都是這個思想。只不過呢,就是說不同模板引擎之間,他們可能這個語法有點不一樣。其他的我就不介紹了,我主要來介紹一下SpringBoot給我們推薦的Thymeleaf模板引擎,這模板引擎呢,是一個高級語言的模板引擎,他的這個語法更簡單。而且呢,功能更強大。

我們呢,就來看一下這個模板引擎,那既然要看這個模板引擎。首先,我們來看SpringBoot里邊怎么用。

引入Thymeleaf

怎么引入呢,對於springboot來說,什么事情不都是一個start的事情嘛,我們去在項目中引入一下。給大家三個網址:

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>

 

Maven會自動下載jar包,我們可以去看下下載的東西;

 

 

Thymeleaf分析

前面呢,我們已經引入了Thymeleaf,那這個要怎么使用呢?

我們首先得按照SpringBoot的自動配置原理看一下我們這個Thymeleaf的自動配置規則,在按照那個規則,我們進行使用。

我們去找一下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下,thymeleaf就可以幫我們自動渲染了。

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

 1、編寫一個TestController

@Controller
public class TestController {
    
    @RequestMapping("/t1")
    public String test1(){
        //classpath:/templates/test.html
        return "test";
    }
    
}

2、編寫一個測試頁面  test.html 放在 templates 目錄下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>測試頁面</h1>

</body>
</html>

3、啟動項目請求測試

Thymeleaf 語法學習

要學習語法,還是參考官網文檔最為准確,我們找到對應的版本看一下;

Thymeleaf 官網:https://www.thymeleaf.org/ , 簡單看一下官網!我們去下載Thymeleaf的官方文檔!

我們做個最簡單的練習 :我們需要查出一些數據,在頁面中展示

1、修改測試請求,增加數據傳輸;

@RequestMapping("/t1")
public String test1(Model model){
    //存入數據
    model.addAttribute("msg","Hello,Thymeleaf");
    //classpath:/templates/test.html
    return "test";
}

2、我們要使用thymeleaf,需要在html文件中導入命名空間的約束,方便提示。

我們可以去官方文檔的#3中看一下命名空間拿來過來:

xmlns:th="http://www.thymeleaf.org"

3、我們去編寫下前端頁面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>狂神說</title>
</head>
<body>
<h1>測試頁面</h1>

<!--th:text就是將div中的內容設置為它指定的值,和之前學習的Vue一樣-->
<div th:text="${msg}"></div>
</body>
</html>

4、啟動測試!

 

 

 

OK,入門搞定,我們來認真研習一下Thymeleaf的使用語法!

1、我們可以使用任意的 th:attr 來替換Html中原生屬性的值!

 

 

 2、我們能寫哪些表達式呢?

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: _

 

練習測試:

1、 我們編寫一個Controller,放一些數據

@RequestMapping("/t2")
public String test2(Map<String,Object> map){
    //存入數據
    map.put("msg","<h1>Hello</h1>");
    map.put("users", Arrays.asList("qinjiang","kuangshen"));
    //classpath:/templates/test.html
    return "test";
}

2、測試頁面取出數據

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>狂神說</title>
</head>
<body>
<h1>測試頁面</h1>

<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>

3、啟動項目測試!

我們看完語法,很多樣式,我們即使現在學習了,也會忘記,所以我們在學習過程中,需要使用什么,根據官方文檔來查詢,才是最重要的,要熟練使用官方文檔!

 

 




 


免責聲明!

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



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