天天看點

SpringBoot入門spring與springboot一、SpringBoot特點二、SpringBoot配置檔案三、日志四、SpringBoot整合其他架構五、web開發其他

spring與springboot

SpringBoot提供了一種快速開發Spring項目的方式,而不是對Spring功能上的增強。

Spring的缺點:

  • 配置繁瑣
  • 依賴繁瑣

SpringBoot功能:

  • 自動配置
  • 依賴管理
  • 輔助功能(例如嵌入式伺服器)

springboot入門:

  • SpringBoot在建立項目時,使用jar的打包方式。
  • SpringBoot的引導類,是項目入口,運作main方法就可以啟動項目。
  • 使用SpringBoot和Spring建構的項目,業務代碼編寫方式完全一樣。

一、SpringBoot特點

1. 依賴管理

  • 在spring-boot-starter-parent中定義了各種技術的版本資訊,組合了一套最優搭配的技術版本。
  • 在各種starter中,定義了完成該功能需要的坐标合集,其中大部分版本資訊來自于父工程。
  • 我們的工程繼承spring-boot-starter-parent,引入starter後,通過依賴傳遞,就可以簡單友善獲得需要的jar包,并且不會存在版本沖突等問題。

父項目做依賴管理

//pom檔案中的父項目

<parent>

       <groupId>org.springframework.boot</groupId>

       <artifactId>spring-boot-starter-parent</artifactId>

       <version>2.3.4.RELEASE</version>

</parent>

//他的父項目

<parent>

   <groupId>org.springframework.boot</groupId>

   <artifactId>spring-boot-dependencies</artifactId>

   <version>2.3.4.RELEASE</version>

 </parent>

//幾乎聲明了所有開發中常用依賴的版本号

導入starter場景啟動器

1、見到很多 spring-boot-starter-* : *就是某種場景

2、隻要引入starter,這個場景的所有正常需要的依賴都會自動引入

3、SpringBoot所有支援的場景

https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter

4、見到的  *-spring-boot-starter: 第三方為我們提供的簡化開發的場景啟動器。

5、所有場景啟動器最底層的依賴

<dependency>

 <groupId>org.springframework.boot</groupId>

 <artifactId>spring-boot-starter</artifactId>

 <version>2.3.4.RELEASE</version>

 <scope>compile</scope>

</dependency>

修改預設版本号

1、檢視父父項目spring-boot-dependencies裡面規定目前依賴的版本用的 key。

2、在目前項目裡面重寫配置

   <properties>

       <mysql.version>5.1.43</mysql.version>

   </properties>

2. 自動配置

  • 自動配好Tomcat
    • 引入Tomcat依賴。
    • 配置Tomcat
  • 自動配好SpringMVC
    • 引入SpringMVC全套元件
    • 自動配好SpringMVC常用元件(功能)
  • 自動配好Web常見功能,如:字元編碼問題
    • SpringBoot幫我們配置好了所有web開發的常見場景
  • 預設的包結構
    • 主程式所在包及其下面的所有子包裡面的元件都會被預設掃描進來
    • 無需以前的包掃描配置
    • 想要改變掃描路徑,@SpringBootApplication(scanBasePackages="com.atguigu")或者@ComponentScan 指定掃描路徑

@SpringBootApplication

通過檢視源碼,發現上面這個注解等同于下面三個注解的組合

@SpringBootConfiguration

@EnableAutoConfiguration

@ComponentScan("com.atguigu.boot")

  • 各種配置擁有預設值
    • 預設配置最終都是映射到某個類上,如:MultipartProperties
    • 配置檔案的值最終會綁定每個類上,這個類會在容器中建立對象
  • 按需加載所有自動配置項
    • 存在非常多的starter,但隻有引入了某個場景,這個場景的自動配置才會開啟
    • SpringBoot所有的自動配置功能都在 spring-boot-autoconfigure 包裡面

二、SpringBoot配置檔案

1. 配置檔案的分類

SpringBoot是基于約定的,是以很多配置都有預設值,但如果想使用自己的配置替換預設配置的話,就可以使用application.properties或者application.yml(application.yaml)進行配置。

  • 預設配置檔案名稱:application
  • 在同一級目錄下優先級為:properties > yml > yaml

2. yaml

1. 基本文法

  • 大小寫敏感
  • 資料值前邊必須有空格,作為分隔符
  • 使用縮進表示層級關系
  • 縮進時不允許使用Tab鍵,隻允許使用空格(各個系統 Tab對應的 空格數目可能不同,導緻層次混亂)。
  • 縮進的空格數目不重要,隻要相同層級的元素左側對齊即可
  • # 表示注釋,從這個字元一直到行尾,都會被解析器忽略。

2. 資料類型

純量

單個的、不可再分的值。date、boolean、string、number、null

k: v

字元串無需加引号,如果要加

  1. ' '

    會忽略轉義字元,即将

    \n

    作為字元串輸出
  2. " "

    會識别轉義字元,即将

    \n

    作為換行輸出

對象

鍵值對的集合。map、hash、object

k:

 k1: v1

 k2: v2

 k3: v3

#行内寫法

k: {k1: v1,k2: v2,k3: v3}

數組

一組按次序排列的值。array、list、queue、set

k:

- v1

- v2

- v3

k: [v1,v2,v3]

3. 參數引用

${key}

name: lisi

person:

   name: ${name} # 引用上邊定義的name值

3. 讀取配置檔案内容

yaml檔案

#對象

  name: zhangsan

  age: 20

#集合

address:

  - beijing

  - shanghai

#純量

msg1: 12

@Value

作用:注入資料

使用:

@Value("${xxx}")

作用在變量上,将配置檔案中的值注入到目前變量

    @Value("${person.name}")

    private String name2;

    @Value("${person.age}")

    private int age;

    @Value("${address[0]}")

    private String address1;

    @Value("${msg1}")

    private String msg1;

Environment

Environment對象在容器中本來就存在,直接注入進來使用即可

    @Autowired

    private Environment env;

        System.out.println(env.getProperty("person.name"));

        System.out.println(env.getProperty("address[0]"));

@ConfigurationProperties

讀取配置檔案中的内容,把它封裝到變量上

1. @Component + @ConfigurationProperties

隻有在容器中的元件,才能使用配置綁定功能

即先添加到容器

@Component

,再将配置綁定

@ConfigurationProperties

@Component

@ConfigurationProperties(prefix = "mycar")

public class Car {

    private String brand;

    private Integer price;

    ……

}

2. @EnableConfigurationProperties + @ConfigurationProperties

@EnableConfigurationProperties

作用在配置類上

@EnableConfigurationProperties(Car.class)

