
一、Spring與spring boot
1、spring能做什么
spring的能力:
微服務(wù)、響應(yīng)式編程、云、web、Serverless、
Event Driven、Batch
spring的生態(tài):
覆蓋了web開發(fā)、數(shù)據(jù)訪問、安全控制、分布式、消息服務(wù)、移動(dòng)開發(fā)、批處理
2、為什么用spring boot
springboot優(yōu)點(diǎn):
創(chuàng)建獨(dú)立spring應(yīng)用、內(nèi)嵌web服務(wù)器、自動(dòng)starter依賴,簡化構(gòu)建配置、自動(dòng)配置spring以及第三方功能、提供生產(chǎn)級(jí)別的監(jiān)控、健康檢查以及外部優(yōu)化配置、無代碼生成無需編寫XML
springboot缺點(diǎn):
版本迭代快、封裝太深不易精通
3、如何學(xué)習(xí)SpringBoot
官方文檔架構(gòu)
二、springboot2入門
1、系統(tǒng)要求
- java8 & 兼容java14 .
- Maven 3.3+
- idea 2019.1.2
2 、HelloWorld
需求:瀏覽發(fā)送/hello請求,響應(yīng) Hello,Spring Boot 2
2.1、創(chuàng)建maven工程
2.2、引入依賴
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
2.3、創(chuàng)建主程序
/**
* 主程序類
* @SpringBootApplication:這是一個(gè)SpringBoot應(yīng)用
*/
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class,args);
}
}
2.4、編寫業(yè)務(wù)
@RestController
public class HelloController {
@RequestMapping("/hello")
public String handle01(){
return "Hello, Spring Boot 2!";
}
}
2.5、測試
直接運(yùn)行main方法
2.6、簡化配置
application.properties
server.port=8888
2.7、簡化部署
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
把項(xiàng)目打成jar包,直接在目標(biāo)服務(wù)器執(zhí)行即可。
三、了解自動(dòng)配置原理

