一、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)級別的監(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ā)中常用的依賴的版本號,自動(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)注版本號右莱,自動(dòng)版本仲裁
1、引入依賴默認(rèn)都可以不寫版本
2档插、引入非版本仲裁的jar慢蜓,要寫版本號。
- 可以修改默認(rèn)版本號
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)
○ 主程序所在包及其下面的所有子包里面的組件都會被默認(rèn)掃描進(jìn)來
○ 無需以前的包掃描配置
○ 想要改變掃描路徑,
@SpringBootApplication(scanBasePackages="com.atguigu")
或者@ComponentScan 指定掃描路徑
@SpringBootApplication
等同于
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.atguigu.boot")
- 各種配置擁有默認(rèn)值
○ 默認(rèn)配置最終都是映射到某個(gè)類上帆谍,如:MultipartProperties
○ 配置文件的值最終會綁定每個(gè)類上,這個(gè)類會在容器中創(chuàng)建對象 - 按需加載所有自動(dòng)配置項(xiàng)
○ 非常多的starter
○ 引入了哪些場景這個(gè)場景的自動(dòng)配置才會開啟
○ SpringBoot所有的自動(dòng)配置功能都在 spring-boot-autoconfigure 包里面
2轴咱、容器功能
2.1汛蝙、組件添加
1、@Configuration
■ 配置類組件之間無依賴關(guān)系用Lite模式加速容器啟動(dòng)過程朴肺,減少判斷
■ 配置類組件之間有依賴關(guān)系窖剑,方法會被調(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:外部無論對配置類中的這個(gè)組件注冊方法調(diào)用多少次獲取的都是之前注冊容器中的單實(shí)例對象
* @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)代理對象調(diào)用方法。SpringBoot總會檢查這個(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:外部無論對配置類中的這個(gè)組件注冊方法調(diào)用多少次獲取的都是之前注冊容器中的單實(shí)例對象
* @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
/**
* 只有在容器中的組件,才會擁有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),最終會按需配置铛只。
3.3埠胖、修改默認(rèn)配置
SpringBoot默認(rèn)會在底層配好所有的組件。但是如果用戶自己配置了以用戶的優(yōu)先
用戶可以使用@Bean注解來或者修改配置文件來自己配置組件
@Bean
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
}
總結(jié):
● SpringBoot先加載所有的自動(dòng)配置類 xxxxxAutoConfiguration
● 每個(gè)自動(dòng)配置類按照條件進(jìn)行生效淳玩,默認(rèn)都會綁定配置文件指定的值直撤。xxxxProperties里面拿。xxxProperties和配置文件進(jìn)行了綁定
● 生效的配置類就會給容器中裝配很多組件
● 只要容器中有這些組件蜕着,相當(dāng)于這些功能就有了
● 定制化配置
○ 用戶直接自己@Bean替換底層的組件
○ 用戶去看這個(gè)組件是獲取的配置文件什么值就去修改谋竖。
xxxxxAutoConfiguration ---> 組件 ---> xxxxProperties里面拿值 ----> application.properties
3.4 最佳實(shí)踐
1红柱、查看自動(dòng)配置了哪些
- 自己分析,引入場景對應(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í),自定義的類也會有提示
<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)詞來表示對資源的操作)
○ 以前:/getUser 獲取用戶 /deleteUser 刪除用戶 /editUser 修改用戶 /saveUser 保存用戶
○ 現(xiàn)在: /user GET-獲取用戶 DELETE-刪除用戶 PUT-修改用戶 POST-保存用戶
○ 核心Filter萝快;HiddenHttpMethodFilter
■ 用法: 表單method=post锻霎,隱藏域 _method=put
■ SpringBoot中手動(dòng)開啟
Rest原理(表單提交要使用REST的時(shí)候)
● 表單提交會帶上_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è)請求對應(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)開啟:原理。對于路徑的處理击敌。UrlPathHelper進(jìn)行解析介返。
// removeSemicolonContent(移除分號內(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ù)會被放在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è)對象
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)會以請求頭的方式告訴服務(wù)器他能接受什么樣的內(nèi)容類型)
■ 2姆怪、服務(wù)器最終根據(jù)自己自身的能力叛赚,決定服務(wù)器能生產(chǎn)出什么樣內(nèi)容類型的數(shù)據(jù)澡绩,
■ 3、SpringMVC會挨個(gè)遍歷所有容器底層的 HttpMessageConverter 俺附,看誰能處理肥卡?
● 1、得到MappingJackson2HttpMessageConverter可以將對象寫為json
● 2昙读、利用MappingJackson2HttpMessageConverter將對象轉(zhuǎn)為json再寫出去召调。
1.2、SpringMVC到底支持哪些返回值
ModelAndView
Model
View
ResponseEntity
ResponseBodyEmitter
StreamingResponseBody
HttpEntity
HttpHeaders
Callable
DeferredResult
ListenableFuture
CompletionStage
WebAsyncTask
有 @ModelAttribute 且為對象類型的
@ResponseBody 注解 ---> RequestResponseBodyMethodProcessor蛮浑;
1.3唠叛、HTTPMessageConverter原理
1、MessageConverter規(guī)范
HttpMessageConverter: 看是否支持將 此 Class類型的對象沮稚,轉(zhuǎn)為MediaType類型的數(shù)據(jù)艺沼。
2、默認(rèn)的MessageConverter
最終 MappingJackson2HttpMessageConverter 把對象轉(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ù)都會被放在 ModelAndViewContainer 里面车份。包括數(shù)據(jù)和視圖地址
2谋减、方法的參數(shù)是一個(gè)自定義類型對象(從請求參數(shù)中確定的),把他重新放在 ModelAndViewContainer
3扫沼、任何目標(biāo)方法執(zhí)行完成以后都會返回 ModelAndView(數(shù)據(jù)和視圖地址)出爹。
4庄吼、processDispatchResult 處理派發(fā)結(jié)果(頁面改如何響應(yīng))
● 1、render(mv, request, response); 進(jìn)行頁面渲染邏輯
○ 1严就、根據(jù)方法的String返回值得到 View 對象【定義了頁面的渲染邏輯】
■ 1总寻、所有的視圖解析器嘗試是否能根據(jù)當(dāng)前返回值得到View對象
■ 2、得到了 redirect:/main.html --> Thymeleaf new RedirectView()
■ 3梢为、ContentNegotiationViewResolver 里面包含了下面所有的視圖解析器渐行,內(nèi)部還是利用下面所有視圖解析器得到視圖對象。
■ 4铸董、view.render(mv.getModelInternal(), request, response); 視圖對象調(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)資源也會被攔截】
*/
@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矾屯、前面的步驟有任何異常都會直接倒序觸發(fā) afterCompletion
7兼蕊、頁面成功渲染完成以后,也會倒序觸發(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ò)誤的映射
● 對于機(jī)器客戶端毅舆,它將生成JSON響應(yīng),其中包含錯(cuò)誤愈腾,HTTP狀態(tài)和異常消息的詳細(xì)信息憋活。對于瀏覽器客戶端,響應(yīng)一個(gè)“ whitelabel”錯(cuò)誤視圖虱黄,以HTML格式呈現(xiàn)相同的數(shù)據(jù)
error/下的4xx悦即,5xx頁面會被自動(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請求就會轉(zhuǎn)給controller
○ 你的異常沒有任何人能處理螃征。tomcat底層 response.sendError搪桂。error請求就會轉(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對象顾瞻。
○ 容器中的組件:類型:DefaultErrorViewResolver -> id:conventionErrorViewResolver
■ 如果發(fā)生錯(cuò)誤,會以HTTP的狀態(tài)碼 作為視圖頁地址(viewName)德绿,找到真正的頁面
■ error/404荷荤、5xx.html
如果想要返回頁面;就會找error視圖【StaticView】移稳。(默認(rèn)是一個(gè)白頁)
4蕴纳、異常處理步驟流程
1、執(zhí)行目標(biāo)方法个粱,目標(biāo)方法運(yùn)行期間有任何異常都會被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)沒有任何人能處理異常锚国,所以異常會被拋出
■ 1、如果沒有任何人能處理最終底層就會發(fā)送 /error 請求娃属。會被底層的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
, orUndertow
ServletWebServerApplicationContext 容器啟動(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)用會創(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
○ 底層直接會有一個(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)配置,可以來對數(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: # 對上面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 就會被自動(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)存儲系統(tǒng)樊销,它可以用作數(shù)據(jù)庫整慎、緩存和消息中間件
1、Redis自動(dòng)配置
自動(dòng)配置:
● RedisAutoConfiguration 自動(dòng)配置類围苫。RedisProperties 屬性類 --> spring.redis.xxx是對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é)非常單一不能聲明任何屬性蚜退,拓展的測試將會由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í)間將會返回錯(cuò)誤
● @ExtendWith :為測試類或測試方法提供擴(kuò)展類引用
3钻注、斷言(assertions)
斷言(assertions)是測試方法中的核心部分蚂且,用來對測試需要滿足的條件進(jìn)行驗(yàn)證。這些斷言方法都是 org.junit.jupiter.api.Assertions 的靜態(tài)方法
1幅恋、簡單斷言
assertEquals 判斷兩個(gè)對象或兩個(gè)原始類型是否相等
assertNotEquals 判斷兩個(gè)對象或兩個(gè)原始類型是否不相等
assertSame 判斷兩個(gè)對象引用是否指向同一個(gè)對象
assertNotSame 判斷兩個(gè)對象引用是否指向不同的對象
assertTrue 判斷給定的布爾值是否為 true
assertFalse 判斷給定的布爾值是否為 false
assertNull 判斷給定的對象引用是否為 null
assertNotNull 判斷給定的對象引用是否不為 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è)對象或原始類型的數(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將會異常
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è)】)類似于斷言泞莉,不同之處在于不滿足的斷言會使得測試方法失敗洁墙,而不滿足的前置條件只會使得測試方法的執(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饮寞,不滿足條件會使得測試執(zhí)行終止孝扛。assumingThat 的參數(shù)是表示條件的布爾值和對應(yīng)的 Executable 接口的實(shí)現(xiàn)對象。只有條件滿足時(shí)幽崩,Executable 對象才會被執(zhí)行苦始;當(dāng)條件不滿足時(shí),測試執(zhí)行并不會終止
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)碓矗С职舜蠡A(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ù)在云上部署以后淫痰,我們都需要對其進(jìn)行監(jiān)控、追蹤整份、審計(jì)待错、控制等。SpringBoot就抽取了Actuator場景烈评,使得我們每個(gè)微服務(wù)快速引用即可獲得生產(chǎn)級別的應(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),我們一般用于在云平臺否彩,平臺會定時(shí)的檢查應(yīng)用的健康狀況疯攒,我們就需要Health Endpoint可以為平臺返回當(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ì)的砂吞、層級的署恍、空間指標(biāo)信息,這些信息可以被pull(主動(dòng)推送)或者push(被動(dòng)獲任厥妗)方式得到锭汛;
● 通過Metrics對接多種監(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乡范,則會默認(rèn)配置安全訪問規(guī)則
8.3、定制 Endpoint
1啤咽、定制 Health 信息
2晋辆、定制info信息
3、定制Metrics信息
九宇整、原理解析
9.1瓶佳、Profile功能
1、application-profile功能
● 默認(rèn)配置文件 application.yaml鳞青;任何時(shí)候都會加載
● 指定環(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() 來完成對引導(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)境信息對象斩芭。StandardServletEnvironment
■ 配置環(huán)境信息對象。
● 讀取所有的配置源的配置屬性值乐疆。
■ 綁定環(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)前會創(chuàng)建 AnnotationConfigServletWebServerApplicationContext
○ 準(zhǔn)備ApplicationContext IOC容器的基本信息 prepareContext()
■ 保存環(huán)境信息
■ IOC容器的后置處理流程挤土。
■ 應(yīng)用初始化器琴庵;applyInitializers;
● 遍歷所有的 ApplicationContextInitializer 耕挨。調(diào)用 initialize.细卧。來對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