public class MyConfig {

//1. 對Car開啟配置綁定功能

//2. 把Car這個元件添加到容器,就無需@Component注解(适用于第三方jar包,我們無法修改源碼的情況下)

4. profile

profile是用來完成不同環境下,配置動态切換功能的。

profile配置方式

  1. 多profile檔案方式:提供多個配置檔案,每個代表一種環境。

    application-dev.properties/yml——開發環境

    application-test.properties/yml——測試環境

    application-pro.properties/yml——生産環境

  2. yml多文檔方式:在yml中使用

    ---

    分隔不同配置

---

server:

  port: 8081

#給目前配置部分起别名

spring:

  profiles: dev

  port: 8082

  profiles: test

  port: 8083

  profiles: pro

#激活配置檔案

  profiles:

    active: dev

profile激活方式

  1. 配置檔案: 在配置檔案中配置:

    spring.profiles.active=dev

  2. 虛拟機參數:在VM options 指定:

    -Dspring.profiles.active=dev

  3. 指令行參數:

    java –jar xxx.jar --spring.profiles.active=dev

5. 内部配置加載順序

Springboot程式啟動時,會從以下位置加載配置檔案:

  1. file:./config/:目前項目下的/config目錄下
  2. file:./ :目前項目的根目錄
  3. classpath:/config/:classpath的/config目錄
  4. classpath:/ :classpath的根目錄

加載順序為上文的排列順序,高優先級配置的屬性會生效

6. 外部配置加載順序

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

三、日志

1. 日志架構

日志架構可分為日志門面(即接口)和日志實作,即面向接口程式設計

日志門面 (日志的抽象層) 日志實作
SLF4j(Simple Logging Facade for Java) Log4j JUL(java.util.logging) Log4j2 Logback

SpringBoot選用 SLF4j和logback

2. SLF4j使用

1. 在程式中使用slf4j

官網:

https://www.slf4j.org

以後開發的時候,日志記錄方法的調用,不應該來直接調用日志的實作類,而是調用日志接口裡面的方法;

給系統裡面導入slf4j的jar和 logback的實作jar

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

public class HelloWorld {

  public static void main(String[] args) {

    Logger logger = LoggerFactory.getLogger(HelloWorld.class);

    logger.info("Hello World");

  }

每一個日志的實作架構都有自己的配置檔案。使用slf4j以後,配置檔案還是做成日志實作架構自己本身的配置檔案

2. 統一日志為slf4j

a(slf4j+logback): Spring(commons-logging)、Hibernate(jboss-logging)、MyBatis、xxxx

如何讓系統中所有的日志都統一到slf4j;

  1. 将系統中其他日志架構先排除出去
  2. 用中間包來替換原有的日志架構
  3. 我們導入slf4j其他的實作

3. SpringBoot日志使用

Spring Boot預設使用LogBack日志系統,如果不需要更改為其他日志系統如Log4j2等,則無需多餘的配置,LogBack預設将日志列印到控制台上。

如果要使用LogBack,原則上是需要添加dependency依賴的

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-logging</artifactId></pre>

但是因為建立的Spring Boot項目一般都會引用

spring-boot-starter

或者

spring-boot-starter-web

,而這兩個起步依賴中都已經包含了對于

spring-boot-starter-logging

的依賴,是以,無需額外添加依賴。

列印日志

建立一個配置類LogConfig,注入一個Bean,并在方法中列印日志

package com.jackie.springbootdemo.config;

import com.jackie.springbootdemo.model.Person;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

@Configuration public class LogConfig {

    private static final Logger LOG = LoggerFactory.getLogger(LogConfig.class);

    @Bean

  public Person logMethod() {

        LOG.info("==========print log==========");

        return new Person();

    }

Spring Boot預設的日志級别為INFO(隻列印info及以上級别的日志),這裡列印的是INFO級别的日志是以可以顯示。

很多開發者在日常寫

private static final Logger LOG = LoggerFactory.getLogger(LogConfig.class);

總覺得後面的LogConfig.class可有可無,因為随便寫個其他類也不會報錯,但是準确編寫class資訊能夠提供快速定位日志的效率。

我們看到列印的日志内容左側就是對應的類名稱,這個是通過

private static final Logger LOG = LoggerFactory.getLogger(LogConfig.class);

實作的。

如果将LogConfig.class換成xxx.class,輸出日志就會顯示對應的xxx類名。這樣聲明的好處就是友善定位日志。

存儲日志

在我們建立的springboot-demo項目中,resources目錄下有個application.properties檔案(如果是application.yml檔案也是同樣的道理,隻是采用的不同的編寫風格而已)。添加如下配置

logging:

  level:

    root: info

    cn.upeveryday: debug

  file:

    name: log/blog-dev.log

logging.path

該屬性用來配置日志檔案的路徑

logging.file

該屬性用來配置日志檔案名,如果該屬性不配置,預設檔案名為spring.log

Example Description
(none) 隻在控制台輸出
指定檔案名 my.log 輸出日志到my.log檔案
指定目錄 /var/log 輸出到指定目錄的 spring.log 檔案中

日志級别

日志級别總共有trace< debug< info< warn< error< fatal,且級别是逐漸提供,如果日志級别設定為INFO,則意味TRACE和DEBUG級别的日志都看不到。

logging.level

該屬性用于配置日志級别。

  • root級别,即項目的所有日志
  • package級别,即指定包下使用相應的日志級别
  • 這裡除了

    cn.upeveryday

    包下的日志是debug級别,其他日志是info級别

四、SpringBoot整合其他架構

1. SpringBoot整合Junit

1. 添加Junit的起步依賴

<!--測試的起步依賴-->

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-test</artifactId>

    <scope>test</scope>

2. 編寫測試類

package com.itheima.test;

@RunWith(SpringRunner.class)

@SpringBootTest(classes = MySpringBootApplication.class)

public class MapperTest {

    private UserMapper userMapper;

    @Test