1、Spring boot特點(diǎn)
1.1、依賴管理
- 父項(xiàng)目做依賴管理
依賴管理
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
</parent>
他的父項(xiàng)目
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.4.RELEASE</version>
</parent>
幾乎聲明了所有開發(fā)中常用的依賴的版本號(hào),自動(dòng)版本仲裁機(jī)制
- 開發(fā)導(dǎo)入starter場景啟動(dòng)器
1、見到很多 spring-boot-starter-* : *就某種場景
2、只要引入starter,這個(gè)場景的所有常規(guī)需要的依賴我們都自動(dòng)引入
3、SpringBoot所有支持的場景
https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter
4、見到的 *-spring-boot-starter: 第三方為我們提供的簡化開發(fā)的場景啟動(dòng)器。
5、所有場景啟動(dòng)器最底層的依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.3.4.RELEASE</version>
<scope>compile</scope>
</dependency>
- 無需關(guān)注版本號(hào),自動(dòng)版本仲裁
1、引入依賴默認(rèn)都可以不寫版本
2、引入非版本仲裁的jar,要寫版本號(hào)。
- 可以修改默認(rèn)版本號(hào)
1、查看spring-boot-dependencies里面規(guī)定當(dāng)前依賴的版本 用的 key。
2、在當(dāng)前項(xiàng)目里面重寫配置
<properties>
<mysql.version>5.1.43</mysql.version>
</properties>
1.2、自動(dòng)配置
- 自動(dòng)配好Tomcat
○ 引入Tomcat依賴。
○ 配置Tomcat
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.3.4.RELEASE</version>
<scope>compile</scope>
</dependency>
- 自動(dòng)配好SpringMVC
○ 引入SpringMVC全套組件
○ 自動(dòng)配好SpringMVC常用組件(功能) - 自動(dòng)配好Web常見功能,如:字符編碼問題
SpringBoot幫我們配置好了所有web開發(fā)的常見場景 - 默認(rèn)的包結(jié)構(gòu)
○ 主程序所在包及其下面的所有子包里面的組件都會(huì)被默認(rèn)掃描進(jìn)來
○ 無需以前的包掃描配置
○ 想要改變掃描路徑,
@SpringBootApplication(scanBasePackages="com.atguigu")
或者@ComponentScan 指定掃描路徑
@SpringBootApplication
等同于
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.atguigu.boot")
- 各種配置擁有默認(rèn)值
○ 默認(rèn)配置最終都是映射到某個(gè)類上,如:MultipartProperties
○ 配置文件的值最終會(huì)綁定每個(gè)類上,這個(gè)類會(huì)在容器中創(chuàng)建對(duì)象 - 按需加載所有自動(dòng)配置項(xiàng)
○ 非常多的starter
○ 引入了哪些場景這個(gè)場景的自動(dòng)配置才會(huì)開啟
○ SpringBoot所有的自動(dòng)配置功能都在 spring-boot-autoconfigure 包里面
2、容器功能
2.1、組件添加
1、@Configuration
■ 配置類組件之間無依賴關(guān)系用Lite模式加速容器啟動(dòng)過程,減少判斷
■ 配置類組件之間有依賴關(guān)系,方法會(huì)被調(diào)用得到之前單實(shí)例組件,用Full模式
#############################Configuration使用示例######################################################
/**
* 1、配置類里面使用@Bean標(biāo)注在方法上給容器注冊組件,默認(rèn)也是單實(shí)例的
* 2、配置類本身也是組件
* 3、proxyBeanMethods:代理bean的方法
* Full(proxyBeanMethods = true)、【保證每個(gè)@Bean方法被調(diào)用多少次返回的組件都是單實(shí)例的】
* Lite(proxyBeanMethods = false)【每個(gè)@Bean方法被調(diào)用多少次返回的組件都是新創(chuàng)建的】
* 組件依賴必須使用Full模式默認(rèn)。其他默認(rèn)是否Lite模式
*
*
*
*/
@Configuration(proxyBeanMethods = false) //告訴SpringBoot這是一個(gè)配置類 == 配置文件
public class MyConfig {
/**
* Full:外部無論對(duì)配置類中的這個(gè)組件注冊方法調(diào)用多少次獲取的都是之前注冊容器中的單實(shí)例對(duì)象
* @return
*/
@Bean //給容器中添加組件。以方法名作為組件的id。返回類型就是組件類型。返回的值,就是組件在容器中的實(shí)例
public User user01(){
User zhangsan = new User("zhangsan", 18);
//user組件依賴了Pet組件
zhangsan.setPet(tomcatPet());
return zhangsan;
}
@Bean("tom")
public Pet tomcatPet(){
return new Pet("tomcat");
}
}
################################@Configuration測試代碼如下########################################
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.atguigu.boot")
public class MainApplication {
public static void main(String[] args) {
//1、返回我們IOC容器
ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
//2、查看容器里面的組件
String[] names = run.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
//3、從容器中獲取組件
Pet tom01 = run.getBean("tom", Pet.class);
Pet tom02 = run.getBean("tom", Pet.class);
System.out.println("組件:"+(tom01 == tom02));
//4、com.atguigu.boot.config.MyConfig$$EnhancerBySpringCGLIB$$51f1e1ca@1654a892
MyConfig bean = run.getBean(MyConfig.class);
System.out.println(bean);
//如果@Configuration(proxyBeanMethods = true)代理對(duì)象調(diào)用方法。SpringBoot總會(huì)檢查這個(gè)組件是否在容器中有。
//保持組件單實(shí)例
User user = bean.user01();
User user1 = bean.user01();
System.out.println(user == user1);
User user01 = run.getBean("user01", User.class);
Pet tom = run.getBean("tom", Pet.class);
System.out.println("用戶的寵物:"+(user01.getPet() == tom));
}
}
2、@Bean、@Component、@Controller、@Service、@Repository
3、@ComponentScan、@Import
@Import({User.class, DBHelper.class})
* 給容器中自動(dòng)創(chuàng)建出這兩個(gè)類型的組件、默認(rèn)組件的名字就是全類名
@Import({User.class, DBHelper.class})
@Configuration(proxyBeanMethods = false) //告訴SpringBoot這是一個(gè)配置類 == 配置文件
public class MyConfig {
}
4.@Conditional
條件裝配:滿足Conditional指定的條件,則進(jìn)行組件注入
=====================測試條件裝配==========================
@Configuration(proxyBeanMethods = false) //告訴SpringBoot這是一個(gè)配置類 == 配置文件
//@ConditionalOnBean(name = "tom")
@ConditionalOnMissingBean(name = "tom")
public class MyConfig {
/**
* Full:外部無論對(duì)配置類中的這個(gè)組件注冊方法調(diào)用多少次獲取的都是之前注冊容器中的單實(shí)例對(duì)象
* @return
*/
@Bean //給容器中添加組件。以方法名作為組件的id。返回類型就是組件類型。返回的值,就是組件在容器中的實(shí)例
public User user01(){
User zhangsan = new User("zhangsan", 18);
//user組件依賴了Pet組件
zhangsan.setPet(tomcatPet());
return zhangsan;
}
@Bean("tom22")
public Pet tomcatPet(){
return new Pet("tomcat");
}
}
public static void main(String[] args) {
//1、返回我們IOC容器
ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
//2、查看容器里面的組件
String[] names = run.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
boolean tom = run.containsBean("tom");
System.out.println("容器中Tom組件:"+tom);
boolean user01 = run.containsBean("user01");
System.out.println("容器中user01組件:"+user01);
boolean tom22 = run.containsBean("tom22");
System.out.println("容器中tom22組件:"+tom22);
}
2.2原生配置文件引入
1、@ImportResource
@ImportResource("classpath:beans.xml")
public class MyConfig {}
2.3 配置綁定
如何使用Java讀取到properties文件中的內(nèi)容,并且把它封裝到JavaBean中,以供隨時(shí)使用;
1、@ConfigurationProperties
/**
* 只有在容器中的組件,才會(huì)擁有SpringBoot提供的強(qiáng)大功能
*/
@Component
@ConfigurationProperties(prefix = "mycar")
public class Car {
private String brand;
private Integer price;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public Integer getPrice() {
return price;
}
public void setPrice(Integer price) {
this.price = price;
}
@Override
public String toString() {
return "Car{" +
"brand='" + brand + '\'' +
", price=" + price +
'}';
}
}
方法一:@EnableConfigurationProperties + @ConfigurationProperties
方法二:@Component + @ConfigurationProperties
@EnableConfigurationProperties(Car.class)
* 開啟car的配置綁定功能,開啟后Car類中就可以不用寫@Component
* 把Car這個(gè)組件注冊到容器中
public class MyConfig {
}
3、自動(dòng)配置原理入門
3.1、引導(dǎo)加載自動(dòng)配置類
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication{}
1、@SpringBootConfiguration
@Configuration。代表當(dāng)前是一個(gè)配置類
2、@ComponentScan
指定掃描哪些,Spring注解;
3、@EnableAutoConfiguration
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}
@AutoConfigurationPackage
自動(dòng)配置包?指定了默認(rèn)的包規(guī)則
@Import(AutoConfigurationPackages.Registrar.class) //給容器中導(dǎo)入一個(gè)組件
public @interface AutoConfigurationPackage {}
//利用Registrar給容器中導(dǎo)入一系列組件
//將指定的一個(gè)包下的所有組件導(dǎo)入進(jìn)來?MainApplication 所在包下。
@Import(AutoConfigurationImportSelector.class)
1、利用getAutoConfigurationEntry(annotationMetadata);給容器中批量導(dǎo)入一些組件
2、調(diào)用List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)獲取到所有需要導(dǎo)入到容器中的配置類
3、利用工廠加載 Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader);得到所有的組件
4、從META-INF/spring.factories位置來加載一個(gè)文件。
默認(rèn)掃描我們當(dāng)前系統(tǒng)里面所有META-INF/spring.factories位置的文件
spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面也有META-INF/spring.factories
文件里面寫死了spring-boot一啟動(dòng)就要給容器中加載的所有配置類
3.2、按需開啟自動(dòng)配置項(xiàng)
雖然我們127個(gè)場景的所有自動(dòng)配置啟動(dòng)的時(shí)候默認(rèn)全部加載。xxxxAutoConfiguration
按照條件裝配規(guī)則(@Conditional),最終會(huì)按需配置。
3.3、修改默認(rèn)配置
SpringBoot默認(rèn)會(huì)在底層配好所有的組件。但是如果用戶自己配置了以用戶的優(yōu)先
用戶可以使用@Bean注解來或者修改配置文件來自己配置組件
@Bean
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
}
總結(jié):
● SpringBoot先加載所有的自動(dòng)配置類 xxxxxAutoConfiguration
● 每個(gè)自動(dòng)配置類按照條件進(jìn)行生效,默認(rèn)都會(huì)綁定配置文件指定的值。xxxxProperties里面拿。xxxProperties和配置文件進(jìn)行了綁定
● 生效的配置類就會(huì)給容器中裝配很多組件
● 只要容器中有這些組件,相當(dāng)于這些功能就有了
● 定制化配置
○ 用戶直接自己@Bean替換底層的組件
○ 用戶去看這個(gè)組件是獲取的配置文件什么值就去修改。
xxxxxAutoConfiguration ---> 組件 ---> xxxxProperties里面拿值 ----> application.properties
3.4 最佳實(shí)踐
1、查看自動(dòng)配置了哪些
- 自己分析,引入場景對(duì)應(yīng)的自動(dòng)配置一般都生效了。
- 或配置文件中debug=true開啟自動(dòng)配置報(bào)告。Negative(不生效)\Positive(生效)
2、是否需要修改配置
- 可以參照文檔修改配置項(xiàng)
- 可以自己分析。xxxxProperties綁定了配置文件的哪些。
- 可以自定義加入或者替換組件
@Bean、@Component。。。 - 可以使用自定義器 XXXXXCustomizer;
4、開發(fā)小技巧
4.1、Lombok
================================簡化日志開發(fā)===================================
@Slf4j
@RestController
public class HelloController {
@RequestMapping("/hello")
public String handle01(@RequestParam("name") String name){
log.info("請求進(jìn)來了....");
return "Hello, Spring Boot 2!"+"你好:"+name;
}
}
4.2、dev-tools
熱部署
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
項(xiàng)目或者頁面修改以后:Ctrl+F9;
4.3、Spring Initailizr
自動(dòng)依賴引入、自動(dòng)創(chuàng)建項(xiàng)目結(jié)構(gòu)、自動(dòng)編寫好主配置類
四、配置文件
文件類型可為properties 或者 yaml
增加配置提示
自定義的類和配置文件綁定一般沒有提示。
可加入以下依賴使得在寫yml文件時(shí),自定義的類也會(huì)有提示
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
五、深入Web原理

