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-starter4、見到的 *-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
字元串無需加引号,如果要加
-
會忽略轉義字元,即将' '
作為字元串輸出\n
-
會識别轉義字元,即将" "
作為換行輸出\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配置方式
-
多profile檔案方式:提供多個配置檔案,每個代表一種環境。
application-dev.properties/yml——開發環境
application-test.properties/yml——測試環境
application-pro.properties/yml——生産環境
- yml多文檔方式:在yml中使用
分隔不同配置---
---
server:
port: 8081
#給目前配置部分起别名
spring:
profiles: dev
port: 8082
profiles: test
port: 8083
profiles: pro
#激活配置檔案
profiles:
active: dev
profile激活方式
- 配置檔案: 在配置檔案中配置:
spring.profiles.active=dev
- 虛拟機參數:在VM options 指定:
-Dspring.profiles.active=dev
- 指令行參數:
java –jar xxx.jar --spring.profiles.active=dev
5. 内部配置加載順序
Springboot程式啟動時,會從以下位置加載配置檔案:
- file:./config/:目前項目下的/config目錄下
- file:./ :目前項目的根目錄
- classpath:/config/:classpath的/config目錄
- 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;
- 将系統中其他日志架構先排除出去
- 用中間包來替換原有的日志架構
- 我們導入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級别,即指定包下使用相應的日志級别
- 這裡除了
包下的日志是debug級别,其他日志是info級别cn.upeveryday
四、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頁面
靜态資源通路字首:
- 預設無字首
mvc:
static-path-pattern: /**
- 設定字首
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,需要在配置檔案中手動開啟
用法:
- HTML的表單送出方法為post,增加隐藏域
name="_method" value="put"
- 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原理
- HTML表單送出要使用REST時
-
- 表單送出會帶上_method=PUT
- 請求過來被
攔截HiddenHttpMethodFilter
- 檢查表單請求是否正常,并且是POST請求
- 擷取到_method的值。相容這些請求:PUT.DELETE.PATCH
- 原生request(post),包裝模式requesWrapper重寫了getMethod方法,傳回的是傳入的值。
- 過濾器鍊放行的時候用wrapper,以後的方法調用getMethod得到的是requesWrapper重寫後的PUT
-
用戶端工具發送請求使用rest
如PostMan直接發送Put、delete等方式請求,無需Filter
參數處理原理
- DispatcherServlet通過處理器映射器HandlerMapping找到能夠處理請求的處理器Handler(控制器中的方法)
- 為目前Handler找一個處理器擴充卡HandlerAdapter
- 擴充卡執行目标方法并确定方法參數的每一個值
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"> |
- all:删除包含标簽和所有的子節點。
- body:不包含标記删除,但删除其所有的子節點。
- tag:包含标記的删除,但不删除它的子節點。
- all-but-first:删除所有包含标簽的子節點,除了第一個。
- 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">
© 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:insert 類似,但它不是插入标簽,而是僅插入指定标簽包含的内容th:include
<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>
<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. 攔截器
- 編寫一個攔截器實作HandlerInterceptor接口
- 攔截器注冊到容器中(實作WebMvcConfigurer的addInterceptors)
- 指定攔截規則【如果是攔截所有,靜态資源也會被攔截】
用于登入檢查的攔截器
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标簽的屬性
-
th:action="@{請求路徑}"
-
method="post"
-
enctype="multipart/form-data"
2. input标簽的屬性
-
type="file"
- 預設為單檔案上傳
- 添加
屬性後,可以實作多檔案上傳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. 異常處理
預設的錯誤響應
- 浏覽器,傳回一個預設的錯誤頁面
- 如果是其他用戶端,預設響應一個json資料
自定義錯誤響應
1. 定制錯誤的頁面
- 有模闆引擎
的情況下;error/狀态碼.html (将錯誤頁面命名為 錯誤狀态碼.html 放在模闆引擎檔案夾templates
裡面的 error檔案夾下),發生此狀态碼的錯誤就會來到對應的頁面;templates
我們可以使用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>
- 沒有模闆引擎(模闆引擎找不到這個錯誤頁面),靜态資源檔案夾下找;
- 以上都沒有錯誤頁面,就是預設來到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="'<!--'" 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="'-->'" th:remove="tag"></div>
@ResponseStatus
指定響應狀态碼——@ResponseStatus注解有兩種用法:
- 加在自定義異常類上
- 加在**
修飾的方法**上@RequestMapping
三個參數:
- value和code作用一樣:設定異常的狀态碼
- 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配置檔案,而是直接将一個類作為配置類
-
注解作用在一個類上,表明目前類是一個配置類@Configuration
- 配置類本身也是容器中的元件,可以通過容器擷取到
MyConfig bean = run.getBean(MyConfig.class);
-
注解有一個屬性@Configuration
:是否代理配置類中被proxyBeanMethods
标注的方法,預設為true@bean
-
- 當
proxyBeanMethods=true
,此時從容器中擷取的配置類對象是CGLIB動态代理對象,通過配置類對象調用方法時,擷取的對象是單例的。這是FULL模式
解決元件依賴的問題:配置類元件之間有依賴關系,方法會被調用得到之前的單例元件
-
,此時從容器中擷取的配置類對象是普通類對象,通過普通類對象調用方法,擷取的對象是多例的。這是LITE模式proxyBeanMethods=false
- 當
- FULL模式與LITE模式分别适用的場景:
-
- FULL模式:配置類元件之間有依賴關系,SpringBoot總會檢查這個元件是否在容器中有,有則使用,沒有則建立
- 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類的源碼
- 利用
給容器中批量導入一些元件getAutoConfigurationEntry(annotationMetadata);
- 調用
擷取到所有需要導入到容器中的配置類List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)
- 利用工廠加載
得到所有的元件Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader)
-
從META-INF/spring.factories位置來加載一個檔案,檔案裡面寫死了springboot一啟動就要給容器中加載的所有配置類
預設掃描所有依賴中的META-INF/spring.factories檔案
spring-boot-autoconfigure-2.3.4.RELEASE.jar包裡面也有META-INF/spring.factories
2. 按需開啟自動配置項
- 雖然所有自動配置啟動的時候預設全部加載。xxxxAutoConfiguration
- 按照條件裝配規則(@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和日志的開發
- idea中搜尋安裝lombok插件
- 導入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;