    public void test() {

        List<User> users = userMapper.queryUserList();

        System.out.println(users);

測試相關注解

  • @SpringBootTest(classes = 啟動類.class)

2. SpringBoot整合Mybatis

1. 添加Mybatis的起步依賴

  <!--mybatis起步依賴-->

        <dependency>

            <groupId>org.mybatis.spring.boot</groupId>

            <artifactId>mybatis-spring-boot-starter</artifactId>

            <version>2.2.0</version>

        </dependency>

2. 添加資料庫驅動坐标

  <!-- MySQL連接配接驅動 -->

            <groupId>mysql</groupId>

            <artifactId>mysql-connector-java</artifactId>

            <scope>runtime</scope>

3. 添加資料庫連接配接資訊

在application.yaml中添加資料源的連接配接資訊

  datasource:

    url: jdbc:mysql://127.0.0.1:3306/springboot?useUnicode=true&characterEncoding=utf8

    username: ggbond

    password: xxxx

    driver-class-name: com.mysql.cj.jdbc.Driver

4. 建立t_user表

在test資料庫中建立t_user表

CREATE DATABASE /*!32312 IF NOT EXISTS*/`springboot` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */;

USE `springboot`;

/*Table structure for table `t_user` */

DROP TABLE IF EXISTS `t_user`;

CREATE TABLE `t_user` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `username` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL,

  `password` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL,

  PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

/*Data for the table `t_user` */

insert  into `t_user`(`id`,`username`,`password`) values (1,'zhangsan','123'),(2,'lisi','234');

5. 建立實體Bean

@ToString

@NoArgsConstructor

@AllArgsConstructor

@Data

public class User {

    private int id;

    private String username;}

6.1 使用注解

7. 編寫mapper

@Mapper

public interface UserMapper {

    @Select("select * from t_user")

    public List<User> findAll();

注意:@Mapper注解是由Mybatis架構中定義的一個描述持久層接口的注解,注解往往起到的都是一個描述性作用,用于告訴spring架構此接口的實作類由Mybatis負責建立,并将其實作類對象存儲到spring容器中。

8. 測試

@SpringBootTest(classes = SpringbootMybatisApplication.class)

class SpringbootMybatisApplicationTests {

    void testFindAll() {

        List<User> users = userMapper.findAll();

6.2 使用xml

7. 編寫接口

public interface UserXmlMapper {

    List<User> findAll();

8. 配置映射檔案

在src\main\resources\mapper路徑下加入UserMapper.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.example.springbootmybatis.mapper.UseXmlMapper">

    <select id="findAll" resultType="user">

        select * from t_user

    </select>

</mapper>

9. 在application.yaml中配置mybatis

mybatis:

  #加載Mybatis映射檔案

  mapper-locations: classpath:mapper/*Mapper.xml

  #對于mapper檔案中實體類的别名,要進行掃描的包

  type-aliases-package: com.example.springbootmybatis.domain

10. 測試

    private UseXmlMapper useXmlMapper;

    void testFindAll2(){

        List<User> users = useXmlMapper.findAll();

@Mapper注解是由Mybatis架構中定義的一個描述持久層接口的注解,注解往往起到的都是一個描述性作用,用于告訴spring架構此接口的實作類由Mybatis負責建立,并将其實作類對象存儲到spring容器中。

添加Mapper注解之後,就不用添加@@Repository注解了

@MapperScan注解

之前是,直接在Mapper類上面添加注解@Mapper,這種方式要求每一個mapper類都需要添加此注解,麻煩。

通過@MapperScan作用在主啟動類上,可以指定要掃描的Mapper類的包的路徑。這樣dao包下的接口什麼注解都不用加

@MapperScan("cn.upeveyday.springboot_ems.dao")

public class SpringbootEmsApplication {

五、web開發

static中的靜态資源可以直接通過url通路

templates中的動态資源隻能通過控制器通路(請求轉發)

1. 靜态資源

存放位置:

預設情況下,Spring Boot從類路徑中名為

/static

/public

/resources

/META-INF/resources

)的目錄中提供靜态内容。(這些目錄都在resources目錄下,例如:

src/main/resources/static

通路:

目前項目根路徑/ + 靜态資源名

原理:

當請求進來,先去找Controller看能不能處理,不能處理的請求再交給靜态資源處理器。靜态資源也找不到則響應404頁面

靜态資源通路字首:

  1. 預設無字首

  mvc:

    static-path-pattern: /**

  1. 設定字首

    static-path-pattern: /res/**

改變預設的靜态資源路徑

  resources:

    static-locations: [classpath:/haha/] # 這是個字元串數組

2. 歡迎頁

通路

http://localhost:8080/

,會預設找到歡迎頁:靜态資源路徑下的index.html

注意:可以配置靜态資源路徑,但是不可以配置靜态資源的通路字首。否則導緻 index.html不能被預設通路

#  mvc:

#    static-path-pattern: /res/**   配置靜态資源的通路字首,會導緻welcome page功能失效

    static-locations: [classpath:/haha/]

3. 請求參數處理

rest的使用與原理

Rest風格:使用HTTP請求方式動詞來表示對資源的操作,傳遞參數不再是請求參數而是URI的一部分

  • 以前:/getUser 擷取使用者 /deleteUser 删除使用者 /editUser 修改使用者 /saveUser 儲存使用者
  • 現在: /user GET-擷取使用者 DELETE-删除使用者 PUT-修改使用者 POST-儲存使用者。請求路徑一樣,根據請求方式的不同來執行不同的方法

rest使用

核心Filter:HiddenHttpMethodFilter,需要在配置檔案中手動開啟

用法:

  1. HTML的表單送出方法為post,增加隐藏域

    name="_method" value="put"

  2. controller中方法的請求映射注解添加屬性

    method = RequestMethod.PUT

    //controller

 @RequestMapping(value = "/user",method = RequestMethod.GET)

    public String getUser(){

        return "GET-張三";

    @RequestMapping(value = "/user",method = RequestMethod.POST)

    public String saveUser(){

        return "POST-張三";

//    @RequestMapping(value = "/user",method = RequestMethod.PUT)

    @PutMapping("/user")//springboot提供的新注解,其他方法類似

    public String putUser(){

        return "PUT-張三";

    @RequestMapping(value = "/user",method = RequestMethod.DELETE)

    public String deleteUser(){

        return "DELETE-張三";

 //HTML

<form action="/user" method="post">

    <input type="hidden" name="_method" value="put">

    <input type="submit" value="送出">

</form>

    //application.yaml

    hiddenmethod:

      filter:

        enabled: true

rest原理

  1. HTML表單送出要使用REST時
    • 表單送出會帶上_method=PUT
    • 請求過來被

      HiddenHttpMethodFilter

      攔截
    • 檢查表單請求是否正常,并且是POST請求
    • 擷取到_method的值。相容這些請求:PUT.DELETE.PATCH
    • 原生request(post),包裝模式requesWrapper重寫了getMethod方法,傳回的是傳入的值。
    • 過濾器鍊放行的時候用wrapper,以後的方法調用getMethod得到的是requesWrapper重寫後的PUT
  1. 用戶端工具發送請求使用rest

    如PostMan直接發送Put、delete等方式請求,無需Filter

參數處理原理

  1. DispatcherServlet通過處理器映射器HandlerMapping找到能夠處理請求的處理器Handler(控制器中的方法)
  2. 為目前Handler找一個處理器擴充卡HandlerAdapter
  3. 擴充卡執行目标方法并确定方法參數的每一個值

1. 作用在參數上的注解

@PathVariable:路徑變量(rest風格)

作用:

用于綁定 url 中的占位符。例如:請求 url 中 /delete/{id},這個{id}就是 url 占位符。

屬性:

value:用于指定 url 中占位符名稱,不指定則是擷取全部。

出現位置:

控制器方法參數上,參數可以是

Map<String,String>

類型的,會将所有占位符存入

    @GetMapping("/car/{id}/owner/{username}")

    public Map<String,Object> getCar(@PathVariable("id") Integer id,

                                     @PathVariable("username") String name,

                                     @PathVariable Map<String,String> pv){

        Map<String,Object> map = new HashMap<>();

        map.put("id",id);

        map.put("name",name);

        map.put("pv",pv);

@RequestHeader

作用

用于擷取請求消息頭。

屬性

value:指定消息頭名稱,不指定則是全部。

出現位置

控制器方法參數上,參數可以是

Map<String,String>

類型的,将所有請求頭存入

    public Map<String,Object> getCar(

                                     @RequestHeader("User-Agent") String userAgent,

                                     @RequestHeader Map<String,String> header){

        map.put("userAgent",userAgent);

        map.put("headers",header);

        return map;

@RequestParam、@CookieValue

@RequestParam:把請求中指定名稱的參數給控制器中的形參指派

@CookieValue:用于把指定 cookie 名稱的值傳入控制器方法參數。

@RequestParam:

  • value:指定請求參數中的名稱,不指定則是全部。

@CookieValue:

  • value:指定 cookie 的名稱。

控制器方法參數上,可以使用

Map<String,String>

類型的參數,将所有請求參數存入

控制器方法參數上,可以使用Cookie類型接收

                                     @RequestParam("age") Integer age,

                                     @RequestParam("inters") List<String> inters,

                                     @RequestParam Map<String,String> params,

                                     @CookieValue("_ga") String _ga,

                                     @CookieValue("_ga") Cookie cookie){

        map.put("age",age);

        map.put("inters",inters);

        map.put("params",params);

        map.put("_ga",_ga);

        System.out.println(cookie.getName()+"===>"+cookie.getValue());

@RequestBody

用于擷取請求體内容。直接使用得到是 key=value&key=value...結構的資料。

隻适用于post請求,get 請求方式不适用,因為get方式沒有請求體,隻有請求參數

控制器方法參數上

    @PostMapping("/save")

    public Map postMethod(@RequestBody String content){

        map.put("content",content);

@RequestAttribute

擷取request域屬性(HttpServetRequest在request域中添加屬性)

@MatrixVariable

矩陣變量使用在請求路徑中,将cookie的值使用矩陣變量的方式傳遞

矩陣變量以

;

分隔

    //1、文法: 請求路徑:/cars/sell;low=34;brand=byd,audi,yd

    //2、SpringBoot預設是禁用了矩陣變量的功能

    //      手動開啟:原理。對于路徑的處理。UrlPathHelper進行解析。

    //              removeSemicolonContent(移除分号内容)支援矩陣變量的

    //3、矩陣變量必須有url路徑變量才能被解析

    @GetMapping("/cars/{path}")

    public Map carsSell(@MatrixVariable("low") Integer low,

                        @MatrixVariable("brand") List<String> brand,

                        @PathVariable("path") String path){

        map.put("low",low);

        map.put("brand",brand);

        map.put("path",path);

    // /boss/1;age=20/2;age=10

    @GetMapping("/boss/{bossId}/{empId}")

    public Map boss(@MatrixVariable(value = "age",pathVar = "bossId") Integer bossAge,

                    @MatrixVariable(value = "age",pathVar = "empId") Integer empAge){

        map.put("bossAge",bossAge);

        map.put("empAge",empAge);

2. Servlet API

WebRequest、ServletRequest、MultipartRequest、 HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId

3. 複雜參數

Map、Model(map、model裡面的資料會被放在request的請求域 request.setAttribute)、Errors/BindingResult、RedirectAttributes( 重定向攜帶資料)、ServletResponse(response)、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder

Map<String,Object> map,  Model model, HttpServletRequest request 都是可以給request域中放資料,

request.getAttribute();

4. 模闆引擎thymeleaf

<html lang="en" xmlns:th="http://www.thymeleaf.org">

視圖解析:SpringBoot預設不支援 JSP,需要引入第三方模闆引擎技術實作頁面渲染。

1、表達式

表達式名字 文法 用途
變量取值 ${...} 擷取域中的值,預設從request域從擷取
選擇變量 *{...} 擷取上下文對象值
消息 #{...} 擷取國際化等值
連結 @{...} 生成連結(會自動添加虛拟目錄)
片段表達式 ~{...} jsp:include 作用,引入公共頁面片段

${...}

預設取的是request域中資料

要取session域中資料:

${session.user.name}

标簽内寫法

雙引号

<p>Hello, <span th:text="${session.user.name}">Sebastian</span>!</p>

标簽外寫法

雙中括号

<p>Hello, [[${session.user.name}]]!</p>

2、常用文法

①、文本操作

  • 字元串拼接:

    +

  • 變量替換:

    |The name is ${name}|

<p th:text="${userName}">neo</p>

<span th:text="'Welcome to our application, ' + ${userName} + '!'"></span>

<span th:text="|Welcome to our application, ${userName}!|"></span>

②、條件判斷 If/Unless

Thymeleaf 中使用 th:if 和 th:unless 屬性進行條件判斷,下面的例子中,<a>标簽隻有在 th:if 中條件成立時才顯示

<a th:if="${flag == 'yes'}"  th:href="@{/home}"> home </a>

<a th:unless="${flag != 'no'}" th:href="@{http://www.ityouknow.com/}" >ityouknow</a>

<div th:unless="${#strings.isEmpty(session.message)}" th:text="${session.message}">使用者名和密碼錯誤</div>

//隻有表達式為false時才顯示目前标簽,為true則不顯示

<div th:if="${session.account!=null}">

    <span th:text="${session.account.username}+'歡迎回來!'"></span>

    <a href="/logout">退出</a>

</div>

//隻有session中的account不為空,才會在HTML代碼中顯示目前标簽k,否則不顯示

th:unless 與 th:if 恰好相反,隻有表達式中的條件不成立,才會顯示其内容。

也可以使用 (if) ? (then) : (else) 這種文法來判斷顯示的内容

If-then: (if) ? (then)

If-then-else: (if) ? (then) : (else)

Default: (value) ?: (defaultvalue) 

<!--根據公共片段傳來的參數n,判斷當光标應該在哪一個導航項-->

<div th:fragment="nav(n)">

    <li class="layui-nav-item" th:classappend="${n==1}?'layui-this'">首頁</li>

    <li class="layui-nav-item" th:classappend="${n==2}?'layui-this'">交流社群</li>

 <li class="layui-nav-item" th:classappend="${n==3}?'layui-this'">生活社群</li>

③、for 循環

周遊users中的元素,每一個都用user接收,類似java增強for循環

<table>

    <tr  th:each="user,iterStat : ${users}">

        <td th:text="${user.name}">neo</td>

        <td th:text="${user.age}">6</td>

        <td th:text="${user.pass}">213</td>

        <td th:text="${iterStat.index}">index</td>

    </tr>

</table>

iterStat 稱作狀态變量,屬性有

  • index:目前疊代對象的 index(從 0 開始計算)
  • count:目前疊代對象的 index(從 1 開始計算)
  • size:被疊代對象的大小
  • current:目前疊代變量
  • even/odd:布爾值,目前循環是否是偶數/奇數(從 0 開始計算)
  • first:布爾值,目前循環是否是第一個
  • last:布爾值,目前循環是否是最後一個

④、URL

URL 在 Web 應用模闆中占據着十分重要的地位,需要特别注意的是 Thymeleaf 對于 URL 的處理是通過文法 @{…} 來處理的。 如果需要 Thymeleaf 對 URL 進行渲染,那麼務必使用 th:href、th:src 等屬性,下面舉一個例子:

<a  th:href="@{http://www.ityouknow.com/{type}(type=${type})}">link1</a>

<a href="details.html" th:href="@{http://www.ityouknow.com/{pageId}/can-use-springcloud.html(pageId=${pageId})}">view</a>

設定背景:

<div th:style="'background:url(' + @{${img}} + ');'">

根據屬性值改變背景:

<div class="media-object resource-card-image"  th:style="'background:url(' + @{(${collect.webLogo}=='' ? 'img/favicon.png' : ${collect.webLogo})} + ')'" ></div>

幾點說明:

  • 上例中 URL 最後的 (pageId=${pageId}) 表示将括号内的内容作為 URL 參數處理,該文法避免使用字元串拼接,大大提高了可讀性。
  • @{…} 表達式中可以通過 {pageId} 通路 Context 中的 pageId 變量。
  • @{/order} 是 Context 相關的相對路徑,在渲染時會自動添加上目前 Web 應用的 Context 名字,假設 context 名字為 app,那麼結果應該是 /app/order。

⑤、内聯 [ [ ] ]

内聯文本:[[…]] 内聯文本的表示方式,使用時,必須先用在 th:inline=”text/javascript/none” 激活,th:inline 可以在父級标簽内使用,甚至作為 body 的标簽。内聯文本盡管比 th:text 的代碼少,不利于原型顯示。

文本内聯:

<div th:inline="text" >

    <h1>内聯js</h1>

    <p>Hello, [[${userName}]]</p>

    <br/>

腳本内聯,腳本内聯可以在 js 中取到背景傳過來的參數:

<script th:inline="javascript">

    var name = [[${userName}]] + ', Sebastian';

    alert(name);

</script>

⑥、内嵌變量

為了模闆更加易用,Thymeleaf 還提供了一系列 Utility 對象(内置于 Context 中),可以通過

#

直接通路:

  • dates:java.util.Date 的功能方法類
  • calendars: 類似 #dates,面向 java.util.Calendar
  • numbers:格式化數字的功能方法類
  • strings:字元串對象的功能類,contains、startWiths、prepending/appending 等
  • objects:對 objects 的功能類操作
  • bools: 對布爾值求值的功能方法
  • arrays:對數組的功能類方法
  • lists:對 lists 的功能類方法
  • sets
  • maps

下面用一段代碼來舉例一些常用的方法:

dates

<p th:text="${#dates.format(date, 'dd/MMM/yyyy HH:mm')}">neo</p>

<p th:text="${#dates.createToday()}">neo</p>

<p th:text="${#dates.createNow()}">neo</p>

strings

<p th:text="${#strings.isEmpty(userName)}">userName</p>

<p th:text="${#strings.listIsEmpty(users)}">userName</p>

<p th:text="${#strings.length(userName)}">userName</p>

<p th:text="${#strings.concat(userName)}"></p>

<p th:text="${#strings.randomAlphanumeric(count)}">userName</p>

3. 設定屬性值-th:attr

設定單個值

<form action="subscribe.html" th:attr="action=@{/subscribe}">

  <fieldset>

    <input type="text" name="email" />

    <input type="submit" value="Subscribe!" th:attr="value=#{subscribe.submit}"/>

  </fieldset>

設定多個值

<img src="../../images/gtvglogo.png"  th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />

以上兩個的代替寫法

th:屬性名="表達式"

<input type="submit" value="Subscribe!" th:value="#{subscribe.submit}"/>

<form action="subscribe.html" th:action="@{/subscribe}">

4、常用th标簽

關鍵字 功能介紹 案例
th:id 替換id <input th:id=”‘xxx’ + ${collect.id}”/>
th:text 文本替換 <p th:text="${collect.description}">description</p>
th:utext 支html的文本替換 <p th:utext="${htmlcontent}">conten</p>
th:object 替換對象 <div th:object="${session.user}">
th:value 屬性指派 <input th:value="${user.name}" />
th:with 變量指派運算 <div th:with=”isEven=${prodStat.count}%2==0″></div>
th:style 設定樣式 th:style=”‘display:’ + @{(${sitrue} ? ‘none’ : ‘inline-block’)} + ””
th:onclick 點選事件 th:onclick=”‘getCollect()'”
th:each tr th:each=”user,userStat:${users}”>
th:if 判斷條件 <a th:if=”${userId == collect.userId}” >
th:unless 和th:if判斷相反 <a th:href=”@{/login}” th:unless=${session.user != null}>Login</a>
th:href 連結位址 <a th:href=”@{/login}” th:unless=${session.user != null}>Login</a> />
th:switch 多路選擇 配合th:case 使用 <div th:switch="${user.role}">
th:case th:switch的一個分支 <p th:case=”‘admin'”>User is an administrator</p>
th:fragment 布局标簽,定義一個代碼片段,友善其它地方引用 <div th:fragment="alert">
th:include 布局标簽,替換内容到引入的檔案 <head th:include=”layout :: htmlhead” th:with=”title=’xx'”></head> />
th:replace 布局标簽,替換整個标簽到引入的檔案 <div th:replace=”fragments/header :: title”></div>
th:selected selected選擇框 選中 th:selected=”(${xxx.id} == ${configObj.dd})”
th:src 圖檔類位址引入 <img class=”img-responsive” alt=”App Logo” th:src=”@{/img/logo.png}” />
th:inline 定義js腳本可以使用變量 <script type="text/javascript" th:inline="javascript">
th:action 表單送出的位址
th:remove 删除某個屬性 <tr th:remove="all">
  1. all:删除包含标簽和所有的子節點。
  2. body:不包含标記删除,但删除其所有的子節點。
  3. tag:包含标記的删除,但不删除它的子節點。
  4. all-but-first:删除所有包含标簽的子節點,除了第一個。
  5. none:什麼也不做。這個值是有用的動态評估

5. springboot使用thymeleaf

引入依賴

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-thymeleaf</artifactId>

檔案都在templates中,字尾為HTML

<!DOCTYPE html>

<head>

    <meta charset="UTF-8">

    <title>Title</title>

</head>

<body>

<h1 th:text="${msg}">哈哈</h1>

<h2>

    <a href="www.atguigu.com" th:href="${link}">去百度</a>  <br/>

    <a href="www.atguigu.com" th:href="@{link}">去百度2</a>

</h2>

</body>

</html>

6. 抽取公共頁面

fragment:碎片;片段

聲明公共片段

th:fragment="xxx"

1、抽取公共片段

<div th:fragment="copy">

&copy; 2011 The Good Thymes Virtual Grocery

2、引入公共片段

<div th:insert="~{footer :: copy}"></div>

~{templatename::selector}:模闆名::選擇器

~{templatename::fragmentname}:模闆名::片段名

3、預設效果:

insert的公共片段在div标簽中

如果使用th:insert等屬性進行引入,可以不用寫~{}:

行内寫法可以加上:[[~{}]];[(~{})];

id="xxx"

<div id="commonScript">

    <script src="js/jquery-1.10.2.min.js"></script>

 ……

<div th:replace="common :: #commonScript"></div>

使用公共片段

th:insert="檔案名 :: 片段名"

  • th:insert

    :在目前标簽中插入指定的标簽
  • th:replace

    :用指定的标簽替換目前标簽
  • th:include

    :與 th:insert 類似,但它不是插入标簽,而是僅插入指定标簽包含的内容

<footer th:fragment="copy">

  1

</footer>

  <div th:insert="footer :: copy">

  </div>

  對應如下:

  <div>

    <footer>

      1

    </footer>

-----------------------------------------------------

  <div th:replace="footer :: copy">

  <footer>

    1

  </footer>

  <div th:include="footer :: copy">

片段的參數

給定義的片段

th:fragment

設定參數:

<div th:fragment="frag (onevar,twovar)">

    <p th:text="${onevar} + ' - ' + ${twovar}">...</p>

以下兩種方法調用片段都可以:

<div th:replace="::frag (${value1},${value2})">...</div>

<div th:replace="::frag (onevar=${value1},twovar=${value2})">...</div>

示例:

1、定義攜帶參數的片段

<!--header-->

<head th:fragment="header(title)">

    <title th:text="${title}">title</title>

2、使用攜帶參數的片段(參數用單引号包圍)

<head th:replace="_fragment::header('【摸魚寶典】堅持摸魚,堅持自我')"></head>

7. 攜帶請求參數

表達式裡加個括号,參數名=xxxx

<a th:href="@{/emp/delete(id=${emp.id})}">删除資訊</a>&nbsp;

<a th:href="@{/emp/find(id=${emp.id})}">修改資訊</a>

8. 相關采坑

HTML中的圖檔路徑要使用絕對路徑,即

/

開頭的路徑

找不到favicon.ico

使用一個标簽,告訴Thymeleaf解析時查找該檔案的位置,即便目标位置不存在該檔案,也不會報錯了

<link rel="shortcut icon" href="/favicon.ico" th:href="@{/favicon.ico}"/>

5. 攔截器

  1. 編寫一個攔截器實作HandlerInterceptor接口
  2. 攔截器注冊到容器中(實作WebMvcConfigurer的addInterceptors)
  3. 指定攔截規則【如果是攔截所有,靜态資源也會被攔截】

用于登入檢查的攔截器

1. 編寫攔截器類

package cn.upeveryday.springbootadmin.interceptor;

public class LoginInterceptor implements HandlerInterceptor {

    @Override

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        //登入檢查

        HttpSession session = request.getSession();

        Object loginUser = session.getAttribute("loginUser");

        if (loginUser!=null){

            return true;

        }else{

            //未登入,将請求轉發

            request.setAttribute("msg", "請先登入!");

            request.getRequestDispatcher("/").forward(request, response);

//            response.sendRedirect("/");

            return false;

        }

2. 将攔截器注冊到容器中

package cn.upeveryday.springbootadmin.config;

@Configuration

public class AdminWebConfig implements WebMvcConfigurer {

    public void addInterceptors(InterceptorRegistry registry) {

        //registry:注冊器

        registry.addInterceptor(new LoginInterceptor())

                .addPathPatterns("/**")//攔截所有請求,包括靜态資源

                .excludePathPatterns("/","/login",//放行登入請求

                        "/css/**","/js/**","/fonts/**","/images/**");//放行靜态資源的請求

6. 檔案上傳

前端代碼

1. form标簽的屬性

  1. th:action="@{請求路徑}"

  2. method="post"

  3. enctype="multipart/form-data"

2. input标簽的屬性

  1. type="file"

  2. 預設為單檔案上傳
  3. 添加

    multiple

    屬性後,可以實作多檔案上傳

<form role="form" th:action="@{/upload}" method="post" enctype="multipart/form-data">

    <div class="form-group">

        <label for="exampleInputFile">File input</label>

        <input name="file" type="file" id="exampleInputFile">

        <p class="help-block">Example block-level help text here.</p>

    </div>

        <label for="exampleInputFile">Files input</label>

        <input name="files" type="file" id="exampleInputFiles" multiple><!--多檔案上傳-->

後端代碼

  servlet:

    multipart:

      max-file-size: 10MB #單個檔案最大大小

      max-request-size: 100MB #整體上傳的最大大小

    /**

     * MultipartFile自動封裝上傳的檔案

     */

    @PostMapping("/upload")

    public String upload(@RequestParam("email")String email,

                         @RequestParam("password")String password,

                         @RequestParam("file")MultipartFile file,

                         @RequestParam("files")MultipartFile[] files){

        log.info("email={},password={},file={},files={}",

                email,password,file.getSize(),files.length);

        //判斷檔案是否為空

        if(!file.isEmpty()){

            //上傳至檔案伺服器

            ……

        return "main";

7. 異常處理

預設的錯誤響應

  1. 浏覽器,傳回一個預設的錯誤頁面
  2. 如果是其他用戶端,預設響應一個json資料

自定義錯誤響應

1. 定制錯誤的頁面

  1. 有模闆引擎

    templates

    的情況下;error/狀态碼.html (将錯誤頁面命名為 錯誤狀态碼.html 放在模闆引擎檔案夾

    templates

    裡面的 error檔案夾下),發生此狀态碼的錯誤就會來到對應的頁面;

我們可以使用4xx和5xx作為錯誤頁面的檔案名來比對這種類型的所有錯誤,精确優先(優先尋找精确的狀态碼.html);

頁面能擷取的資訊;

timestamp:時間戳

status:狀态碼

error:錯誤提示

exception:異常對象

message:異常消息

errors:JSR303資料校驗的錯誤都在這裡

将錯誤資訊從json中提取出來,放在錯誤頁面上展示

    <section class="error-wrapper text-center">

            <h3 th:text="${messgae}">Something went wrong.</h3>

            <p class="nrml-txt" th:text="${trace}">Or you can if </p>

        </section>

  1. 沒有模闆引擎(模闆引擎找不到這個錯誤頁面),靜态資源檔案夾下找;
  2. 以上都沒有錯誤頁面,就是預設來到SpringBoot預設的錯誤提示頁面;

2. 定制錯誤的json資料

使用

@ControllerAdvice

來聲明一些全局性的東西,最常見的是結合

@ExceptionHandler

注解用于全局異常的處理

@ControllerAdvice是在類上聲明的注解,其用法主要有三點:

  • @ExceptionHandler注解标注的方法:用于捕獲Controller中抛出的不同類型的異常,進而達到異常全局處理的目的;
  • @InitBinder注解标注的方法:用于請求中注冊自定義參數的解析,進而達到自定義請求參數格式的目的;
  • @ModelAttribute注解标注的方法:表示此方法會在執行目标Controller方法之前執行 。

8. 常用注解

@ControllerAdvice

@ControllerAdvice,是Spring3.2提供的新注解,它是一個Controller增強器,可對controller中被

@RequestMapping

注解的方法加一些邏輯處理。主要作用有一下三種

  • 通過@ControllerAdvice注解可以将對于控制器的全局配置放在同一個位置。
  • 注解了@ControllerAdvice的類的方法可以使用@ExceptionHandler、@InitBinder、@ModelAttribute注解到方法上。
    • **

      @ExceptionHandler

      :用于全局處理控制器裡的異常,進行全局異常處理**
    • @InitBinder:用來設定WebDataBinder,用于自動綁定前台請求參數到Model中,全局資料預處理。
    • @ModelAttribute:本來作用是綁定鍵值對到Model中,此處讓全局的@RequestMapping都能獲得在此處設定的鍵值對 ,全局資料綁定。
  • @ControllerAdvice注解将作用在所有注解了@RequestMapping的控制器的方法上。

全局異常處理

需要配合

@ExceptionHandler

使用。當将異常抛到controller時,可以對異常進行統一處理,規定傳回的json格式或是跳轉到一個錯誤頁面

/**

 * 統一處理所有異常(除了帶有ResponseStatus注解的異常)

 */

public class ControllerExceptionHandler {

    //日志

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @ExceptionHandler(Exception.class)//異常處理器:處理所有的異常

    public ModelAndView exceptionHandler(HttpServletRequest request,Exception e) throws Exception {

        //輸出日志:請求url + 錯誤資訊

        logger.error("RequestURL: {}, Exception: {}",

                request.getRequestURL(),e);

        //如果異常帶有ResponseStatus注解,那麼就交給springboot處理

        if(AnnotationUtils.findAnnotation(e.getClass(),ResponseStatus.class)!=null){

            throw e;

        //傳回ModelAndView:存儲資料并且轉發頁面

        ModelAndView mv = new ModelAndView();

        mv.addObject("url", request.getRequestURL());

        mv.addObject("exception", e);

        mv.setViewName("error/error");

        return mv;

錯誤頁面異常資訊顯示處理:把錯誤資訊放在error.html源碼的注釋中

<div>

    <div th:utext="'&lt;!--'" th:remove="tag"></div>

    <div th:utext="'Failed Request URL : ' + ${url}" th:remove="tag"></div>

    <div th:utext="'Exception message : ' + ${exception.message}" th:remove="tag"></div>

    <ul th:remove="tag">

        <li th:each="st : ${exception.stackTrace}" th:remove="tag"><span th:utext="${st}" th:remove="tag"></span></li>

    </ul>

    <div th:utext="'--&gt;'" th:remove="tag"></div>

@ResponseStatus

指定響應狀态碼——@ResponseStatus注解有兩種用法:

  1. 加在自定義異常類上
  2. 加在**

    @RequestMapping

    修飾的方法**上

三個參數:

  1. value和code作用一樣:設定異常的狀态碼
  2. reason是對于異常的描述

1. @ResponseStatus響應失敗

@RequestMapping(value = "/helloResponseStatus")

@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "404-NotFound")//響應404

@ResponseBody

public void helloResponseStatus(@RequestParam(value = "name") String name) {

    System.out.println("======");

2. @ResponseStatus響應成功

@RequestMapping(value = "/helloResponseStatus1")

@ResponseStatus(value = HttpStatus.OK, reason = "200-Success")//響應200

public void helloResponseStatus1(@RequestParam(value = "name") String name) {

3. @ResponseStatus作用在自定義異常類上

使用時,先聲明一個自定義異常類,在自定義異常類上面加上@ResponseStatus注釋表示系統運作期間,當抛出自定義異常的時候,将@ResponseStatus注解中的狀态碼code和異常資訊reason傳回給用戶端,提高可讀性。

 * 自定義異常類

@ResponseStatus(HttpStatus.NOT_FOUND)//指定響應狀态碼,springboot對應找到404.html

public class NotFoundException extends RuntimeException {

    public NotFoundException() {

        super();

    public NotFoundException(String s) {

        super(s);

    public NotFoundException(String s, Throwable throwable) {

        super(s, throwable);

其他

3. 容器功能

1. 添加元件

springboot不再使用xml配置檔案,而是直接将一個類作為配置類

  1. @Configuration

    注解作用在一個類上,表明目前類是一個配置類
  2. 配置類本身也是容器中的元件,可以通過容器擷取到

MyConfig bean = run.getBean(MyConfig.class);

  1. @Configuration

    注解有一個屬性

    proxyBeanMethods

    :是否代理配置類中被

    @bean

    标注的方法,預設為true
    • proxyBeanMethods=true

      ,此時從容器中擷取的配置類對象是CGLIB動态代理對象,通過配置類對象調用方法時,擷取的對象是單例的。這是FULL模式

      解決元件依賴的問題:配置類元件之間有依賴關系,方法會被調用得到之前的單例元件

    • proxyBeanMethods=false

      ,此時從容器中擷取的配置類對象是普通類對象,通過普通類對象調用方法,擷取的對象是多例的。這是LITE模式
  1. FULL模式與LITE模式分别适用的場景:
    1. FULL模式:配置類元件之間有依賴關系,SpringBoot總會檢查這個元件是否在容器中有,有則使用,沒有則建立
    2. LITE模式:配置類元件之間無依賴關系用Lite模式加速容器啟動過程,減少判斷

@Bean

配置類裡面使用

@Bean

注解标注在方法上給容器注冊元件,預設是單例的,如果傳入了對象參數,這個參數的值就會從容器中找

方法名是元件的id,傳回類型是元件類型,傳回值是元件在容器中的執行個體

    //元件id:tom

 //元件類型:Pet

 //容器中執行個體:new Pet("tomcat")

 @Bean("tom")

    public Pet tomcatPet(){

        return new Pet("tomcat");

@Component、@Controller、@Service、@Repository

spring中的注解在springboot中同樣适用

@Import

作用在類上

 * 4、@Import({User.class, DBHelper.class})

 *      在容器中調用無參構造,自動建立出這兩個類型的元件,預設元件id為全類名

@Import({User.class, DBHelper.class})

@Conditional

條件裝配:滿足Conditional指定的條件,則進行元件注入

作用在類上:目前條件不符合,整個類都不執行

作用在方法上:目前條件不符合,整個方法不執行

=====================測試條件裝配==========================

@Configuration(proxyBeanMethods = false)

//@ConditionalOnBean(name = "tom")

@ConditionalOnMissingBean(name = "tom")

    public User user01(){

        User zhangsan = new User("zhangsan", 18);

        zhangsan.setPet(tomcatPet());

        return zhangsan;

    @Bean("tom22")

2. 原生配置檔案引入

@ImportResource

spring是使用的xml檔案配置元件,springboot通過該注解讀取xml檔案,進而将檔案中配置的元件添加到容器

@ImportResource("classpath:beans.xml")

public class MyConfig {}

4. 自動配置原理

1. 引導加載自動配置類

主程式上的注解是

public class BootWeb01Application {

點進去檢視

@SpringBootApplication

源碼,發現這是一個合成注解

@ComponentScan()

public @interface SpringBootApplication {

相當于

@Configuration

,表示主程式是一個配置類

@ComponentScan

指定對哪些包進行元件掃描

開啟自動配置

@AutoConfigurationPackage

@Import({AutoConfigurationImportSelector.class})

public @interface EnableAutoConfiguration {

自動配置包:指定了預設的包規則

@Import({Registrar.class})

public @interface AutoConfigurationPackage {}

//利用Registrar将主程式所在包下的所有元件導入容器

檢視Registrar類

    static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

        //AnnotationMetadata metadata:注解元資訊

        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {

            AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));

        public Set<Object> determineImports(AnnotationMetadata metadata) {

            return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));

檢視AutoConfigurationImportSelector類的源碼

  1. 利用

    getAutoConfigurationEntry(annotationMetadata);

    給容器中批量導入一些元件
  2. 調用

    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)

    擷取到所有需要導入到容器中的配置類
  3. 利用工廠加載

    Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader)

    得到所有的元件
  4. 從META-INF/spring.factories位置來加載一個檔案,檔案裡面寫死了springboot一啟動就要給容器中加載的所有配置類

    預設掃描所有依賴中的META-INF/spring.factories檔案

    spring-boot-autoconfigure-2.3.4.RELEASE.jar包裡面也有META-INF/spring.factories

2. 按需開啟自動配置項

  1. 雖然所有自動配置啟動的時候預設全部加載。xxxxAutoConfiguration
  2. 按照條件裝配規則(@Conditional),最終會按需配置(隻有條件生效,自動配置類才能生效)

檢測檔案上傳解析器,防止我們自己配置的解析器名字不符合規範:

        @Bean

  @ConditionalOnBean(MultipartResolver.class)  //容器中有這個類型的元件

  @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) //容器中沒有這個名字 multipartResolver的元件

  public MultipartResolver multipartResolver(MultipartResolver resolver) {

            //給@Bean标注的方法傳入了對象參數,這個參數的值就會從容器中找。

   return resolver;

  }

3. 修改預設配置

SpringBoot預設會在底層配好所有的元件,但是如果使用者自己配置了以使用者的優先

  • SpringBoot先加載所有的自動配置類 xxxxxAutoConfiguration
  • 每個自動配置類按照條件進行生效,預設都會綁定配置檔案指定的值

    元件中的值從xxxxProperties裡面拿,而xxxProperties和配置檔案進行了綁定

  • 生效的配置類就會給容器中裝配很多元件
  • 隻要容器中有這些元件,相當于這些功能就有了
  • 修改預設元件的預設配置
    • 直接在配置類中@Bean替換底層的元件
    • 檢視這個元件是擷取的配置檔案什麼值,在配置檔案中修改

加載xxxxxAutoConfiguration ---> 添加元件 ---> 元件從xxxxProperties裡面拿值 ----> application.properties

5. 開發小技巧

1. Lombok

簡化JavaBean和日志的開發

  1. idea中搜尋安裝lombok插件
  2. 導入maven依賴

            <groupId>org.projectlombok</groupId>

            <artifactId>lombok</artifactId>

使用示例:

===============================簡化JavaBean開發===================================

@NoArgsConstructor //全參構造器,某幾個參數的構造器就需要手動寫了

@AllArgsConstructor //無參構造器

@Data    //生成getter/setter方法

@ToString   //toString方法

@EqualsAndHashCode //equals和hashCode方法

    private String name;

    private Integer age;

    private Pet pet;

================================簡化日志開發===================================

@Slf4j //給目前類注入日志log對象

@RestController

public class HelloController {

    @RequestMapping("/hello")

    public String handle01(@RequestParam("name") String name){

        log.info("請求進來了....");

        return "Hello, Spring Boot 2!"+"你好:"+name;

2. dev-tools

            <artifactId>spring-boot-devtools</artifactId>

            <optional>true</optional>

項目或者頁面修改以後:Ctrl+F9

6. 代碼技巧

1. 對象的整體複制

将一個對象指派給另一個對象,使用spring自帶的

BeanUtils.copyProperties(source, target);

@Override

@Transactional

public Integer updateType(Long id, Type type) {

    Type t = typeMapper.selectById(id);

    if(t==null){

        throw new NotFoundException("不存在");

    //spring提供的将一個對象的值批量賦給另一個對象,适合在更新操作時使用,避免了自己手動指派

    BeanUtils.copyProperties(type, t);

    int i = typeMapper.updateById(t);

    return i;