1、springmvc自動(dòng)配置概覽
- 大部分都已經(jīng)幫助我們自動(dòng)配置,包括:
內(nèi)容協(xié)商視圖解析器和BeanName視圖解析器
靜態(tài)資源(包括webjars)
自動(dòng)注冊Converter, GenericConverter, Formatter
支持HttpMessageConverters
自動(dòng)注冊MessageCodesResolver(國際化用)
靜態(tài)index.html支持
自定義Favicon
自動(dòng)使用ConfigurableWebBindingInitializer, (DataBinder負(fù)責(zé)將請求數(shù)據(jù)綁定到JavaBean上) - 自定義MVC方法:
①不用@EnableWebMvc注解,使用@Configuration+WebMvcConfigurer自定義規(guī)則
②聲明WebMvcRegistrations 改變默認(rèn)底層組件
③使用@EnableWebMvc+@configuration+DelegatingWebMvcConfiguration全面接管SpringMVC
2、簡單功能分析
2.1靜態(tài)資源訪問
1.靜態(tài)資源目錄
只要靜態(tài)資源放在類路徑下: called /static (or /public or /resources or /META-INF/resources
訪問 : 當(dāng)前項(xiàng)目根路徑/ + 靜態(tài)資源名
原理: 靜態(tài)映射/**。
請求進(jìn)來,先去找Controller看能不能處理。不能處理的所有請求又都交給靜態(tài)資源處理器。靜態(tài)資源也找不到則響應(yīng)404頁面
改變默認(rèn)的靜態(tài)資源路徑:
spring:
resources:
static-locations: [classpath:/haha/]
2、靜態(tài)資源訪問前綴
spring:
mvc:
static-path-pattern: /res/**
當(dāng)前項(xiàng)目 + static-path-pattern + 靜態(tài)資源名 = 靜態(tài)資源文件夾下找
3、webjar
自動(dòng)映射 /webjars/** (添加依賴后,路徑mapping也可以直接訪問webjars里面的東西,webjars也算靜態(tài)資源)
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.5.1</version>
</dependency>
eg:http://localhost:8080/webjars/jquery/3.5.1/jquery.js
2.2歡迎頁支持
靜態(tài)資源路徑下 index.html
可以配置靜態(tài)資源路徑
但是不可以配置靜態(tài)資源的訪問前綴。否則導(dǎo)致 index.html不能被默認(rèn)訪問
controller能處理/index
2.3自定義Favicon
favicon.ico 放在靜態(tài)資源目錄下即可
2.4靜態(tài)資源配置原理
SpringMvc功能的自動(dòng)配置類WebMvcAutoConfiguration生效->配置文件的相關(guān)屬性和xxx類進(jìn)行了綁定。(WebMvcProperties==spring.mvc、ResourceProperties==spring.resources)
spring:
resources:
add-mappings: false 禁用所有靜態(tài)資源規(guī)則
3、請求參數(shù)原理
3.1 請求映射
1、rest使用與原理
● @xxxMapping;
● Rest風(fēng)格支持(使用HTTP請求方式動(dòng)詞來表示對(duì)資源的操作)
○ 以前:/getUser 獲取用戶 /deleteUser 刪除用戶 /editUser 修改用戶 /saveUser 保存用戶
○ 現(xiàn)在: /user GET-獲取用戶 DELETE-刪除用戶 PUT-修改用戶 POST-保存用戶
○ 核心Filter;HiddenHttpMethodFilter
■ 用法: 表單method=post,隱藏域 _method=put
■ SpringBoot中手動(dòng)開啟
Rest原理(表單提交要使用REST的時(shí)候)
● 表單提交會(huì)帶上_method=PUT
● 請求過來被HiddenHttpMethodFilter攔截
○ 請求是否正常,并且是POST
■ 獲取到_method的值。
■ 兼容以下請求;PUT.DELETE.PATCH
■ 原生request(post),包裝模式requesWrapper重寫了getMethod方法,返回的是傳入的值。
■ 過濾器鏈放行的時(shí)候用wrapper。以后的方法調(diào)用getMethod是調(diào)用requesWrapper的。
Rest使用客戶端工具,
● 如PostMan直接發(fā)送Put、delete等方式請求,無需Filter
2請求映射原理
SpringMVC功能分析都從 org.springframework.web.servlet.DispatcherServlet->doDispatch()
RequestMappingHandlerMapping:保存了所有@RequestMapping 和handler的映射規(guī)則。
所有的請求映射(controller路徑與資源的映射)都在HandlerMapping中。
● 請求進(jìn)來,挨個(gè)嘗試所有的HandlerMapping看是否有請求信息。
○ 如果有就找到這個(gè)請求對(duì)應(yīng)的handler
○ 如果沒有就是下一個(gè) HandlerMapping
3.2普通參數(shù)與基本注解
2.1、注解
@PathVariable、@RequestHeader、@ModelAttribute、@RequestParam、@MatrixVariable、@CookieValue、@RequestBody
@MatrixVariable 矩陣變量:
//1、語法: 請求路徑:/cars/sell;low=34;brand=byd,audi,yd
//2、SpringBoot默認(rèn)是禁用了矩陣變量的功能
// 手動(dòng)開啟:原理。對(duì)于路徑的處理。UrlPathHelper進(jìn)行解析。
// removeSemicolonContent(移除分號(hào)內(nèi)容)支持矩陣變量的
//3、矩陣變量必須有url路徑變量才能被解析
@GetMapping("/cars/{path}")
public Map carsSell(@MatrixVariable("low") Integer low,
@MatrixVariable("brand") List<String> brand,
@PathVariable("path") String path){
Map<String,Object> map = new HashMap<>();
map.put("low",low);
map.put("brand",brand);
map.put("path",path);
return map;
}
// /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<String,Object> map = new HashMap<>();
map.put("bossAge",bossAge);
map.put("empAge",empAge);
return map;
}
2.2 Servlet API:
ServletRequestMethodArgumentResolver 可以解析的參數(shù):
WebRequest、ServletRequest、MultipartRequest、 HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId
2.3 復(fù)雜參數(shù):
Map、Model(map、model里面的數(shù)據(jù)會(huì)被放在request的請求域 request.setAttribute)、Errors/BindingResult、RedirectAttributes( 重定向攜帶數(shù)據(jù))、ServletResponse(response)、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder
Map<String,Object> map, Model model, HttpServletRequest request 都是可以給request域中放數(shù)據(jù),
Map和Model的底層都是同一個(gè)對(duì)象
3.3POJO封裝過程
ServletModelAttributeMethodProcessor
3.4參數(shù)處理原理
● HandlerMapping中找到能處理請求的Handler(Controller.method())
● 為當(dāng)前Handler 找一個(gè)適配器 HandlerAdapter; RequestMappingHandlerAdapter
● 適配器執(zhí)行目標(biāo)方法并確定方法參數(shù)的每一個(gè)值
處理步驟:
1、HandlerAdapter
2、執(zhí)行目標(biāo)方法
3、參數(shù)解析器-HandlerMethodArgumentResolver
確定將要執(zhí)行的目標(biāo)方法的每一個(gè)參數(shù)的值是什么;
SpringMVC目標(biāo)方法能寫多少種參數(shù)類型。取決于參數(shù)解析器
● 當(dāng)前解析器是否支持解析這種參數(shù)
● 支持就調(diào)用 resolveArgument
4、返回值處理器
5、如何確定目標(biāo)方法每一個(gè)參數(shù)的值
5.1 挨個(gè)判斷所有參數(shù)解析器那個(gè)支持解析這個(gè)參數(shù)
5.2 解析這個(gè)參數(shù)的值
5.3 自定義類型參數(shù) 封裝POJO
ServletModelAttributeMethodProcessor 這個(gè)參數(shù)處理器支持是否為簡單類型。
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
WebDataBinder :web數(shù)據(jù)綁定器,將請求參數(shù)的值綁定到指定的JavaBean里面
WebDataBinder 利用它里面的 Converters 將請求數(shù)據(jù)轉(zhuǎn)成指定的數(shù)據(jù)類型。再次封裝到JavaBean中
GenericConversionService:在設(shè)置每一個(gè)值的時(shí)候,找它里面的所有converter那個(gè)可以將這個(gè)數(shù)據(jù)類型(request帶來參數(shù)的字符串)轉(zhuǎn)換到指定的類型
6、目標(biāo)方法執(zhí)行完成
將所有的數(shù)據(jù)都放在ModelAndViewContainer;包含要去的頁面地址View。還包含Model數(shù)據(jù)。
7、處理派發(fā)結(jié)果
暴露模型作為請求域?qū)傩?br>
exposeModelAsRequestAttributes(model, request);
model中的所有數(shù)據(jù)遍歷挨個(gè)放在請求域中
4、數(shù)據(jù)相應(yīng)與內(nèi)容協(xié)商
4.1響應(yīng)JSON
1.1 jackson.jar+@ResponseBody
web場景自動(dòng)引入了json場景
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<version>2.3.4.RELEASE</version>
<scope>compile</scope>
</dependency>
給前端自動(dòng)返回json數(shù)據(jù);
1、返回值解析器
2、返回值解析器原理
● 1、返回值處理器判斷是否支持這種類型返回值 supportsReturnType
● 2、返回值處理器調(diào)用 handleReturnValue 進(jìn)行處理
● 3、RequestResponseBodyMethodProcessor 可以處理返回值標(biāo)了@ResponseBody 注解的。
○ 1. 利用 MessageConverters 進(jìn)行處理 將數(shù)據(jù)寫為json
■ 1、內(nèi)容協(xié)商(瀏覽器默認(rèn)會(huì)以請求頭的方式告訴服務(wù)器他能接受什么樣的內(nèi)容類型)
■ 2、服務(wù)器最終根據(jù)自己自身的能力,決定服務(wù)器能生產(chǎn)出什么樣內(nèi)容類型的數(shù)據(jù),
■ 3、SpringMVC會(huì)挨個(gè)遍歷所有容器底層的 HttpMessageConverter ,看誰能處理?
● 1、得到MappingJackson2HttpMessageConverter可以將對(duì)象寫為json
● 2、利用MappingJackson2HttpMessageConverter將對(duì)象轉(zhuǎn)為json再寫出去。
1.2、SpringMVC到底支持哪些返回值
ModelAndView
Model
View
ResponseEntity
ResponseBodyEmitter
StreamingResponseBody
HttpEntity
HttpHeaders
Callable
DeferredResult
ListenableFuture
CompletionStage
WebAsyncTask
有 @ModelAttribute 且為對(duì)象類型的
@ResponseBody 注解 ---> RequestResponseBodyMethodProcessor;
1.3、HTTPMessageConverter原理
1、MessageConverter規(guī)范
HttpMessageConverter: 看是否支持將 此 Class類型的對(duì)象,轉(zhuǎn)為MediaType類型的數(shù)據(jù)。
2、默認(rèn)的MessageConverter
最終 MappingJackson2HttpMessageConverter 把對(duì)象轉(zhuǎn)為JSON(利用底層的jackson的objectMapper轉(zhuǎn)換的)
4.2 內(nèi)容協(xié)商
根據(jù)客戶端接收能力不同,返回不同媒體類型的數(shù)據(jù)。
1、引入xml依賴
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
2、postman分別測試返回json和xml
只需要改變請求頭中Accept字段。Http協(xié)議中規(guī)定的,告訴服務(wù)器本客戶端可以接收的數(shù)據(jù)類型。
3、開啟瀏覽器參數(shù)方式內(nèi)容協(xié)商功能
為了方便內(nèi)容協(xié)商,開啟基于請求參數(shù)的內(nèi)容協(xié)商功能。
spring:
contentnegotiation:
favor-parameter: true #開啟請求參數(shù)內(nèi)容協(xié)商模式
eg:
http://localhost:8080/test/person?format=json
4、內(nèi)容協(xié)商原理
● 1、判斷當(dāng)前響應(yīng)頭中是否已經(jīng)有確定的媒體類型。MediaType
● 2、獲取客戶端(PostMan、瀏覽器)支持接收的內(nèi)容類型。(獲取客戶端Accept請求頭字段)【application/xml】
用最佳匹配媒體類型 的converter。調(diào)用它進(jìn)行轉(zhuǎn)化
5.視圖解析與模板引擎
視圖解析:SpringBoot默認(rèn)不支持 JSP,需要引入第三方模板引擎技術(shù)實(shí)現(xiàn)頁面渲染。
5.1 視圖解析
視圖解析原理流程
1、目標(biāo)方法處理的過程中,所有數(shù)據(jù)都會(huì)被放在 ModelAndViewContainer 里面。包括數(shù)據(jù)和視圖地址
2、方法的參數(shù)是一個(gè)自定義類型對(duì)象(從請求參數(shù)中確定的),把他重新放在 ModelAndViewContainer
3、任何目標(biāo)方法執(zhí)行完成以后都會(huì)返回 ModelAndView(數(shù)據(jù)和視圖地址)。
4、processDispatchResult 處理派發(fā)結(jié)果(頁面改如何響應(yīng))
● 1、render(mv, request, response); 進(jìn)行頁面渲染邏輯
○ 1、根據(jù)方法的String返回值得到 View 對(duì)象【定義了頁面的渲染邏輯】
■ 1、所有的視圖解析器嘗試是否能根據(jù)當(dāng)前返回值得到View對(duì)象
■ 2、得到了 redirect:/main.html --> Thymeleaf new RedirectView()
■ 3、ContentNegotiationViewResolver 里面包含了下面所有的視圖解析器,內(nèi)部還是利用下面所有視圖解析器得到視圖對(duì)象。
■ 4、view.render(mv.getModelInternal(), request, response); 視圖對(duì)象調(diào)用自定義的render進(jìn)行頁面渲染工作
● RedirectView 如何渲染【重定向到一個(gè)頁面】
● 1、獲取目標(biāo)url地址
● 2、response.sendRedirect(encodedURL);
5.2、模板引擎-Thymeleaf
6、攔截器
6.1、HandlerInterceptor 接口
6.2、配置攔截器
/**
* 1、編寫一個(gè)攔截器實(shí)現(xiàn)HandlerInterceptor接口
* 2、攔截器注冊到容器中(實(shí)現(xiàn)WebMvcConfigurer的addInterceptors)
* 3、指定攔截規(guī)則【如果是攔截所有,靜態(tài)資源也會(huì)被攔截】
*/
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**") //所有請求都被攔截包括靜態(tài)資源
.excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**"); //放行的請求
}
}
6.3、攔截器原理
1、根據(jù)當(dāng)前請求,找到HandlerExecutionChain【可以處理請求的handler以及handler的所有 攔截器】
2、先來順序執(zhí)行 所有攔截器的 preHandle方法
● 1、如果當(dāng)前攔截器prehandler返回為true。則執(zhí)行下一個(gè)攔截器的preHandle
● 2、如果當(dāng)前攔截器返回為false。直接 倒序執(zhí)行所有已經(jīng)執(zhí)行了的攔截器的 afterCompletion;
3、如果任何一個(gè)攔截器返回false。直接跳出不執(zhí)行目標(biāo)方法
4、所有攔截器都返回True。執(zhí)行目標(biāo)方法
5、倒序執(zhí)行所有攔截器的postHandle方法。
6、前面的步驟有任何異常都會(huì)直接倒序觸發(fā) afterCompletion
7、頁面成功渲染完成以后,也會(huì)倒序觸發(fā) afterCompletion

7、文件上傳
7.1、頁面表單
<form method="post" action="/upload" enctype="multipart/form-data">
<input type="file" name="file"><br>
<input type="submit" value="提交">
</form>
7.2、文件上傳代碼
@PostMapping("/upload")
public String upload(@RequestParam("email") String email,
@RequestParam("username") String username,
@RequestPart("headerImg") MultipartFile headerImg,
@RequestPart("photos") MultipartFile[] photos) throws IOException {
log.info("上傳的信息:email={},username={},headerImg={},photos={}",
email,username,headerImg.getSize(),photos.length);
if(!headerImg.isEmpty()){
//保存到文件服務(wù)器,OSS服務(wù)器
String originalFilename = headerImg.getOriginalFilename();
headerImg.transferTo(new File("H:\\cache\\"+originalFilename));
}
if(photos.length > 0){
for (MultipartFile photo : photos) {
if(!photo.isEmpty()){
String originalFilename = photo.getOriginalFilename();
photo.transferTo(new File("H:\\cache\\"+originalFilename));
}
}
}
return "main";
}
7.3、自動(dòng)配置原理
文件上傳自動(dòng)配置類-MultipartAutoConfiguration-MultipartProperties
● 自動(dòng)配置好了 StandardServletMultipartResolver 【文件上傳解析器】
● 原理步驟
○ 1、請求進(jìn)來使用文件上傳解析器判斷(isMultipart)并封裝(resolveMultipart,返回MultipartHttpServletRequest)文件上傳請求
○ 2、參數(shù)解析器來解析請求中的文件內(nèi)容封裝成MultipartFile
○ 3、將request中文件信息封裝為一個(gè)Map;MultiValueMap<String, MultipartFile>
FileCopyUtils。實(shí)現(xiàn)文件流的拷貝
8、異常處理
錯(cuò)誤處理
1、默認(rèn)規(guī)則
● 默認(rèn)情況下,Spring Boot提供/error處理所有錯(cuò)誤的映射
● 對(duì)于機(jī)器客戶端,它將生成JSON響應(yīng),其中包含錯(cuò)誤,HTTP狀態(tài)和異常消息的詳細(xì)信息。對(duì)于瀏覽器客戶端,響應(yīng)一個(gè)“ whitelabel”錯(cuò)誤視圖,以HTML格式呈現(xiàn)相同的數(shù)據(jù)
error/下的4xx,5xx頁面會(huì)被自動(dòng)解析
2、定制錯(cuò)誤處理邏輯
● 自定義錯(cuò)誤頁
○ error/404.html error/5xx.html;有精確的錯(cuò)誤狀態(tài)碼頁面就匹配精確,沒有就找 4xx.html;如果都沒有就觸發(fā)白頁
● @ControllerAdvice+@ExceptionHandler處理全局異常;底層是 ExceptionHandlerExceptionResolver 支持的
● @ResponseStatus+自定義異常 ;底層是 ResponseStatusExceptionResolver ,把responsestatus注解的信息底層調(diào)用 response.sendError(statusCode, resolvedReason);tomcat發(fā)送的/error
● Spring底層的異常,如 參數(shù)類型轉(zhuǎn)換異常;
自定義實(shí)現(xiàn) HandlerExceptionResolver 處理異常;可以作為默認(rèn)的全局異常處理規(guī)則.
● ErrorViewResolver 實(shí)現(xiàn)自定義處理異常;
○ response.sendError 。error請求就會(huì)轉(zhuǎn)給controller
○ 你的異常沒有任何人能處理。tomcat底層 response.sendError。error請求就會(huì)轉(zhuǎn)給controller
○ basicErrorController 要去的頁面地址是 ErrorViewResolver
3、異常處理自動(dòng)配置原理
● ErrorMvcAutoConfiguration 自動(dòng)配置異常處理規(guī)則
○ 容器中的組件:類型:DefaultErrorAttributes -> id:errorAttributes
■ public class DefaultErrorAttributes implements ErrorAttributes, HandlerExceptionResolver
■ DefaultErrorAttributes:定義錯(cuò)誤頁面中可以包含哪些數(shù)據(jù)。
○ 容器中的組件:類型:BasicErrorController --> id:basicErrorController(json+白頁 適配響應(yīng))
■ 處理默認(rèn) /error 路徑的請求;頁面響應(yīng) new ModelAndView("error", model);
■ 容器中有組件 View->id是error;(響應(yīng)默認(rèn)錯(cuò)誤頁)
■ 容器中放組件 BeanNameViewResolver(視圖解析器);按照返回的視圖名作為組件的id去容器中找View對(duì)象。
○ 容器中的組件:類型:DefaultErrorViewResolver -> id:conventionErrorViewResolver
■ 如果發(fā)生錯(cuò)誤,會(huì)以HTTP的狀態(tài)碼 作為視圖頁地址(viewName),找到真正的頁面
■ error/404、5xx.html
如果想要返回頁面;就會(huì)找error視圖【StaticView】。(默認(rèn)是一個(gè)白頁)
4、異常處理步驟流程
1、執(zhí)行目標(biāo)方法,目標(biāo)方法運(yùn)行期間有任何異常都會(huì)被catch、而且標(biāo)志當(dāng)前請求結(jié)束;并且用 dispatchException
2、進(jìn)入視圖解析流程(頁面渲染?)
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
3、mv = processHandlerException;處理handler發(fā)生的異常,處理完成返回ModelAndView;
● 1、遍歷所有的 handlerExceptionResolvers,看誰能處理當(dāng)前異常
2、系統(tǒng)默認(rèn)的 異常解析器
○ 1、DefaultErrorAttributes先來處理異常。把異常信息保存到rrequest域,并且返回null;
○ 2、默認(rèn)沒有任何人能處理異常,所以異常會(huì)被拋出
■ 1、如果沒有任何人能處理最終底層就會(huì)發(fā)送 /error 請求。會(huì)被底層的BasicErrorController處理
■ 2、解析錯(cuò)誤視圖;遍歷所有的 ErrorViewResolver 看誰能解析。
■ 3、默認(rèn)的 DefaultErrorViewResolver ,作用是把響應(yīng)狀態(tài)碼作為錯(cuò)誤頁的地址,error/500.html
■ 4、模板引擎最終響應(yīng)這個(gè)頁面 error/500.html
9、Web原生組件注入(Servlet、Filter、Listener)
1、使用Servlet API
@ServletComponentScan(basePackages = "com.atguigu.admin") :指定原生Servlet組件都放在那里
@WebServlet(urlPatterns = "/my"):效果:直接響應(yīng),不用經(jīng)過Spring的攔截器
@WebFilter(urlPatterns={"/css/","/images/"})
@WebListener
2、使用RegistrationBean
ServletRegistrationBean, FilterRegistrationBean, and ServletListenerRegistrationBean
10、嵌入式Servlet容器
1、切換嵌入式Servlet容器
默認(rèn)支持的webServer
Tomcat,Jetty, orUndertowServletWebServerApplicationContext 容器啟動(dòng)尋找ServletWebServerFactory 并引導(dǎo)創(chuàng)建服務(wù)器切換服務(wù)器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
● 原理
○ SpringBoot應(yīng)用啟動(dòng)發(fā)現(xiàn)當(dāng)前是Web應(yīng)用。web場景包-導(dǎo)入tomcat
○ web應(yīng)用會(huì)創(chuàng)建一個(gè)web版的ioc容器 ServletWebServerApplicationContext
○ ServletWebServerApplicationContext 啟動(dòng)的時(shí)候?qū)ふ?ServletWebServerFactory(Servlet 的web服務(wù)器工廠---> Servlet 的web服務(wù)器)
○ SpringBoot底層默認(rèn)有很多的WebServer工廠;TomcatServletWebServerFactory, JettyServletWebServerFactory, or UndertowServletWebServerFactory
○ 底層直接會(huì)有一個(gè)自動(dòng)配置類。ServletWebServerFactoryAutoConfiguration
○ ServletWebServerFactoryAutoConfiguration導(dǎo)入了ServletWebServerFactoryConfiguration(配置類)
○ ServletWebServerFactoryConfiguration 配置類 根據(jù)動(dòng)態(tài)判斷系統(tǒng)中到底導(dǎo)入了那個(gè)Web服務(wù)器的包。(默認(rèn)是web-starter導(dǎo)入tomcat包),容器中就有 TomcatServletWebServerFactory
○ TomcatServletWebServerFactory 創(chuàng)建出Tomcat服務(wù)器并啟動(dòng);TomcatWebServer 的構(gòu)造器擁有初始化方法initialize---this.tomcat.start();
○ 內(nèi)嵌服務(wù)器,就是手動(dòng)把啟動(dòng)服務(wù)器的代碼調(diào)用(tomcat核心jar包存在)
2、定制Servlet容器
● 實(shí)現(xiàn) WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>
○ 把配置文件的值和ServletWebServerFactory 進(jìn)行綁定
● 修改配置文件 server.xxx
● 直接自定義 ConfigurableServletWebServerFactory
xxxxxCustomizer:定制化器,可以改變xxxx的默認(rèn)規(guī)則
@Component
public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
@Override
public void customize(ConfigurableServletWebServerFactory server) {
server.setPort(9000);
}
}
11、定制化原理
1、定制化的常見方式
● 修改配置文件;
● xxxxxCustomizer;
● 編寫自定義的配置類 xxxConfiguration;+ @Bean替換、增加容器中默認(rèn)組件;視圖解析器
● Web應(yīng)用 編寫一個(gè)配置類實(shí)現(xiàn) WebMvcConfigurer 即可定制化web功能;+ @Bean給容器中再擴(kuò)展一些組件
@EnableWebMvc + WebMvcConfigurer —— @Bean 可以全面接管SpringMVC,所有規(guī)則全部自己重新配置; 實(shí)現(xiàn)定制和擴(kuò)展功能
2、原理分析套路
場景starter - xxxxAutoConfiguration - 導(dǎo)入xxx組件 - 綁定xxxProperties -- 綁定配置文件項(xiàng)
六、數(shù)據(jù)訪問
1、SQL
1.1數(shù)據(jù)源的自動(dòng)配置-HikariDataSource
1、導(dǎo)入JDBC場景
2、分析自動(dòng)配置
1、自動(dòng)配置的類
● DataSourceAutoConfiguration : 數(shù)據(jù)源的自動(dòng)配置
○ 修改數(shù)據(jù)源相關(guān)的配置:spring.datasource
○ 數(shù)據(jù)庫連接池的配置,是自己容器中沒有DataSource才自動(dòng)配置的
○ 底層配置好的連接池是:HikariDataSource
● DataSourceTransactionManagerAutoConfiguration: 事務(wù)管理器的自動(dòng)配置
● JdbcTemplateAutoConfiguration: JdbcTemplate的自動(dòng)配置,可以來對(duì)數(shù)據(jù)庫進(jìn)行crud
○ 可以修改這個(gè)配置項(xiàng)@ConfigurationProperties(prefix = "spring.jdbc") 來修改JdbcTemplate
○ @Bean@Primary JdbcTemplate;容器中有這個(gè)組件
● JndiDataSourceAutoConfiguration: jndi的自動(dòng)配置
● XADataSourceAutoConfiguration: 分布式事務(wù)相關(guān)的
3、修改配置項(xiàng)
spring:
datasource:
url: jdbc:mysql://localhost:3306/db_account
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
4、測試
1.2使用Druid數(shù)據(jù)源
自定義方式
1、創(chuàng)建數(shù)據(jù)源
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.17</version>
</dependency>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close">
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="maxActive" value="20" />
<property name="initialSize" value="1" />
<property name="maxWait" value="60000" />
<property name="minIdle" value="1" />
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<property name="minEvictableIdleTimeMillis" value="300000" />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<property name="poolPreparedStatements" value="true" />
<property name="maxOpenPreparedStatements" value="20" />
2、StatViewServlet
StatViewServlet的用途包括:
提供監(jiān)控信息展示的html頁面
提供監(jiān)控信息的JSON API
<servlet>
<servlet-name>DruidStatView</servlet-name>
<servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DruidStatView</servlet-name>
<url-pattern>/druid/*</url-pattern>
</servlet-mapping>
3、StatFilter
用于統(tǒng)計(jì)監(jiān)控信息;如SQL監(jiān)控、URI監(jiān)控
使用官方starter方式
1、引入druid-starter
、分析自動(dòng)配置
● 擴(kuò)展配置項(xiàng) spring.datasource.druid
● DruidSpringAopConfiguration.class, 監(jiān)控SpringBean的;配置項(xiàng):spring.datasource.druid.aop-patterns
● DruidStatViewServletConfiguration.class, 監(jiān)控頁的配置:spring.datasource.druid.stat-view-servlet;默認(rèn)開啟
● DruidWebStatFilterConfiguration.class, web監(jiān)控配置;spring.datasource.druid.web-stat-filter;默認(rèn)開啟
● DruidFilterConfiguration.class}) 所有Druid自己filter的配置
3、配置示例
spring:
datasource:
url: jdbc:mysql://localhost:3306/db_account
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
druid:
aop-patterns: com.atguigu.admin.* #監(jiān)控SpringBean
filters: stat,wall # 底層開啟功能,stat(sql監(jiān)控),wall(防火墻)
stat-view-servlet: # 配置監(jiān)控頁功能
enabled: true
login-username: admin
login-password: admin
resetEnable: false
web-stat-filter: # 監(jiān)控web
enabled: true
urlPattern: /*
exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'
filter:
stat: # 對(duì)上面filters里面的stat的詳細(xì)配置
slow-sql-millis: 1000
logSlowSql: true
enabled: true
wall:
enabled: true
config:
drop-table-allow: false
1.3整合MyBatis操作
1、配置模式
● 全局配置文件
● SqlSessionFactory: 自動(dòng)配置好了
● SqlSession:自動(dòng)配置了 SqlSessionTemplate 組合了SqlSession
● @Import(AutoConfiguredMapperScannerRegistrar.class);
● Mapper: 只要我們寫的操作MyBatis的接口標(biāo)準(zhǔn)了 @Mapper 就會(huì)被自動(dòng)掃描進(jìn)來
配置 private Configuration configuration; mybatis.configuration下面的所有,就是相當(dāng)于改mybatis全局配置文件中的值
# 配置mybatis規(guī)則
mybatis:
# config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
configuration:
map-underscore-to-camel-case: true
可以不寫全局;配置文件,所有全局配置文件的配置都放在configuration配置項(xiàng)中即可
● 導(dǎo)入mybatis官方starter
● 編寫mapper接口。標(biāo)準(zhǔn)@Mapper注解
● 編寫sql映射文件并綁定mapper接口
● 在application.yaml中指定Mapper配置文件的位置,以及指定全局配置文件的信息 (建議;配置在mybatis.configuration)
2、注解模式
@Mapper
public interface CityMapper {
@Select("select * from city where id=#{id}")
public City getById(Long id);
public void insert(City city);
}
1.4整合 MyBatis-Plus 完成CRUD
優(yōu)點(diǎn):
● 只需要我們的Mapper繼承 BaseMapper 就可以擁有crud能力
2、NOSQL
Redis 是一個(gè)開源(BSD許可)的,內(nèi)存中的數(shù)據(jù)結(jié)構(gòu)存儲(chǔ)系統(tǒng),它可以用作數(shù)據(jù)庫、緩存和消息中間件
1、Redis自動(dòng)配置
自動(dòng)配置:
● RedisAutoConfiguration 自動(dòng)配置類。RedisProperties 屬性類 --> spring.redis.xxx是對(duì)redis的配置
● 連接工廠是準(zhǔn)備好的。LettuceConnectionConfiguration、JedisConnectionConfiguration
● 自動(dòng)注入了RedisTemplate<Object, Object> : xxxTemplate;
● 自動(dòng)注入了StringRedisTemplate;k:v都是String
● key:value
● 底層只要我們使用 StringRedisTemplate、RedisTemplate就可以操作redis
redis環(huán)境搭建
1、阿里云按量付費(fèi)redis。經(jīng)典網(wǎng)絡(luò)
2、申請redis的公網(wǎng)連接地址
3、修改白名單 允許0.0.0.0/0 訪問
2、RedisTemplate與Lettuce
3、切換至jedis
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 導(dǎo)入jedis-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
spring:
redis:
host: r-bp1nc7reqesxisgxpipd.redis.rds.aliyuncs.com
port: 6379
password: lfy:Lfy123456
client-type: jedis
jedis:
pool:
max-active: 10
七、單元測試
1、JUNIT5的變化
以前:
@SpringBootTest + @RunWith(SpringTest.class)
現(xiàn)在:
SpringBoot整合Junit以后。
● 編寫測試方法:@Test標(biāo)注(注意需要使用junit5版本的注解)
● Junit類具有Spring的功能,@Autowired、比如 @Transactional 標(biāo)注測試方法,測試完成后自動(dòng)回滾
2、JUnit5常用注解
● @Test :表示方法是測試方法。但是與JUnit4的@Test不同,他的職責(zé)非常單一不能聲明任何屬性,拓展的測試將會(huì)由Jupiter提供額外測試
● @ParameterizedTest :表示方法是參數(shù)化測試
● @RepeatedTest :表示方法可重復(fù)執(zhí)行
● @DisplayName :為測試類或者測試方法設(shè)置展示名稱
● @BeforeEach :表示在每個(gè)單元測試之前執(zhí)行
● @AfterEach :表示在每個(gè)單元測試之后執(zhí)行
● @BeforeAll :表示在所有單元測試之前執(zhí)行
● @AfterAll :表示在所有單元測試之后執(zhí)行
● @Tag :表示單元測試類別,類似于JUnit4中的@Categories
● @Disabled :表示測試類或測試方法不執(zhí)行,類似于JUnit4中的@Ignore
● @Timeout :表示測試方法運(yùn)行如果超過了指定時(shí)間將會(huì)返回錯(cuò)誤
● @ExtendWith :為測試類或測試方法提供擴(kuò)展類引用
3、斷言(assertions)
斷言(assertions)是測試方法中的核心部分,用來對(duì)測試需要滿足的條件進(jìn)行驗(yàn)證。這些斷言方法都是 org.junit.jupiter.api.Assertions 的靜態(tài)方法
1、簡單斷言
assertEquals 判斷兩個(gè)對(duì)象或兩個(gè)原始類型是否相等
assertNotEquals 判斷兩個(gè)對(duì)象或兩個(gè)原始類型是否不相等
assertSame 判斷兩個(gè)對(duì)象引用是否指向同一個(gè)對(duì)象
assertNotSame 判斷兩個(gè)對(duì)象引用是否指向不同的對(duì)象
assertTrue 判斷給定的布爾值是否為 true
assertFalse 判斷給定的布爾值是否為 false
assertNull 判斷給定的對(duì)象引用是否為 null
assertNotNull 判斷給定的對(duì)象引用是否不為 null
@Test
@DisplayName("simple assertion")
public void simple() {
assertEquals(3, 1 + 2, "simple math");
assertNotEquals(3, 1 + 1);
assertNotSame(new Object(), new Object());
Object obj = new Object();
assertSame(obj, obj);
assertFalse(1 > 2);
assertTrue(1 < 2);
assertNull(null);
assertNotNull(new Object());
}
2、數(shù)組斷言
通過 assertArrayEquals 方法來判斷兩個(gè)對(duì)象或原始類型的數(shù)組是否相等
@Test
@DisplayName("array assertion")
public void array() {
assertArrayEquals(new int[]{1, 2}, new int[] {1, 2});
}
3、組合斷言
assertAll 方法接受多個(gè) org.junit.jupiter.api.Executable 函數(shù)式接口的實(shí)例作為要驗(yàn)證的斷言,可以通過 lambda 表達(dá)式很容易的提供這些斷言
@Test
@DisplayName("assert all")
public void all() {
assertAll("Math",
() -> assertEquals(2, 1 + 1),
() -> assertTrue(1 > 0)
);
}
4、異常斷言
在JUnit4時(shí)期,想要測試方法的異常情況時(shí),需要用@Rule注解的ExpectedException變量還是比較麻煩的。而JUnit5提供了一種新的斷言方式Assertions.assertThrows() ,配合函數(shù)式編程就可以進(jìn)行使用。
@Test
@DisplayName("異常測試")
public void exceptionTest() {
ArithmeticException exception = Assertions.assertThrows(
//扔出斷言異常
ArithmeticException.class, () -> System.out.println(1 % 0));
}
5、超時(shí)斷言
Junit5還提供了Assertions.assertTimeout() 為測試方法設(shè)置了超時(shí)時(shí)間
@Test
@DisplayName("超時(shí)測試")
public void timeoutTest() {
//如果測試方法時(shí)間超過1s將會(huì)異常
Assertions.assertTimeout(Duration.ofMillis(1000), () -> Thread.sleep(500));
}
6、快速失敗
通過 fail 方法直接使得測試失敗
@Test
@DisplayName("fail")
public void shouldFail() {
fail("This should fail");
}
4、前置條件(assumptions)
JUnit 5 中的前置條件(assumptions【假設(shè)】)類似于斷言,不同之處在于不滿足的斷言會(huì)使得測試方法失敗,而不滿足的前置條件只會(huì)使得測試方法的執(zhí)行終止。前置條件可以看成是測試方法執(zhí)行的前提,當(dāng)該前提不滿足時(shí),就沒有繼續(xù)執(zhí)行的必要。
@DisplayName("前置條件")
public class AssumptionsTest {
private final String environment = "DEV";
@Test
@DisplayName("simple")
public void simpleAssume() {
assumeTrue(Objects.equals(this.environment, "DEV"));
assumeFalse(() -> Objects.equals(this.environment, "PROD"));
}
@Test
@DisplayName("assume then do")
public void assumeThenDo() {
assumingThat(
Objects.equals(this.environment, "DEV"),
() -> System.out.println("In DEV")
);
}
}
assumeTrue 和 assumFalse 確保給定的條件為 true 或 false,不滿足條件會(huì)使得測試執(zhí)行終止。assumingThat 的參數(shù)是表示條件的布爾值和對(duì)應(yīng)的 Executable 接口的實(shí)現(xiàn)對(duì)象。只有條件滿足時(shí),Executable 對(duì)象才會(huì)被執(zhí)行;當(dāng)條件不滿足時(shí),測試執(zhí)行并不會(huì)終止
5、嵌套測試
Unit 5 可以通過 Java 中的內(nèi)部類和@Nested 注解實(shí)現(xiàn)嵌套測試,從而可以更好的把相關(guān)的測試方法組織在一起。在內(nèi)部類中可以使用@BeforeEach 和@AfterEach 注解,而且嵌套的層次沒有限制。
@DisplayName("A stack")
class TestingAStackDemo {
Stack<Object> stack;
@Test
@DisplayName("is instantiated with new Stack()")
void isInstantiatedWithNew() {
new Stack<>();
}
@Nested
@DisplayName("when new")
class WhenNew {
@BeforeEach
void createNewStack() {
stack = new Stack<>();
}
@Test
@DisplayName("is empty")
void isEmpty() {
assertTrue(stack.isEmpty());
}
@Test
@DisplayName("throws EmptyStackException when popped")
void throwsExceptionWhenPopped() {
assertThrows(EmptyStackException.class, stack::pop);
}
@Test
@DisplayName("throws EmptyStackException when peeked")
void throwsExceptionWhenPeeked() {
assertThrows(EmptyStackException.class, stack::peek);
}
@Nested
@DisplayName("after pushing an element")
class AfterPushing {
String anElement = "an element";
@BeforeEach
void pushAnElement() {
stack.push(anElement);
}
@Test
@DisplayName("it is no longer empty")
void isNotEmpty() {
assertFalse(stack.isEmpty());
}
@Test
@DisplayName("returns the element when popped and is empty")
void returnElementWhenPopped() {
assertEquals(anElement, stack.pop());
assertTrue(stack.isEmpty());
}
@Test
@DisplayName("returns the element when peeked but remains not empty")
void returnElementWhenPeeked() {
assertEquals(anElement, stack.peek());
assertFalse(stack.isEmpty());
}
}
}
}
6、參數(shù)化測試
參數(shù)化測試是JUnit5很重要的一個(gè)新特性,它使得用不同的參數(shù)多次運(yùn)行測試成為了可能,也為我們的單元測試帶來許多便利。
@ValueSource: 為參數(shù)化測試指定入?yún)碓?,支持八大基礎(chǔ)類以及String類型,Class類型
@NullSource: 表示為參數(shù)化測試提供一個(gè)null的入?yún)?br>
@EnumSource: 表示為參數(shù)化測試提供一個(gè)枚舉入?yún)?br>
@CsvFileSource:表示讀取指定CSV文件內(nèi)容作為參數(shù)化測試入?yún)?br>
@MethodSource:表示讀取指定方法的返回值作為參數(shù)化測試入?yún)?注意方法返回需要是一個(gè)流)
@ParameterizedTest
@ValueSource(strings = {"one", "two", "three"})
@DisplayName("參數(shù)化測試1")
public void parameterizedTest1(String string) {
System.out.println(string);
Assertions.assertTrue(StringUtils.isNotBlank(string));
}
@ParameterizedTest
@MethodSource("method") //指定方法名
@DisplayName("方法來源參數(shù)")
public void testWithExplicitLocalMethodSource(String name) {
System.out.println(name);
Assertions.assertNotNull(name);
}
static Stream<String> method() {
return Stream.of("apple", "banana");
}
7、遷移指南
<article id="content" class="article-content" tabindex="0" style="outline-style: none;">
在進(jìn)行遷移的時(shí)候需要注意如下的變化:
- 注解在 org.junit.jupiter.api 包中,斷言在 org.junit.jupiter.api.Assertions 類中,前置條件在 org.junit.jupiter.api.Assumptions 類中。
- 把@Before 和@After 替換成@BeforeEach 和@AfterEach。
- 把@BeforeClass 和@AfterClass 替換成@BeforeAll 和@AfterAll。
- 把@Ignore 替換成@Disabled。
- 把@Category 替換成@Tag。
- 把@RunWith、@Rule 和@ClassRule 替換成@ExtendWith。
八、指標(biāo)監(jiān)控
8.1、SpringBoot Actuator
1、簡介
未來每一個(gè)微服務(wù)在云上部署以后,我們都需要對(duì)其進(jìn)行監(jiān)控、追蹤、審計(jì)、控制等。SpringBoot就抽取了Actuator場景,使得我們每個(gè)微服務(wù)快速引用即可獲得生產(chǎn)級(jí)別的應(yīng)用監(jiān)控、審計(jì)等功能。
2、如何使用
- 引入場景
- 訪問 http://localhost:8080/actuator/
- 暴露所有監(jiān)控信息為HTTP
management:
endpoints:
enabled-by-default: true #暴露所有端點(diǎn)信息
web:
exposure:
include: '*' #以web方式暴露
3、可視化
https://github.com/codecentric/spring-boot-admin
8.2、Actuator Endpoint
1、最常使用的端點(diǎn)
最常用的Endpoint
● Health:監(jiān)控狀況
● Metrics:運(yùn)行時(shí)指標(biāo)
● Loggers:日志記錄
2、Health Endpoint
健康檢查端點(diǎn),我們一般用于在云平臺(tái),平臺(tái)會(huì)定時(shí)的檢查應(yīng)用的健康狀況,我們就需要Health Endpoint可以為平臺(tái)返回當(dāng)前應(yīng)用的一系列組件健康狀況的集合。
重要的幾點(diǎn):
● health endpoint返回的結(jié)果,應(yīng)該是一系列健康檢查后的一個(gè)匯總報(bào)告
● 很多的健康檢查默認(rèn)已經(jīng)自動(dòng)配置好了,比如:數(shù)據(jù)庫、redis等
● 可以很容易的添加自定義的健康檢查機(jī)制
3、Metrics Endpoint
供詳細(xì)的、層級(jí)的、空間指標(biāo)信息,這些信息可以被pull(主動(dòng)推送)或者push(被動(dòng)獲取)方式得到;
● 通過Metrics對(duì)接多種監(jiān)控系統(tǒng)
● 簡化核心Metrics開發(fā)
● 添加自定義Metrics或者擴(kuò)展已有Metrics
4、管理Endpoints
1、開啟與禁用Endpoints
● 默認(rèn)所有的Endpoint除過shutdown都是開啟的。
● 需要開啟或者禁用某個(gè)Endpoint。配置模式為 management.endpoint.<endpointName>.enabled = true
或者禁用所有的Endpoint然后手動(dòng)開啟指定的Endpoint
2、暴露Endpoints
支持的暴露方式
● HTTP:默認(rèn)只暴露health和info Endpoint
● JMX(例如Jconsole):默認(rèn)暴露所有Endpoint
● 除過health和info,剩下的Endpoint都應(yīng)該進(jìn)行保護(hù)訪問。如果引入SpringSecurity,則會(huì)默認(rèn)配置安全訪問規(guī)則
8.3、定制 Endpoint
1、定制 Health 信息
2、定制info信息
3、定制Metrics信息
九、原理解析
9.1、Profile功能
1、application-profile功能
● 默認(rèn)配置文件 application.yaml;任何時(shí)候都會(huì)加載
● 指定環(huán)境配置文件 application-{env}.yaml
● 激活指定環(huán)境
○ 配置文件激活
○ 命令行激活:java -jar xxx.jar --spring.profiles.active=prod --person.name=haha
■ 修改配置文件的任意值,命令行優(yōu)先
● 默認(rèn)配置與環(huán)境配置同時(shí)生效
● 同名配置項(xiàng),profile配置優(yōu)先
2、@Profile條件裝配功能
@Configuration(proxyBeanMethods = false)
@Profile("production")
public class ProductionConfiguration {
// ...
}
3、profile分組
spring.profiles.group.production[0]=proddb
spring.profiles.group.production[1]=prodmq
使用:--spring.profiles.active=production 激活
9.2、外部化配置
1、外部配置源
常用:Java屬性文件、YAML文件、環(huán)境變量、命令行參數(shù);
2、配置文件查找位置
(1) classpath 根路徑
(2) classpath 根路徑下config目錄
(3) jar包當(dāng)前目錄
(4) jar包當(dāng)前目錄的config目錄
(5) /config子目錄的直接子目錄
3、配置文件加載順序:
- 當(dāng)前jar包內(nèi)部的application.properties和application.yml
- 當(dāng)前jar包內(nèi)部的application-{profile}.properties 和 application-{profile}.yml
- 引用的外部jar包的application.properties和application.yml
- 引用的外部jar包的application-{profile}.properties 和 application-{profile}.yml
4、指定環(huán)境優(yōu)先,外部優(yōu)先,后面的可以覆蓋前面的同名配置項(xiàng)
9.3、自定義starter
1、starter啟動(dòng)原理
starter-pom引入 autoconfigurer 包
● autoconfigure包中配置使用 META-INF/spring.factories 中 EnableAutoConfiguration 的值,使得項(xiàng)目啟動(dòng)加載指定的自動(dòng)配置類
● 編寫自動(dòng)配置類 xxxAutoConfiguration -> xxxxProperties
○ @Configuration
○ @Conditional
○ @EnableConfigurationProperties
○ @Bean
○ ......
引入starter --- xxxAutoConfiguration --- 容器中放入組件 ---- 綁定xxxProperties ---- 配置項(xiàng)
2、自定義starter
atguigu-hello-spring-boot-starter(啟動(dòng)器)
atguigu-hello-spring-boot-starter-autoconfigure(自動(dòng)配置包)
9.4、SpringBoot原理
SpringBoot啟動(dòng)過程:
● 創(chuàng)建 SpringApplication
○ 保存一些信息。
○ 判定當(dāng)前應(yīng)用的類型。ClassUtils。Servlet
○ bootstrappers:初始啟動(dòng)引導(dǎo)器(List<Bootstrapper>):去spring.factories文件中找 org.springframework.boot.Bootstrapper
○ 找 ApplicationContextInitializer;去spring.factories找 ApplicationContextInitializer
■ List<ApplicationContextInitializer<?>> initializers
○ 找 ApplicationListener ;應(yīng)用監(jiān)聽器。去spring.factories找 ApplicationListener
■ List<ApplicationListener<?>> listeners
● 運(yùn)行 SpringApplication
○ StopWatch
○ 記錄應(yīng)用的啟動(dòng)時(shí)間
○ 創(chuàng)建引導(dǎo)上下文(Context環(huán)境)createBootstrapContext()
■ 獲取到所有之前的 bootstrappers 挨個(gè)執(zhí)行 intitialize() 來完成對(duì)引導(dǎo)啟動(dòng)器上下文環(huán)境設(shè)置
○ 讓當(dāng)前應(yīng)用進(jìn)入headless模式。java.awt.headless
○ 獲取所有 RunListener(運(yùn)行監(jiān)聽器)【為了方便所有Listener進(jìn)行事件感知】
■ getSpringFactoriesInstances 去spring.factories找 SpringApplicationRunListener.
○ 遍歷 SpringApplicationRunListener 調(diào)用 starting 方法;
■ 相當(dāng)于通知所有感興趣系統(tǒng)正在啟動(dòng)過程的人,項(xiàng)目正在 starting。
○ 保存命令行參數(shù);ApplicationArguments
○ 準(zhǔn)備環(huán)境 prepareEnvironment();
■ 返回或者創(chuàng)建基礎(chǔ)環(huán)境信息對(duì)象。StandardServletEnvironment
■ 配置環(huán)境信息對(duì)象。
● 讀取所有的配置源的配置屬性值。
■ 綁定環(huán)境信息
■ 監(jiān)聽器調(diào)用 listener.environmentPrepared();通知所有的監(jiān)聽器當(dāng)前環(huán)境準(zhǔn)備完成
○ 創(chuàng)建IOC容器(createApplicationContext())
■ 根據(jù)項(xiàng)目類型(Servlet)創(chuàng)建容器,
■ 當(dāng)前會(huì)創(chuàng)建 AnnotationConfigServletWebServerApplicationContext
○ 準(zhǔn)備ApplicationContext IOC容器的基本信息 prepareContext()
■ 保存環(huán)境信息
■ IOC容器的后置處理流程。
■ 應(yīng)用初始化器;applyInitializers;
● 遍歷所有的 ApplicationContextInitializer 。調(diào)用 initialize.。來對(duì)ioc容器進(jìn)行初始化擴(kuò)展功能
● 遍歷所有的 listener 調(diào)用 contextPrepared。EventPublishRunListenr;通知所有的監(jiān)聽器contextPrepared
■ 所有的監(jiān)聽器 調(diào)用 contextLoaded。通知所有的監(jiān)聽器 contextLoaded;
○ 刷新IOC容器。refreshContext
■ 創(chuàng)建容器中的所有組件(Spring注解)
○ 容器刷新完成后工作?afterRefresh
○ 所有監(jiān)聽 器 調(diào)用 listeners.started(context); 通知所有的監(jiān)聽器 started
○ 調(diào)用所有runners;callRunners()
■ 獲取容器中的 ApplicationRunner
■ 獲取容器中的 CommandLineRunner
■ 合并所有runner并且按照@Order進(jìn)行排序
■ 遍歷所有的runner。調(diào)用 run 方法
○ 如果以上有異常,
■ 調(diào)用Listener 的 failed
○ 調(diào)用所有監(jiān)聽器的 running 方法 listeners.running(context); 通知所有的監(jiān)聽器 running
○ running如果有問題。繼續(xù)通知 failed 。調(diào)用所有 Listener 的 failed;通知所有的監(jiān)聽器 failed