javascript
SpringBoot学习笔记~狂神
什么是SpringBoot
回顧Spring
Spring是一個開源框架,2003 年興起的一個輕量級的Java 開發框架,作者:Rod Johnson 。
Spring是為了解決企業級應用開發的復雜性而創建的,簡化開發。
什么 SpringBoot
學過javaweb的同學就知道,開發一個web應用,從最初開始接觸Servlet結合Tomcat, 跑出一個Hello Wolrld程序,是要經歷特別多的步驟;后來就用了框架Struts,再后來是SpringMVC,到了現在的SpringBoot,過一兩年又會有其他web框架出現;你們有經歷過框架不斷的演進,然后自己開發項目所有的技術也在不斷的變化、改造嗎?建議都可以去經歷一遍;
言歸正傳,什么是SpringBoot呢,就是一個javaweb的開發框架,和SpringMVC類似,對比其他javaweb框架的好處,官方說是簡化開發,約定大于配置, you can “just run”,能迅速的開發web應用,幾行代碼開發一個http接口。
所有的技術框架的發展似乎都遵循了一條主線規律:從一個復雜應用場景 衍生 一種規范框架,人們只需要進行各種配置而不需要自己去實現它,這時候強大的配置功能成了優點;發展到一定程度之后,人們根據實際生產應用情況,選取其中實用功能和設計精華,重構出一些輕量級的框架;之后為了提高開發效率,嫌棄原先的各類配置過于麻煩,于是開始提倡“約定大于配置”,進而衍生出一些一站式的解決方案。
是的這就是Java企業級應用->J2EE->spring->springboot的過程。
隨著 Spring 不斷的發展,涉及的領域越來越多,項目整合開發需要配合各種各樣的文件,慢慢變得不那么易用簡單,違背了最初的理念,甚至人稱配置地獄。Spring Boot 正是在這樣的一個背景下被抽象出來的開發框架,目的為了讓大家更容易的使用 Spring 、更容易的集成各種常用的中間件、開源軟件;
Spring Boot 基于 Spring 開發,Spirng Boot 本身并不提供 Spring 框架的核心特性以及擴展功能,只是用于快速、敏捷地開發新一代基于 Spring 框架的應用程序。也就是說,它并不是用來替代 Spring 的解決方案,而是和 Spring 框架緊密結合用于提升 Spring 開發者體驗的工具。Spring Boot 以約定大于配置的核心思想,默認幫我們進行了很多設置,多數 Spring Boot 應用只需要很少的 Spring 配置。同時它集成了大量常用的第三方庫配置(例如 Redis、MongoDB、Jpa、RabbitMQ、Quartz 等等),Spring Boot 應用中這些第三方庫幾乎可以零配置的開箱即用。
簡單來說就是SpringBoot其實不是什么新的框架,它默認配置了很多框架的使用方式,就像maven整合了所有的jar包,spring boot整合了所有的框架 。
Spring Boot 出生名門,從一開始就站在一個比較高的起點,又經過這幾年的發展,生態足夠完善,Spring Boot 已經當之無愧成為 Java 領域最熱門的技術。
Spring Boot的主要優點:
- 為所有Spring開發者更快的入門
- 開箱即用,提供各種默認配置來簡化項目配置
- 內嵌式容器簡化Web項目
- 沒有冗余代碼生成和XML配置的要求
微服務
什么是微服務
微服務也是架構,把service拆分成小模塊放在不同的電腦上,就比如springMVC中每個controller都分布在一臺電腦上,然后只提供接口,然后形成了網狀。
微服務是一種架構風格,它要求我們在開發一個應用的時倏,這個應用必須構建成一系列小服務的組合;可以通過http的方式進行互通。要說微服務架構,先得說說過去我們的單體應用架構。
單體應用架構
? 所謂單體應用架構 (all in one)是指,我們將一個應用的中的所有應用服務都封裝在一個應用中。
? 無論是ERP、CRM或是其他什么系統,你都把數據庫訪問,web訪問,等等各個功能放到一個war包內。
- 這樣做的好處是,易于開發和測試;也十分方便部署;當需要擴展時,只需要將war復制多份,然后放到多個服務器上,再做個負載均衡就可以了。
- 單體應用架構的缺點是,哪怕我要修改一個非常小的地方,我都需要停掉整個服務,重新打包、部署這個應用war包。特別是對于一個大型應用,我們不可能吧所有內容都放在一個應用里面,我們如何維護、如何分工合作都是問題。
微服務架構
all in one的架構方式,我們把所有的功能單元放在一個應用里面。然后我們把整個應用部署到服務器上。如果負載能力不行,我們將整個應用進行水平復制,進行擴展,然后在負載均衡。
所謂微服務架構,就是打破之前all in one的架構方式,把每個功能元素獨立出來。把獨立出來的功能元素的動態組合,需要的功能元素才去拿來組合,需要多一些時可以整合多個功能元素。所以微服務架構是對功能元素進行復制,而沒有對整個應用進行復制。
這樣做的好處是:
Martin Flower于2014年3月25日寫的《Microservices》,詳細的闡述了什么是微服務。
原文地址: http://martinfowler.com/articles/microservices.html
翻譯:https://www.cnblogs.com/liuning8023/p/4493156.html
如何構建一個微服務
? 一個大型系統的微服務架構,就像一個復雜交織的神經網絡,每一個神經元就是一個功能元素,它們各自完成自己的功能,然后通過http相互請求調用。比如一個電商系統,查緩存、連數據庫、瀏覽頁面、結賬、支付等服務都是一個個獨立的功能服務,都被微化了,它們作為一個個微服務共同構建了一個龐大的系統。如果修改其中的一個功能,只需要更新升級其中一個功能服務單元即可。
? 但是這種龐大的系統架構給部署和運維帶來很大的難度。于是,spring為我們帶來了構建大型分布式微服務的全套、全程產品:
- 構建一個個功能獨立的微服務應用單元,可以使用springboot,可以幫我們快速構建一個應用;
- 大型分布式網絡服務的調用,這部分由spring cloud來完成,實現分布式;
- 在分布式中間,進行流式數據計算、批處理,我們有spring cloud data flow。
- spring為我們想清楚了整個從開始構建應用到大型分布式應用全流程方案。
第一個SpringBoot程序
官方:提供了一個快速生成的網站!IDEA集成了這個網站。
**項目創建方式一:**使用Spring Initializr 的 Web頁面創建項目
1、打開 https://start.spring.io/
2、填寫項目信息
3、點擊”Generate Project“按鈕生成項目;下載此項目
4、解壓項目包,并用IDEA以Maven項目導入,一路下一步即可,直到項目導入完畢。
5、如果是第一次使用,可能速度會比較慢,包比較多、需要耐心等待一切就緒。
**項目創建方式二:**使用 IDEA 直接創建項目
1、創建一個新項目
2、選擇spring initalizr , 可以看到默認就是去官網的快速構建工具那里實現
3、填寫項目信息
4、選擇初始化的組件(初學勾選 Web 即可)
如果不勾選 在pom.xml里面加入以下代碼也可以
<!--web依賴: tomcat,dispatcherServlet,xml--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId> </dependency>5、填寫項目路徑
6、等待項目構建成功
- 項目元數據信息:創建時候輸入的Project Metadata部分,也就是Maven項目的基本元素,包括: groupld、 artifactld、version、name、description等
- parent:繼承 spring-boot-starter-parent的依賴管理,控制版本與打包等內容
- dependencies:項目具體依賴,這里包含了spring-boot-starter-web用于實現HTTP接口(該依賴中包含了Spring MVC),官網對它的描述是:使用Spring MVC構建Web(包括RESTful)應用程序的入門者,使用Tomcat作為默認嵌入式容器。; spring-boot-starter-test用于編寫單元測試的依賴包。更多功能模塊的使用我們將在后面逐步展開。
- build:構建配置部分。默認使用了spring-boot-maven-plug in,配合spring-boot-starter-parent就可以把Spring Boot應用打包成JAR來直接運行。
打包
如果遇到以上錯誤,可以配置打包時 跳過項目運行測試用例
<!--在工作中,很多情況下我們打包是不想執行測試用例的可能是測試用例不完事,或是測試用例會影響數據庫數據跳過測試用例執--> <plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><configuration><!--跳過項目運行測試用例--><skipTests>true</skipTests></configuration> </plugin>如果打包成功,則會在target目錄下生成一個 jar 包
運行jar包 java -jar jar包名字
彩蛋
如何更改啟動時顯示的字符拼成的字母,SpringBoot呢?也就是 banner 圖案;
只需一步:到項目下的 resources 目錄下新建一個banner.txt 即可。
圖案可以到:https://www.bootschool.net/ascii 這個網站生成,然后拷貝到文件中即可!
原理初探
父依賴
自動配置
pom.xml
其中它主要是依賴一個父項目,主要是管理項目的資源過濾及插件!
parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.2.5.RELEASE</version><relativePath/> <!-- lookup parent from repository --> </parent>點進去,發現還有一個父依賴
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.2.5.RELEASE</version><relativePath>../../spring-boot-dependencies</relativePath> </parent>這里才是真正管理SpringBoot應用里面所有依賴版本的地方,SpringBoot的版本控制中心;
以后我們導入依賴默認是不需要寫版本;但是如果導入的包沒有在依賴中管理著就需要手動配置版本了;
啟動器
啟動器 spring-boot-starte - xxx
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId> </dependency>啟動器就是Springboot 的啟動場景
SpringBoot將所有的功能場景都抽取出來,做成一個個的starter (啟動器),只需要在項目中引入這些starter即可,所有相關的依賴都會導入進來 , 我們要用什么功能就導入什么樣的場景啟動器即可 ;我們未來也可以自己自定義 starter;
啟動器模塊是一個 空 jar 文件,僅提供輔助性依賴管理,這些依賴可能用于自動裝配或者其他類庫;
命名歸約:
官方命名:
- 前綴:spring-boot-starter-xxx
- 比如:spring-boot-starter-web…
自定義命名:
- xxx-spring-boot-starter
- 比如:mybatis-spring-boot-starter
編寫啟動器
1、在IDEA中新建一個空項目 spring-boot-starter-diy
2、新建一個普通Maven模塊:kuang-spring-boot-starter
3、新建一個Springboot模塊:kuang-spring-boot-starter-autoconfigure
4、點擊apply即可,基本結構
5、在我們的 starter 中 導入 autoconfigure 的依賴!
<!-- 啟動器 --> <dependencies><!-- 引入自動配置模塊 --><dependency><groupId>com.kuang</groupId><artifactId>kuang-spring-boot-starter-autoconfigure</artifactId><version>0.0.1-SNAPSHOT</version></dependency> </dependencies>6、將 autoconfigure 項目下多余的文件都刪掉,Pom中只留下一個 starter,這是所有的啟動器基本配置!
7、我們編寫一個自己的服務
package com.kuang;public class HelloService {HelloProperties helloProperties;public HelloProperties getHelloProperties() {return helloProperties;}public void setHelloProperties(HelloProperties helloProperties) {this.helloProperties = helloProperties;}public String sayHello(String name){return helloProperties.getPrefix() + name + helloProperties.getSuffix();}}8、編寫HelloProperties 配置類
package com.kuang;import org.springframework.boot.context.properties.ConfigurationProperties;// 前綴 kuang.hello @ConfigurationProperties(prefix = "kuang.hello") public class HelloProperties {private String prefix;private String suffix;public String getPrefix() {return prefix;}public void setPrefix(String prefix) {this.prefix = prefix;}public String getSuffix() {return suffix;}public void setSuffix(String suffix) {this.suffix = suffix;} }9、編寫我們的自動配置類并注入bean,測試
package com.kuang;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;@Configuration @ConditionalOnWebApplication //web應用生效 @EnableConfigurationProperties(HelloProperties.class) public class HelloServiceAutoConfiguration {@AutowiredHelloProperties helloProperties;@Beanpublic HelloService helloService(){HelloService service = new HelloService();service.setHelloProperties(helloProperties);return service;}}10、在resources編寫一個自己的 META-INF\spring.factories
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.kuang.HelloServiceAutoConfiguration11、編寫完成后,可以安裝到maven倉庫中!
新建項目測試
1、新建一個SpringBoot 項目
2、導入我們自己寫的啟動器
<dependency><groupId>com.kuang</groupId><artifactId>kuang-spring-boot-starter</artifactId><version>1.0-SNAPSHOT</version> </dependency>3、編寫一個 HelloController 進行測試我們自己的寫的接口!
package com.kuang.controller;@RestController public class HelloController {@AutowiredHelloService helloService;@RequestMapping("/hello")public String hello(){return helloService.sayHello("zxc");}}4、編寫配置文件 application.properties
kuang.hello.prefix="ppp" kuang.hello.suffix="sss"5、啟動項目進行測試,結果成功 !
主程序
// @SpringBootApplication 標注這個類是一個springboot的應用 啟動類下的所有資源被導入 @SpringBootApplication public class Springboot01HelloworldApplication {public static void main(String[] args) {//將spring引應用啟動SpringApplication.run(Springboot01HelloworldApplication.class, args);} }-
注解
- @SpringBootConfiguration springboot的配置@Configuration spring配置類@Component 說明這也是一個spring的組件@EnableAutoConfiguration 自動配置@AutoConfigurationPackage 自動配置包@Import(AutoConfigurationPackages.Registrar.class) 自動配置包注冊@Import(AutoConfigurationImportSelector.class) 自動導入選擇List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); 獲取所有的配置
獲取候選的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader());Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "+ "are using a custom packaging, make sure that file is correct.");return configurations;}ETA-INF/spring.factories: 自動配置的核心文件
- @SpringBootConfiguration springboot的配置@Configuration spring配置類@Component 說明這也是一個spring的組件@EnableAutoConfiguration 自動配置@AutoConfigurationPackage 自動配置包@Import(AutoConfigurationPackages.Registrar.class) 自動配置包注冊@Import(AutoConfigurationImportSelector.class) 自動導入選擇List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); 獲取所有的配置
結論
為什么這么多的配置有的沒有生效,需要導入對應的start才能有用
@ConditionalOnXXX 核心注解 如果這里面的條件都滿足,才會生效
springboot所有自動配置都是在啟動的時候掃描并加載:spring.factories所有的自動配置類都在這里面,但是不一定生效,要判斷條件是否成立,只要導入了對應的start,就有對應的啟動器了,有了啟動器,我們自動裝配就會生效,然后就配置成功!
SpringBoot在啟動的時候從類路徑下的META-INF/spring.factories中獲取EnableAutoConfiguration指定的值
將這些值作為自動配置類導入容器 , 自動配置類就生效 , 幫我們進行自動配置工作;
整個J2EE的整體解決方案和自動配置都在springboot-autoconfigure的jar包中;
它會給容器中導入非常多的自動配置類 (xxxAutoConfiguration), 就是給容器中導入這個場景需要的所有組件 , 并配置好這些組件 ;
容器中也會存在非常多的xxxAutoConfiguration的文件(@Bean),就是這些類給容器中導入了這個場景需要的所有組件;并自動配置,@Configuration , JavaConfig !
有了自動配置類 , 免去了我們手動編寫配置注入功能組件等的工作;
SpringApplication
這個類主要做了以下四件事情
1、推斷應用的類型是普通的項目還是Web項目
2、查找并加載所有可用初始化器 , 設置到initializers屬性中
3、找出所有的應用程序監聽器,設置到listeners屬性中
4、推斷并設置main方法的定義類,找到運行的主類
SpringBoot Config
配置文件
SpringBoot使用一個全局的配置文件 , 配置文件名稱是固定的
-
application.properties
-
- 語法結構 :key=value
-
application.yml
-
- 語法結構 :key:空格value
- 語法結構 :key:空格value
對空格要求嚴格,注意空格
可以注入到配置類中
**配置文件的作用 :**修改SpringBoot自動配置的默認值,因為SpringBoot在底層都給我們自動配置好了;
比如我們可以在配置文件中修改Tomcat 默認啟動的端口號!測試一下!
yaml可以直接給實體類賦值
實體類:
/* @ConfigurationProperties作用: 將配置文件中配置的每一個屬性的值,映射到這個組件中; 告訴SpringBoot將本類中的所有屬性和配置文件中相關的配置進行綁定 參數 prefix = “person” : 將配置文件中的person下面的所有屬性一一對應 *///再配置一個person2,然后將 @ConfigurationProperties(prefix = "person2") 指向我們的person2; @ConfigurationProperties(prefix = "person")@Component //注冊bean導入依賴
<!-- 導入配置文件處理器,配置文件進行綁定就會有提示,需要重啟 --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional> </dependency>1、將配置文件的key 值 和 屬性的值設置為不一樣,則結果輸出為null,注入失敗
2、在配置一個person2,然后將 @ConfigurationProperties(prefix = “person2”) 指向我們的person2;
yaml
配置文件還可以編寫占位符生成隨機數
person:name: qinjiang${random.uuid} # 隨機uuidage: ${random.int} # 隨機inthappy: falsebirth: 2000/01/01maps: {k1: v1,k2: v2}lists:- code- girl- musicdog:name: ${person.hello:other}_旺財 //EL表達式 如果前面的存在就用前面的 不存在則用后面的age: 1加載指定的配置文件
properties配置文件在寫中文的時候,會有亂碼 , 我們需要去IDEA中設置編碼格式為UTF-8;
settings–>FileEncodings 中配置;
person.properties
name=世杰實體類
@PropertySource(value = "classpath:person.properties") @Component //注冊bean public class Person {@Value("${name}")private String name;...... }對比小結
@Value這個使用起來并不友好!我們需要為每個屬性單獨注解賦值,比較麻煩;我們來看個功能對比圖
1、@ConfigurationProperties只需要寫一次即可 , @Value則需要每個字段都添加
2、松散綁定:這個什么意思呢? 比如我的yml中寫的last-name,這個和lastName是一樣的, - 后面跟著的字母默認是大寫的。這就是松散綁定。可以測試一下
3、JSR303數據校驗 , 這個就是我們可以在字段是增加一層過濾器驗證 , 可以保證數據的合法性
4、復雜類型封裝,yml中可以封裝對象 , 使用value就不支持
結論:
配置yml和配置properties都可以獲取到值 , 強烈推薦 yml;
如果我們在某個業務中,只需要獲取配置文件中的某個值,可以使用一下 @value;
如果說,我們專門編寫了一個JavaBean來和配置文件進行一一映射,就直接@configurationProperties,不要猶豫!
JSR303數據校驗
報紅
<!--@Email注解報紅 是因為新版本需要validation啟動器 新的springBoot版本得導入spring-boot-starter-validation依賴--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId> </dependency>注解
@NotNull(message="名字不能為空") private String userName; @Max(value=120,message="年齡最大不能查過120") private int age; @Email(message="郵箱格式錯誤") private String email;空檢查 @Null 驗證對象是否為null @NotNull 驗證對象是否不為null, 無法查檢長度為0的字符串 @NotBlank 檢查約束字符串是不是Null還有被Trim的長度是否大于0,只對字符串,且會去掉前后空格. @NotEmpty 檢查約束元素是否為NULL或者是EMPTY.Booelan檢查 @AssertTrue 驗證 Boolean 對象是否為 true @AssertFalse 驗證 Boolean 對象是否為 false 長度檢查 @Size(min=, max=) 驗證對象(Array,Collection,Map,String)長度是否在給定的范圍之內 @Length(min=, max=) string is between min and max included.日期檢查 @Past 驗證 Date 和 Calendar 對象是否在當前時間之前 @Future 驗證 Date 和 Calendar 對象是否在當前時間之后 @Pattern 驗證 String 對象是否符合正則表達式的規則 正則表達式.......等等 除此以外,我們還可以自定義一些數據校驗規則環境配置位置和優先級
優先級由高到低
多環境切換
properties如何切換
application.properties
# springboot的多環境配置,可以選擇激活哪一個配置文件 spring.profiles.active = test #-后面的 # 使用application-test.properties配置文件application-dev.properties
application-test.properties
yaml
server:port: 8081 spring:profiles:active: dev--- server:port: 8082 spring:profiles: dev --- server:port: 8083 spring:profiles: test分析自動配置原理
我們以**HttpEncodingAutoConfiguration(Http編碼自動配置)**為例解釋自動配置原理;
//表示這是一個配置類,和以前編寫的配置文件一樣,也可以給容器中添加組件; @Configuration //啟動指定類的ConfigurationProperties功能;//進入這個HttpProperties查看,將配置文件中對應的值和HttpProperties綁定起來;//并把HttpProperties加入到ioc容器中 @EnableConfigurationProperties({HttpProperties.class}) //Spring底層@Conditional注解//根據不同的條件判斷,如果滿足指定的條件,整個配置類里面的配置就會生效;//這里的意思就是判斷當前應用是否是web應用,如果是,當前配置類生效 @ConditionalOnWebApplication(type = Type.SERVLET )//判斷當前項目有沒有這個類CharacterEncodingFilter;SpringMVC中進行亂碼解決的過濾器; @ConditionalOnClass({CharacterEncodingFilter.class})//判斷配置文件中是否存在某個配置:spring.http.encoding.enabled;//如果不存在,判斷也是成立的//即使我們配置文件中不配置pring.http.encoding.enabled=true,也是默認生效的; @ConditionalOnProperty(prefix = "spring.http.encoding",value = {"enabled"},matchIfMissing = true )public class HttpEncodingAutoConfiguration {//他已經和SpringBoot的配置文件映射了private final Encoding properties;//只有一個有參構造器的情況下,參數的值就會從容器中拿public HttpEncodingAutoConfiguration(HttpProperties properties) {this.properties = properties.getEncoding();}//給容器中添加一個組件,這個組件的某些值需要從properties中獲取@Bean@ConditionalOnMissingBean //判斷容器沒有這個組件?public CharacterEncodingFilter characterEncodingFilter() {CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();filter.setEncoding(this.properties.getCharset().name());filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST));filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE));return filter;}//。。。。。。。 }一句話總結 :根據當前不同的條件判斷,決定這個配置類是否生效!
- 一但這個配置類生效;這個配置類就會給容器中添加各種組件;
- 這些組件的屬性是從對應的properties類中獲取的,這些類里面的每一個屬性又是和配置文件綁定的;
- 所有在配置文件中能配置的屬性都是在xxxxProperties類中封裝著;
- 配置文件能配置什么就可以參照某個功能對應的這個屬性類
我們去配置文件里面試試前綴,看提示!
這就是自動裝配的原理!
精髓
1、SpringBoot啟動會加載大量的自動配置類
2、我們看我們需要的功能有沒有在SpringBoot默認寫好的自動配置類當中;
3、我們再來看這個自動配置類中到底配置了哪些組件;(只要我們要用的組件存在在其中,我們就不需要再手動配置了)
4、給容器中自動配置類添加組件的時候,會從properties類中獲取某些屬性。我們只需要在配置文件中指定這些屬性的值即可;
**xxxxAutoConfigurartion:自動配置類;**給容器中添加組件
xxxxProperties:封裝配置文件中相關屬性;和我們的配置文件綁定 就可以在自己的配置文件中配置了
了解:@Conditional
了解完自動裝配的原理后,我們來關注一個細節問題,自動配置類必須在一定的條件下才能生效;
@Conditional派生注解(Spring注解版原生的@Conditional作用)
作用:必須是@Conditional指定的條件成立,才給容器中添加組件,配置配里面的所有內容才生效;
那么多的自動配置類,必須在一定的條件下才能生效;也就是說,我們加載了這么多的配置類,但不是所有的都生效了。
我們怎么知道哪些自動配置類生效?
我們可以通過啟用 debug=true屬性;來讓控制臺打印自動配置報告,這樣我們就可以很方便的知道哪些自動配置類生效;
#開啟springboot的調試類 debug=truePositive matches:(自動配置類啟用的:正匹配)
Negative matches:(沒有啟動,沒有匹配成功的自動配置類:負匹配)
Unconditional classes: (沒有條件的類)
【演示:查看輸出的日志】
掌握吸收理解原理,即可以不變應萬變!
SpringBoot Web開發
靜態資源
可以獲取靜態資源的地方 按照優先級順序排序
獲取方式:webjars 里面通過pom導入maven包
只需輸入http://localhost:8080/webjars/jquery/4.1.3/jquery.js
總結:
1.在springboot,我們可以使用以下方式處理靜態資源
? public,static,/**,resources `localhost:8080/ ``
自定義靜態資源路徑
我們也可以自己通過配置文件來指定一下,哪些文件夾是需要我們放靜態資源文件的,在application.properties中配置;
spring.mvc.static-path-pattern=/hello/,classpath:/kuang/一旦自己定義了靜態文件夾的路徑,原來的自動配置就都會失效了!
首頁如何定制
靜態資源文件夾說完后,我們繼續向下看源碼!可以看到一個歡迎頁的映射,就是我們的首頁!
@Bean public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,FormattingConversionService mvcConversionService,ResourceUrlProvider mvcResourceUrlProvider) {WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(), // getWelcomePage 獲得歡迎頁this.mvcProperties.getStaticPathPattern());welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));return welcomePageHandlerMapping; }點進去繼續看
private Optional<Resource> getWelcomePage() {String[] locations = getResourceLocations(this.resourceProperties.getStaticLocations());// ::是java8 中新引入的運算符// Class::function的時候function是屬于Class的,應該是靜態方法。// this::function的funtion是屬于這個對象的。// 簡而言之,就是一種語法糖而已,是一種簡寫return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst(); } // 歡迎頁就是一個location下的的 index.html 而已 private Resource getIndexHtml(String location) {return this.resourceLoader.getResource(location + "index.html"); }歡迎頁,靜態資源文件夾下的所有 index.html 頁面;被 /** 映射。
比如我訪問 http://localhost:8080/ ,就會找靜態資源文件夾下的 index.html
新建一個 index.html ,在我們上面的3個目錄中任意一個;然后訪問測試 http://localhost:8080/ 看結果!
低版本有,高版本沒有了 2.1.7有 2.2.0就沒有了
與其他靜態資源一樣,Spring Boot在配置的靜態內容位置中查找 favicon.ico。如果存在這樣的文件,它將自動用作應用程序的favicon。
1、關閉SpringBoot默認圖標
#關閉默認圖標 spring.mvc.favicon.enabled=false2、自己放一個圖標在靜態資源目錄下,我放在 public 目錄下
3、清除瀏覽器緩存!刷新網頁,發現圖標已經變成自己的了!
Thymeleaf模板引擎
模板引擎
前端交給我們的頁面,是html頁面。如果是我們以前開發,我們需要把他們轉成jsp頁面,jsp好處就是當我們查出一些數據轉發到JSP頁面以后,我們可以用jsp輕松實現數據的顯示,及交互等。jsp支持非常強大的功能,包括能寫Java代碼,但是呢,我們現在的這種情況,SpringBoot這個項目首先是以jar的方式,不是war,像第二,我們用的還是嵌入式的Tomcat,所以呢,他現在默認是不支持jsp的。
那不支持jsp,如果我們直接用純靜態頁面的方式,那給我們開發會帶來非常大的麻煩,那怎么辦呢,SpringBoot推薦你可以來使用模板引擎。
那么這模板引擎,我們其實大家聽到很多,其實jsp就是一個模板引擎,還有以用的比較多的freemarker,包括SpringBoot給我們推薦的Thymeleaf,模板引擎有非常多,但再多的模板引擎,他們的思想都是一樣的,什么樣一個思想呢我們來看一下這張圖。
模板引擎的作用就是我們來寫一個頁面模板,比如有些值呢,是動態的,我們寫一些表達式。而這些值,從哪來呢,就是我們在后臺封裝一些數據。然后把這個模板和這個數據交給我們模板引擎,模板引擎按照我們這個數據幫你把這表達式解析、填充到我們指定的位置,然后把這個數據最終生成一個我們想要的內容給我們寫出去,這就是我們這個模板引擎,不管是jsp還是其他模板引擎,都是這個思想。只不過呢,就是說不同模板引擎之間,他們可能這個語法有點不一樣。其他的我就不介紹了,我主要來介紹一下SpringBoot給我們推薦的Thymeleaf模板引擎,這模板引擎呢,是一個高級語言的模板引擎,他的這個語法更簡單。而且呢,功能更強大。
我們呢,就來看一下這個模板引擎,那既然要看這個模板引擎。首先,我們來看SpringBoot里邊怎么用。
引入Thymeleaf
怎么引入呢,對于springboot來說,什么事情不都是一個start的事情嘛,我們去在項目中引入一下。給大家三個網址:
Thymeleaf 官網:https://www.thymeleaf.org/
Thymeleaf 在Github 的主頁:https://github.com/thymeleaf/thymeleaf
Spring官方文檔:找到我們對應的版本
https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/reference/htmlsingle/#using-boot-starter
找到對應的pom依賴:可以適當點進源碼看下本來的包!
<!--thymeleaf--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency><dependency><groupId>org.thymeleaf</groupId><artifactId>thymeleaf-spring5</artifactId></dependency><dependency><groupId>org.thymeleaf.extras</groupId><artifactId>thymeleaf-extras-java8time</artifactId></dependency>Maven會自動下載jar包,我們可以去看下下載的東西;
分析Thymeleaf
首先得按照SpringBoot的自動配置原理看一下我們這個Thymeleaf的自動配置規則,在按照那個規則,我們進行使用。
我們去找一下Thymeleaf的自動配置類:ThymeleafProperties
@ConfigurationProperties(prefix = "spring.thymeleaf" ) public class ThymeleafProperties {private static final Charset DEFAULT_ENCODING;public static final String DEFAULT_PREFIX = "classpath:/templates/";public static final String DEFAULT_SUFFIX = ".html";private boolean checkTemplate = true;private boolean checkTemplateLocation = true;private String prefix = "classpath:/templates/";private String suffix = ".html";private String mode = "HTML";private Charset encoding; }我們可以在其中看到默認的前綴和后綴!
我們只需要把我們的html頁面放在類路徑下的templates下,thymeleaf就可以幫我們自動渲染了。
使用thymeleaf什么都不需要配置,只需要將他放在指定的文件夾下即可!
測試
1、編寫一個TestController
@Controller public class TestController {@RequestMapping("/t1")public String test1(){//classpath:/templates/test.htmlreturn "test";}}2、編寫一個測試頁面 test.html 放在 templates 目錄下
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> <h1>測試頁面</h1></body> </html>3、啟動項目請求測試
Thymeleaf語法
學習語法,還是參考官網文檔最為準確,我們找到對應的版本看一下;
Thymeleaf 官網:https://www.thymeleaf.org/ , 簡單看一下官網!我們去下載Thymeleaf的官方文檔!
我們做個最簡單的練習 :我們需要查出一些數據,在頁面中展示
1、修改測試請求,增加數據傳輸;
@RequestMapping("/t1") public String test1(Model model){//存入數據model.addAttribute("msg","Hello,Thymeleaf");//classpath:/templates/test.htmlreturn "test"; }2、我們要使用thymeleaf,需要在html文件中導入命名空間的約束,方便提示。
我們可以去官方文檔的#3中看一下命名空間拿來過來:
xmlns:th="http://www.thymeleaf.org"3、我們去編寫下前端頁面
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head><meta charset="UTF-8"><title>狂神說</title> </head> <body> <h1>測試頁面</h1><!--th:text就是將div中的內容設置為它指定的值,和之前學習的Vue一樣--> <div th:text="${msg}"></div> </body> </html>4、啟動測試!
認真學習語法
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-04DJdgBA-1621008881187)(SpringBoot.assets/image-20201217210034131.png)]
能寫哪些表達式
Simple expressions:(表達式語法) Variable Expressions: ${...}:獲取變量值;OGNL;1)、獲取對象的屬性、調用方法2)、使用內置的基本對象:#18#ctx : the context object.#vars: the context variables.#locale : the context locale.#request : (only in Web Contexts) the HttpServletRequest object.#response : (only in Web Contexts) the HttpServletResponse object.#session : (only in Web Contexts) the HttpSession object.#servletContext : (only in Web Contexts) the ServletContext object.3)、內置的一些工具對象:#execInfo : information about the template being processed.#uris : methods for escaping parts of URLs/URIs#conversions : methods for executing the configured conversion service (if any).#dates : methods for java.util.Date objects: formatting, component extraction, etc.#calendars : analogous to #dates , but for java.util.Calendar objects.#numbers : methods for formatting numeric objects.#strings : methods for String objects: contains, startsWith, prepending/appending, etc.#objects : methods for objects in general.#bools : methods for boolean evaluation.#arrays : methods for arrays.#lists : methods for lists.#sets : methods for sets.#maps : methods for maps.#aggregates : methods for creating aggregates on arrays or collections. ==================================================================================Selection Variable Expressions: *{...}:選擇表達式:和${}在功能上是一樣;Message Expressions: #{...}:獲取國際化內容Link URL Expressions: @{...}:定義URL;Fragment Expressions: ~{...}:片段引用表達式Literals(字面量)Text literals: 'one text' , 'Another one!' ,…Number literals: 0 , 34 , 3.0 , 12.3 ,…Boolean literals: true , falseNull literal: nullLiteral tokens: one , sometext , main ,…Text operations:(文本操作)String concatenation: +Literal substitutions: |The name is ${name}|Arithmetic operations:(數學運算)Binary operators: + , - , * , / , %Minus sign (unary operator): -Boolean operations:(布爾運算)Binary operators: and , orBoolean negation (unary operator): ! , notComparisons and equality:(比較運算)Comparators: > , < , >= , <= ( gt , lt , ge , le )Equality operators: == , != ( eq , ne )Conditional operators:條件運算(三元運算符)If-then: (if) ? (then)If-then-else: (if) ? (then) : (else)Default: (value) ?: (defaultvalue)Special tokens:No-Operation: _SpringMVC配置
官網閱讀
在進行項目編寫前,我們還需要知道一個東西,就是SpringBoot對我們的SpringMVC還做了哪些配置,包括如何擴展,如何定制。
只有把這些都搞清楚了,我們在之后使用才會更加得心應手。途徑一:源碼分析,途徑二:官方文檔!
地址 :https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/reference/htmlsingle/#boot-features-spring-mvc-auto-configuration
Spring MVC Auto-configuration // Spring Boot為Spring MVC提供了自動配置,它可以很好地與大多數應用程序一起工作。 Spring Boot provides auto-configuration for Spring MVC that works well with most applications. // 自動配置在Spring默認設置的基礎上添加了以下功能: The auto-configuration adds the following features on top of Spring’s defaults: // 包含視圖解析器 Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans. // 支持靜態資源文件夾的路徑,以及webjars Support for serving static resources, including support for WebJars // 自動注冊了Converter: // 轉換器,這就是我們網頁提交數據到后臺自動封裝成為對象的東西,比如把"1"字符串自動轉換為int類型 // Formatter:【格式化器,比如頁面給我們了一個2019-8-10,它會給我們自動格式化為Date對象】 Automatic registration of Converter, GenericConverter, and Formatter beans. // HttpMessageConverters // SpringMVC用來轉換Http請求和響應的的,比如我們要把一個User對象轉換為JSON字符串,可以去看官網文檔解釋; Support for HttpMessageConverters (covered later in this document). // 定義錯誤代碼生成規則的 Automatic registration of MessageCodesResolver (covered later in this document). // 首頁定制 Static index.html support. // 圖標定制 Custom Favicon support (covered later in this document). // 初始化數據綁定器:幫我們把請求數據綁定到JavaBean中! Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document)./* 如果您希望保留Spring Boot MVC功能,并且希望添加其他MVC配置(攔截器、格式化程序、視圖控制器和其他功能),則可以添加自己 的@configuration類,類型為webmvcconfiguer,但不添加@EnableWebMvc。如果希望提供 RequestMappingHandlerMapping、RequestMappingHandlerAdapter或ExceptionHandlerExceptionResolver的自定義 實例,則可以聲明WebMVCregistrationAdapter實例來提供此類組件。 */ If you want to keep Spring Boot MVC features and you want to add additional MVC configuration (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc. If you wish to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver, you can declare a WebMvcRegistrationsAdapter instance to provide such components.// 如果您想完全控制Spring MVC,可以添加自己的@Configuration,并用@EnableWebMvc進行注釋。 If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc.ContentNegotiatingViewResolver 內容協商視圖解析器
自動配置了ViewResolver,就是我們之前學習的SpringMVC的視圖解析器;
即根據方法的返回值取得視圖對象(View),然后由視圖對象決定如何渲染(轉發,重定向)。
我們去看看這里的源碼:我們找到 WebMvcAutoConfiguration , 然后搜索ContentNegotiatingViewResolver。找到如下方法!
@Bean @ConditionalOnBean(ViewResolver.class) @ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class) public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();resolver.setContentNegotiationManager(beanFactory.getBean(ContentNegotiationManager.class));// ContentNegotiatingViewResolver使用所有其他視圖解析器來定位視圖,因此它應該具有較高的優先級resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);return resolver; }點進這類看看!找到對應的解析視圖的代碼;
getCandidateViews中看到他是把所有的視圖解析器拿來,進行while循環,挨個解析!
Iterator var5 = this.viewResolvers.iterator();得出結論:ContentNegotiatingViewResolver 這個視圖解析器就是用來組合所有的視圖解析器的
我們再去研究下他的組合邏輯,看到有個屬性viewResolvers,看看它是在哪里進行賦值的!
protected void initServletContext(ServletContext servletContext) {// 這里它是從beanFactory工具中獲取容器中的所有視圖解析器// ViewRescolver.class 把所有的視圖解析器來組合的Collection<ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(this.obtainApplicationContext(), ViewResolver.class).values();ViewResolver viewResolver;if (this.viewResolvers == null) {this.viewResolvers = new ArrayList(matchingBeans.size());}// ............... }既然它是在容器中去找視圖解析器,我們是否可以猜想,我們就可以去實現一個視圖解析器了呢?
我們可以自己給容器中去添加一個視圖解析器;這個類就會幫我們自動的將它組合進來;我們去實現一下
1、我們在我們的主程序中去寫一個視圖解析器來試試;
@Bean //放到bean中 public ViewResolver myViewResolver(){return new MyViewResolver(); }//我們寫一個靜態內部類,視圖解析器就需要實現ViewResolver接口 private static class MyViewResolver implements ViewResolver{@Overridepublic View resolveViewName(String s, Locale locale) throws Exception {return null;} }2、怎么看我們自己寫的視圖解析器有沒有起作用呢?
我們給 DispatcherServlet 中的 doDispatch方法 加個斷點進行調試一下,因為所有的請求都會走到這個方法中
3、我們啟動我們的項目,然后隨便訪問一個頁面,看一下Debug信息;
找到this
找到視圖解析器,我們看到我們自己定義的就在這里了;
所以說,我們如果想要使用自己定制化的東西,我們只需要給容器中添加這個組件就好了!剩下的事情SpringBoot就會幫我們做了!
修改SpringBoot的默認配置
這么多的自動配置,原理都是一樣的,通過這個WebMVC的自動配置原理分析,我們要學會一種學習方式,通過源碼探究,得出結論;這個結論一定是屬于自己的,而且一通百通。
SpringBoot的底層,大量用到了這些設計細節思想,所以,沒事需要多閱讀源碼!得出結論;
SpringBoot在自動配置很多組件的時候,先看容器中有沒有用戶自己配置的(如果用戶自己配置@bean),如果有就用用戶配置的,如果沒有就用自動配置的;
如果有些組件可以存在多個,比如我們的視圖解析器,就將用戶配置的和自己默認的組合起來!
擴展使用SpringMVC 官方文檔如下:
If you want to keep Spring Boot MVC features and you want to add additional MVC configuration (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc. If you wish to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver, you can declare a WebMvcRegistrationsAdapter instance to provide such components.
我們要做的就是編寫一個@Configuration注解類,并且類型要為WebMvcConfigurer,還不能標注@EnableWebMvc注解;我們去自己寫一個;我們新建一個包叫config,寫一個類MyMvcConfig;
//應為類型要求為WebMvcConfigurer,所以我們實現其接口 //可以使用自定義類擴展MVC的功能 @Configuration public class MyMvcConfig implements WebMvcConfigurer {@Overridepublic void addViewControllers(ViewControllerRegistry registry) {// 瀏覽器發送/test , 就會跳轉到test頁面;registry.addViewController("/test").setViewName("test");} }確實也跳轉過來了!所以說,我們要擴展SpringMVC,官方就推薦我們這么去使用,既保SpringBoot留所有的自動配置,也能用我們擴展的配置!
我們可以去分析一下原理:
1、WebMvcAutoConfiguration 是 SpringMVC的自動配置類,里面有一個類WebMvcAutoConfigurationAdapter
2、這個類上有一個注解,在做其他自動配置時會導入:@Import(EnableWebMvcConfiguration.class)
3、我們點進EnableWebMvcConfiguration這個類看一下,它繼承了一個父類:DelegatingWebMvcConfiguration
這個父類中有這樣一段代碼:
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();// 從容器中獲取所有的webmvcConfigurer@Autowired(required = false)public void setConfigurers(List<WebMvcConfigurer> configurers) {if (!CollectionUtils.isEmpty(configurers)) {this.configurers.addWebMvcConfigurers(configurers);}} }4、我們可以在這個類中去尋找一個我們剛才設置的viewController當做參考,發現它調用了一個
protected void addViewControllers(ViewControllerRegistry registry) {this.configurers.addViewControllers(registry); }5、我們點進去看一下
public void addViewControllers(ViewControllerRegistry registry) {Iterator var2 = this.delegates.iterator();while(var2.hasNext()) {// 將所有的WebMvcConfigurer相關配置來一起調用!包括我們自己配置的和Spring給我們配置的WebMvcConfigurer delegate = (WebMvcConfigurer)var2.next();delegate.addViewControllers(registry);}}所以得出結論:所有的WebMvcConfiguration都會被作用,不止Spring自己的配置類,我們自己的配置類當然也會被調用;
全面接管SpringMVC
官方文檔:
If you want to take complete control of Spring MVCyou can add your own @Configuration annotated with @EnableWebMvc.全面接管即:SpringBoot對SpringMVC的自動配置不需要了,所有都是我們自己去配置!
只需在我們的配置類中要加一個@EnableWebMvc。
我們看下如果我們全面接管了SpringMVC了,我們之前SpringBoot給我們配置的靜態資源映射一定會無效,我們可以去測試一下;
不加注解之前,訪問首頁:
給配置類加上注解:@EnableWebMvc
我們發現所有的SpringMVC自動配置都失效了!回歸到了最初的樣子;
當然,我們開發中,不推薦使用全面接管SpringMVC
思考問題?為什么加了一個注解,自動配置就失效了!我們看下源碼:
1、這里發現它是導入了一個類,我們可以繼續進去看
@Import({DelegatingWebMvcConfiguration.class}) public @interface EnableWebMvc { }2、它繼承了一個父類 WebMvcConfigurationSupport
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {// ...... } @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class }) // 這個注解的意思就是:容器中沒有這個組件的時候,這個自動配置類才生效 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10) @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,ValidationAutoConfiguration.class }) public class WebMvcAutoConfiguration {}總結一句話:@EnableWebMvc將WebMvcConfigurationSupport組件導入進來了;
而導入的WebMvcConfigurationSupport只是SpringMVC最基本的功能!
在springboot中,有非常多的xxxx Configuration幫助我們進行擴展配置,只要看見了這個東西,我們就要注意了!
Springboot項目
首頁
首頁配置,所有的頁面靜態資源都需要被Thymeleaf接管 @{} href src等等
國際化
2020版本idea可視化配置有問題
返不回去的話點上面的index 再點下面的text就可以返回了
在Spring中有一個國際化的Locale (區域信息對象);里面有一個叫做LocaleResolver (獲取區域信息對象)的解析器!
我們去我們webmvc自動配置文件,尋找一下!看到SpringBoot默認配置:
@Bean @ConditionalOnMissingBean @ConditionalOnProperty(prefix = "spring.mvc", name = "locale") public LocaleResolver localeResolver() {// 容器中沒有就自己配,有的話就用用戶配置的if (this.mvcProperties.getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {return new FixedLocaleResolver(this.mvcProperties.getLocale());}// 接收頭國際化分解AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();localeResolver.setDefaultLocale(this.mvcProperties.getLocale());return localeResolver; }AcceptHeaderLocaleResolver 這個類中有一個方法
public Locale resolveLocale(HttpServletRequest request) {Locale defaultLocale = this.getDefaultLocale();// 默認的就是根據請求頭帶來的區域信息獲取Locale進行國際化if (defaultLocale != null && request.getHeader("Accept-Language") == null) {return defaultLocale;} else {Locale requestLocale = request.getLocale();List<Locale> supportedLocales = this.getSupportedLocales();if (!supportedLocales.isEmpty() && !supportedLocales.contains(requestLocale)) {Locale supportedLocale = this.findSupportedLocale(request, supportedLocales);if (supportedLocale != null) {return supportedLocale;} else {return defaultLocale != null ? defaultLocale : requestLocale;}} else {return requestLocale;}} }分析之后可以自己寫一個
整個過程
自動配置會根據我們的環境來更改語言,比較高級 使用#{}來取值 如下圖
攔截器
LoginHandleInterceptor.java
public class LoginHandleInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//登錄成功之后,應該有用戶的sessionObject loginUsername = request.getSession().getAttribute("loginUsername");if (loginUsername==null){//沒有登錄request.setAttribute("msg","沒有權限,請先登錄");request.getRequestDispatcher("/index.html").forward(request,response);return false;}else {return true;}} }在MyMvcConfig中配置 注意過濾靜態資源
@Override public void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginHandleInterceptor()).addPathPatterns("/**").excludePathPatterns("/index.html","/","/user/login","/css/**","/js/**","/img/**"); }頁面公共部分抽取
th:fragment="sidebar" 定義抽取部分的名字引用頁面部分的時候要用~{頁面名::頁面部分名} 注意路徑要正確完整
<div th:insert="~{dashboard::sidebar} "></div>提取公共界面 傳參高亮
<div th:replace="~{common/commons::sidebar(active='main.html')}"></div>獲取參數 判斷 賦值不一樣的class
<a th:class="${active=='main.html'?'nav-link active':'nav-link'}" th:href="@{/dashboard}"></a>thymeleaf模板中insert/include/replace三種引用fragment方式的區別
insert: 把整個fragment(包括fragment的節點tag)插入到當前節點內部,
replace:用fragment(包括fragment的節點tag)替換掉當前節點
include:把fragment的內容(不包括fragment的節點)插入到當前節點內容
CRUD
注意參數傳遞,取值,放值等問題。
404
在template文件夾下面新建一個error文件夾 把錯誤界面放進去就可以自動找到了
把對應的錯誤界面放進去就可以了
如何寫網站
1.前端搞定:頁面長什么樣子:數據
2.設計教據庫(數據庫設計難點!
3.前端讓他能夠自動運行,獨立化工程
4.數據接口如何對接: json,對象all in one !
5.前后端聯調測試!
數據庫 Druid
spring Data簡介
對于數據訪問層,無論是 SQL(關系型數據庫) 還是 NOSQL(非關系型數據庫),Spring Boot 底層都是采用 Spring Data 的方式進行統一處理。
Spring Boot 底層都是采用 Spring Data 的方式進行統一處理各種數據庫,Spring Data 也是 Spring 中與 Spring Boot、Spring Cloud 等齊名的知名項目。
Sping Data 官網:https://spring.io/projects/spring-data
數據庫相關的啟動器 :可以參考官方文檔:
https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/reference/htmlsingle/#using-boot-starter
JDBCTemplate
1、有了數據源(com.zaxxer.hikari.HikariDataSource),然后可以拿到數據庫連接(java.sql.Connection),有了連接,就可以使用原生的 JDBC 語句來操作數據庫;
2、即使不使用第三方第數據庫操作框架,如 MyBatis等,Spring 本身也對原生的JDBC 做了輕量級的封裝,即JdbcTemplate。
3、數據庫操作的所有 CRUD 方法都在 JdbcTemplate 中。
4、Spring Boot 不僅提供了默認的數據源,同時默認已經配置好了 JdbcTemplate 放在了容器中,程序員只需自己注入即可使用
5、JdbcTemplate 的自動配置是依賴 org.springframework.boot.autoconfigure.jdbc 包下的 JdbcTemplateConfiguration 類
JdbcTemplate主要提供以下幾類方法:
- execute方法:可以用于執行任何SQL語句,一般用于執行DDL語句;
- update方法及batchUpdate方法:update方法用于執行新增、修改、刪除等語句;batchUpdate方法用于執行批處理相關語句;
- query方法及queryForXXX方法:用于執行查詢相關語句;
- call方法:用于執行存儲過程、函數相關語句。
Druid
簡介
Java程序很大一部分要操作數據庫,為了提高性能操作數據庫的時候,又不得不使用數據庫連接池。
Druid 是阿里巴巴開源平臺上一個數據庫連接池實現,結合了 C3P0、DBCP 等 DB 池的優點,同時加入了日志監控。
Druid 可以很好的監控 DB 池連接和 SQL 的執行情況,天生就是針對監控而生的 DB 連接池。
Druid已經在阿里巴巴部署了超過600個應用,經過一年多生產環境大規模部署的嚴苛考驗。
Spring Boot 2.0 以上默認使用 Hikari 數據源,可以說 Hikari 與 Driud 都是當前 Java Web 上最優秀的數據源,我們來重點介紹 Spring Boot 如何集成 Druid 數據源,如何實現數據庫監控。
Github地址:https://github.com/alibaba/druid/
com.alibaba.druid.pool.DruidDataSource 基本配置參數如下:
配置數據源
1、添加上 Druid 數據源依賴。 可以加上log4j
<!-- https://mvnrepository.com/artifact/com.alibaba/druid --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.4</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency>2、切換數據源;之前已經說過 Spring Boot 2.0 以上默認使用 com.zaxxer.hikari.HikariDataSource 數據源,但可以 通過 spring.datasource.type 指定數據源。
spring:datasource:username: rootpassword: 123456url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8driver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSource # 自定義數據源3、數據源切換之后,在測試類中注入 DataSource,然后獲取到它,輸出一看便知是否成功切換;
4、切換成功!既然切換成功,就可以設置數據源連接初始化大小、最大連接數、等待時間、最小連接數 等設置項;可以查看源碼
spring:datasource:username: rootpassword: 123456#?serverTimezone=UTC解決時區的報錯url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8driver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSource#Spring Boot 默認是不注入這些屬性值的,需要自己綁定#druid 數據源專有配置initialSize: 5minIdle: 5maxActive: 20maxWait: 60000timeBetweenEvictionRunsMillis: 60000minEvictableIdleTimeMillis: 300000validationQuery: SELECT 1 FROM DUALtestWhileIdle: truetestOnBorrow: falsetestOnReturn: falsepoolPreparedStatements: true#配置監控統計攔截的filters,stat:監控統計、log4j:日志記錄、wall:防御sql注入#如果允許時報錯 java.lang.ClassNotFoundException: org.apache.log4j.Priority#則導入 log4j 依賴即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4jfilters: stat,wall,log4jmaxPoolPreparedStatementPerConnectionSize: 20useGlobalDataSourceStat: trueconnectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5005、導入log4j
<!-- https://mvnrepository.com/artifact/log4j/log4j --> <dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version> </dependency>6、現在需要程序員自己為 DruidDataSource 綁定全局配置文件中的參數,再添加到容器中,而不再使用 Spring Boot 的自動生成了;我們需要 自己添加 DruidDataSource 組件到容器中,并綁定屬性;
配置Druid數據源監控
Druid 數據源具有監控的功能,并提供了一個 web 界面方便用戶查看,類似安裝 路由器 時,人家也提供了一個默認的 web 頁面。
所以第一步需要設置 Druid 的后臺管理頁面,比如 登錄賬號、密碼 等;配置后臺管理;
//配置 Druid 監控管理后臺的Servlet; //內置 Servlet 容器時沒有web.xml文件,所以使用 Spring Boot 的注冊 Servlet 方式 @Bean public ServletRegistrationBean statViewServlet() {ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");// 這些參數可以在 com.alibaba.druid.support.http.StatViewServlet // 的父類 com.alibaba.druid.support.http.ResourceServlet 中找到Map<String, String> initParams = new HashMap<>();initParams.put("loginUsername", "admin"); //后臺管理界面的登錄賬號initParams.put("loginPassword", "123456"); //后臺管理界面的登錄密碼//后臺允許誰可以訪問//initParams.put("allow", "localhost"):表示只有本機可以訪問//initParams.put("allow", ""):為空或者為null時,表示允許所有訪問initParams.put("allow", "");//deny:Druid 后臺拒絕誰訪問//initParams.put("kuangshen", "192.168.1.20");表示禁止此ip訪問//設置初始化參數bean.setInitParameters(initParams);return bean; }配置完畢后,我們可以選擇訪問 :http://localhost:8080/druid/login.html
整合Mybatis
Mybatis啟動器
<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter --> <dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.1</version> </dependency>兩種表示這是一個mybatis的mapper類
1、在mapper接口上添加注解@Mapper
2、在啟動類上加@MapperScan(“要掃描的包”) 比如com.can.mapper
Mybatis整合
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.1</version> </dependency> #整合Mybatis mybatis.type-aliases-package=com.can.pojo mybatis.mapper-locations=classpath:mybatis/mapper/*.xmlSpringSecurity(安全)
-
在web開發中,安全第一位!過濾器,攔截器也可以實現
功能性需求:非功能性需求
做網站: 安全應該在還說呢么時候考慮? 設計之初
- 漏洞,隱私泄露~
- 架構一旦確定,再加安全 就不容易了
shiro、SpringSecurity: 很像~除了類不一樣,名字不一樣;認證,授權
認證,授權(vip1,vip2,vip3)權限不同
簡介
Spring Security是針對Spring項目的安全框架,也是Spring Boot底層安全模塊默認的技術選型,他可以實現強大的Web安全控制,對于安全控制,我們僅需要引入spring-boot-starter-security模塊,進行少量的配置,即可實現強大的安全管理!
記住幾個類:
- WebSecurityConfigurerAdapter:自定義Security策略
- AuthenticationManagerBuilder:自定義認證策略
- @EnableWebSecurity:開啟WebSecurity模式
Spring Security的兩個主要目標是“認證”和“授權”(訪問控制)。
“認證”(Authentication)
“授權”(Authorization)
這個概念是通用的,而不是只在Spring security中存在。
參考官網: https://spring.io/projects/spring-security。查看我們自己項目中的版本,找到對應的幫助文檔:https://docs.spring.io/spring-security/site/docs/5.2.0.RELEASE/reference/htmlsingle
security和thymeleaf整合
<!--security和Thymeleaf整合包--> <!-- https://mvnrepository.com/artifact/org.thymeleaf.extras/thymeleaf-extras-springsecurity5 --> <dependency><groupId>org.thymeleaf.extras</groupId><artifactId>thymeleaf-extras-springsecurity5</artifactId><version>3.0.4.RELEASE</version> </dependency>thymeleaf需要導入的東西<dependency><groupId>org.thymeleaf</groupId><artifactId>thymeleaf-spring5</artifactId></dependency><dependency><groupId>org.thymeleaf.extras</groupId><artifactId>thymeleaf-extras-java8time</artifactId></dependency>主要就是一下兩句有問題導致報錯 狂神角色寫錯了
用戶名:<span sec:authentication="name"></span> 角色:<span sec:authentication="authorities"></span>命名空間
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">springsecurity4不支持高版本 但是彈幕好像說支持2.3.3
springsecurity4點擊注銷 會報錯 因為springsecurity4自動開啟了csrf功能
但是springsecurity5不用這個操作,據說好像是logout的時候用了post傳輸,因為是a標簽,舊版沒有做處理,只能用get傳輸。因為get是明文傳輸,容易被攻擊,所以默認開啟了csrf。
//防止網站攻擊: get post //關閉csrf功能 http.csrf().disable();使用自己的登錄界面loginPage("/toLogin")自定義登錄請求。 .loginProcessingUrl("/login")自定義登錄請求接收
需要跟登錄頁提交的地址一致。
不用定義登錄請求接收的話需要將登錄表單提交到toLogin。
如果加了loginProcessingUrl的話 前端傳入的 用戶名不是username和密碼不是password的話會error
但是可以自定義接收屬性
.usernameParameter("user").passwordParameter("pwd")Shiro 安全 容易被面試提問
官網:http://shiro.apache.org/index.html
簡介:
Shiro是Apache旗下的一個開源項目,它是一個非常易用的安全框架,提供了包括認證、授權、加密、會話管理等功能,與Spring Security一樣屬基于權限的安全框架,但是與Spring Security 相比,Shiro使用了比較簡單易懂易于使用的授權方式。Shiro屬于輕量級框架,相對于Spring Security簡單很多,并沒有security那么復雜。
優勢特點
它是一個功能強大、靈活的、優秀的、開源的安全框架。
它可以勝任身份驗證、授權、企業會話管理和加密等工作。
它易于使用和理解,與Spring Security相比,入門門檻低。
主要功能
- 驗證用戶身份
- 用戶訪問權限控制
- 支持單點登錄(SSO)功能
- 可以響應認證、訪問控制,或Session事件
- 支持提供“Remember Me”服務
框架體系
Shiro 的整體框架大致如下圖所示(圖片來自互聯網):
Authentication(認證), Authorization(授權), Session Management(會話管理), Cryptography(加密)代表Shiro應用安全的四大基石。
它們分別是:
- Authentication(認證):用戶身份識別,通常被稱為用戶“登錄”。
- Authorization(授權):訪問控制。比如某個用戶是否具有某個操作的使用權限。
- Session Management(會話管理):特定于用戶的會話管理,甚至在非web 應用程序。
- Cryptography(加密):在對數據源使用加密算法加密的同時,保證易于使用。
除此之外,還有其他的功能來支持和加強這些不同應用環境下安全領域的關注點。
特別是對以下的功能支持:
- Web支持:Shiro 提供的 web 支持 api ,可以很輕松的保護 web 應用程序的安全。
- 緩存:緩存是 Apache Shiro 保證安全操作快速、高效的重要手段。
- 并發:Apache Shiro 支持多線程應用程序的并發特性。
- 測試:支持單元測試和集成測試,確保代碼和預想的一樣安全。
- “Run As”:這個功能允許用戶在許可的前提下假設另一個用戶的身份。
- “Remember Me”:跨 session 記錄用戶的身份,只有在強制需要時才需要登錄。
架構(外部)
在概念層,Shiro 架構包含三個主要的理念:Subject, SecurityManager 和 Realm。下面的圖展示了這些組件如何相互作用,我們將在下面依次對其進行描述。
Shiro執行流程圖(圖片來自互聯網)
三個主要理念/三大對象 重點!!!!
- Subject:代表當前用戶,Subject 可以是一個人,也可以是第三方服務、守護進程帳戶、時鐘守護任務或者其它當前和軟件交互的任何事件。
- SecurityManager:管理所有Subject,SecurityManager 是 Shiro 架構的核心,配合內部安全組件共同組成安全傘。
- Realms:用于進行權限信息的驗證,我們自己實現。Realm 本質上是一個特定的安全 DAO:它封裝與數據源連接的細節,得到Shiro 所需的相關的數據。在配置 Shiro 的時候,你必須指定至少一個Realm 來實現認證(authentication)和/或授權(authorization)。
我們需要實現Realms的Authentication 和 Authorization。其中 Authentication 是用來驗證用戶身份,Authorization 是授權訪問控制,用于對用戶進行的操作授權,證明該用戶是否允許進行當前操作,如訪問某個鏈接,某個資源文件等。
架構(內部)
- Subject:任何可以與應用交互的’用戶’;
- Security Manager: 相當于SpringMVC中的DispatcherServlet;是Shiro的心臟,所有具體的交互都通過Security Manager進行控制,它管理者所有的Subject,且負責進行認證,授權,會話,及緩存的管理。
- Authenticator:負責Subject認證,是一個擴展點,可以自定義實現;可以使用認證策略(Authenticationstrategy),即什么情況下算用戶認證通過了;
- Authorizer:授權器,即訪問控制器,用來決定主體是否有權限進行相應的操作;即控制著用戶能訪問應用中的那些功能;
- Realm: 可以有一個或者多個的realm,可以認為是安全實體數據源,即用于獲取安全實體的,可以用DBC實現,也可以是內存實現等等,由用戶提供;所以一般在應用中都需要實現自己的realm
- SessionManager:管理Session生命周期的組件,而Shiro并不僅僅可以用在Web環境,也可以用在普通的JavaSE環境中
- CacheManager:緩存控制器,來管理如用戶,角色,權限等緩存的;因為這些數據基本上很少改變,放到緩存中后可以提高訪問的性能;
- Cryptography:密碼模塊,Shiro提高了一些常見的加密組件用于密碼加密,解密等
spring整合shio
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.4.1</version> </dependency> //得到一個用戶 subject currentuser = securityutils.getsubject( ); //通過當前用戶得到session session session = currentuser.getsession(); //判斷當前用戶是否被認證 currentuser.isAuthenticated (); //獲得當前用戶的認證 currentuser.getPrincipal (); //獲得用戶是否擁有這個角色 currentuser.hasRole("schwartz"); //獲得當前用戶的權限 currentuser.ispermitted("lightsaber:wield"); //注銷 currentuser.logout();和security區別
沒有自定義登錄界面 需要自己動手寫
整合Mybatis
加上druid
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId> </dependency><!-- https://mvnrepository.com/artifact/com.alibaba/druid --> <dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.4</version> </dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version> </dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.1</version> </dependency>連接上數據庫之后
mybatis.type-aliases-package=com.can.pojo mybatis.mapper-locations=classpath:mapper/*.xml整合Thymeleaf
導入包
<dependency><groupId>com.github.theborakompanioni</groupId><artifactId>thymeleaf-extras-shiro</artifactId><version>2.0.0</version> </dependency>需要配置 shiroConfig.java
@Bean //整合ShiroDialect:用來整合shiro Thymeleaf public ShiroDialect getShiroDialect(){return new ShiroDialect(); }命名空間
xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"Swagger
- 號稱世界上最流行的Api框架;
- RestFul Api文檔在線自動生成工具=>Api文檔與API定義同步更新
- 直接運行,可以在線測試API接口;
- 支持多種語言: (Java,Php…)
官網:https://swagger.io/
在項目中使用需要springfox
-
swagger2
-
ui
SpringBoot集成Swagger
新建一個springweb項目
導包
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 --> <dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.9.2</version> </dependency><!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui --> <dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactId><version>2.9.2</version> </dependency>編寫一個Hello
配置Swagger==>Config
@Configuration @EnableSwagger2 //開啟Swagger2 public class SwaggerConfig { }測試運行
訪問 : http://localhost:8080/swagger-ui.html
配置Swagger
Swagger的beansh實例Docket
//配置了Swagger的Docket實例 @Bean public Docket docket(){return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()); }//配置Swagger信息 = apiInfo private ApiInfo apiInfo(){//作者信息Contact DEFAULT_CONTACT = new Contact("燦燦", "https://m.gmw.cn/baijia/2020-12/23/1301967099.html", "821057799@qq.com");return new ApiInfo("燦燦的SwaggerApI文檔","不想描述","1.0", "https://www.baidu.com/",DEFAULT_CONTACT,"Apache 2.0","http://www.apache.org/licenses/LICENSE-2.0",new ArrayList<VendorExtension>()); }Swagger配置掃描接口
Docket.select()
只希望在生產環境中使用,在發布的時候不使用
- 判斷是不是生產環境 flag = false
- 注入enable(flag)
在application中配置
#指定誰激活 spring.profiles.active=dev然后config中寫代碼
如何配置多個分組,多個Docket實例就可以,不能重名
controller
//只要接口中,返回值存在實體類,他就會被掃描到Swagger中 @PostMapping(value = "/user") public User user(){return new User(); }注釋
//給生成的文檔加注釋 //ApiModel 用在類上 @ApiModel("用戶實體類") public class User {//ApiModelProperty用在方法上 不是public的字段上不會在文檔中顯示 私有屬性要加set get@ApiModelProperty("用戶名")public String username;@ApiModelProperty("密碼")public String password; }總結:
Swagger是一個優秀的工具,幾乎所有的大公司都有使用它
注意:
在正式發布的時候,關閉Swagger!出于安全考慮并且節省運行的內存
任務
異步任務~
定時任務~
郵件任務~
異步任務
//告訴Spring這是一個異步的方法 @Async public void hello(){try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("數據正在處理...."); }在Spring主啟動類里面啟動
//開啟異步注解功能 @EnableAsync郵件任務
導入郵箱包
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId> </dependency>配置 application.properties
spring.mail.username=821057799@qq.com spring.mail.password=qwjsdodvnxlrbcaa spring.mail.host=smtp.qq.com # 開啟加密驗證 spring.mail.properties.mail.smtp.enable=true測試
@Test void contextLoads() {boolean html = true;String subject = "哈嘍!";String text = "<p style='color:red'>李詩雨愛張世杰</p>";ArrayList<String> pathnames = new ArrayList<>();String toBody = "1537752955@qq.com";String fromBody = "821057799@qq.com";pathnames.add("C:\\Users\\86177\\Desktop\\1.jpg");pathnames.add("C:\\Users\\86177\\Desktop\\1.jpg");try {send(html,subject,text,pathnames,toBody,fromBody);} catch (MessagingException e) {e.printStackTrace();} }@Test void contextLoads2() throws MessagingException {//一個復雜的郵件MimeMessage mimeMessage = mailSender.createMimeMessage();//組裝MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,true);helper.setSubject("雨寶寶你好呀");helper.setText("<p style='color:red'>詩雨你好呀</p>",true);//附件helper.addAttachment("1.jpg",new File("C:\\Users\\86177\\Desktop\\1.jpg"));helper.addAttachment("2.jpg",new File("C:\\Users\\86177\\Desktop\\1.jpg"));helper.setTo("1537752955@qq.com");helper.setFrom("821057799@qq.com");mailSender.send(mimeMessage); }/**** @param html 是否開啟html編碼* @param subject 主題* @param text 文本* @param pathnames 路徑名* @param toBody 發送給誰* @param fromBody 來自誰* @throws MessagingException*/ public void send(Boolean html,String subject,String text,ArrayList<String> pathnames,String toBody,String fromBody) throws MessagingException {//一個復雜的郵件MimeMessage mimeMessage = mailSender.createMimeMessage();//組裝MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,true);helper.setSubject(subject);helper.setText(text,html);String uuid = UUID.randomUUID().toString() + "";// substring() 方法用于提取字符串中介于兩個指定下標之間的字符。String filename;for (String pathname : pathnames) {//附件filename = uuid + pathname.substring(pathname.lastIndexOf('\\')+1);helper.addAttachment(filename,new File(pathname));}helper.setTo(toBody);helper.setFrom(fromBody);mailSender.send(mimeMessage); }定時任務
核心
TaskScheduler 任務調度者 TaskExecutor 人去執行者//核心啟動類上啟動 開啟定時功能的注解 @EnableScheduling@Scheduled //什么時候執行Cron表達式 //較難測試
@Service public class ScheduledService {//在一個特定的時間執行這個方法~ Timer//cron 表達式/*30 15 10 * * ? 每天的10點15分30秒執行一次30 0/5 10,18 * * ? 每天10點和18點 每隔五分鐘執行一次*///秒 分 時 日 月 周幾@Scheduled(cron = "30 15 10 * * ?")public void hello(){System.out.println("hello,你被執行了!");} }分布式Dubbo + Zookeeper + SpringBoot
什么是分布式系統
在《分布式系統原理與范型》一書中有如下定義:“分布式系統是若干獨立計算機的集合,這些計算機對于用戶來說就像單個相關系統”;
分布式系統是由一組通過網絡進行通信、為了完成共同的任務而協調工作的計算機節點組成的系統。分布式系統的出現是為了用廉價的、普通的機器完成單個計算機無法完成的計算、存儲任務。其目的是利用更多的機器,處理更多的數據。
分布式系統(distributed system)是建立在網絡之上的軟件系統。
首先需要明確的是,只有當單個節點的處理能力無法滿足日益增長的計算、存儲任務的時候,且硬件的提升(加內存、加磁盤、使用更好的CPU)高昂到得不償失的時候,應用程序也不能進一步優化的時候,我們才需要考慮分布式系統。因為,分布式系統要解決的問題本身就是和單機系統一樣的,而由于分布式系統多節點、通過網絡通信的拓撲結構,會引入很多單機系統沒有的問題,為了解決這些問題又會引入更多的機制、協議,帶來更多的問題。。。
RPC
RPC【Remote Procedure Call】是指遠程過程調用,是一種進程間通信方式,他是一種技術的思想,而不是規范。它允許程序調用另一個地址空間(通常是共享網絡的另一臺機器上)的過程或函數,而不用程序員顯式編碼這個遠程調用的細節。即程序員無論是調用本地的還是遠程的函數,本質上編寫的調用代碼基本相同。
也就是說兩臺服務器A,B,一個應用部署在A服務器上,想要調用B服務器上應用提供的函數/方法,由于不在一個內存空間,不能直接調用,需要通過網絡來表達調用的語義和傳達調用的數據。為什么要用RPC呢?就是無法在一個進程內,甚至一個計算機內通過本地調用的方式完成的需求,比如不同的系統間的通訊,甚至不同的組織間的通訊,由于計算能力需要橫向擴展,需要在多臺機器組成的集群上部署應用。RPC就是要像調用本地的函數一樣去調遠程函數;
推薦閱讀文章:https://www.jianshu.com/p/2accc2840a1b
Dubbo
什么是dubbo?
Apache Dubbo |?d?b??| 是一款高性能、輕量級的開源Java RPC框架,它提供了三大核心能力:面向接口的遠程方法調用,智能容錯和負載均衡,以及服務自動注冊和發現。
dubbo官網
1.了解Dubbo的特性
2.查看官方文檔
服務提供者(Provider):暴露服務的服務提供方,服務提供者在啟動時,向注冊中心注冊自己提供的服務。
服務消費者(Consumer):調用遠程服務的服務消費方,服務消費者在啟動時,向注冊中心訂閱自己所需的服務,服務消費者,從提供者地址列表中,基于軟負載均衡算法,選一臺提供者進行調用,如果調用失敗,再選另一臺調用。
注冊中心(Registry):注冊中心返回服務提供者地址列表給消費者,如果有變更,注冊中心將基于長連接推送變更數據給消費者
監控中心(Monitor):服務消費者和提供者,在內存中累計調用次數和調用時間,定時每分鐘發送一次統計數據到監控中心
調用關系說明
Zookeeper
下載地址:http://archive.apache.org/dist/zookeeper/zookeeper-3.4.14/
最新版下載地址:http://mirror.bit.edu.cn/apache/zookeeper/stable/
3.5以后的版本要下載帶有bin標識的zookeeper
運行/bin/zkServer.cmd ,初次運行會報錯,沒有zoo.cfg配置文件;
可能遇到問題:閃退 !
解決方案:編輯zkServer.cmd文件末尾添加pause 。這樣運行出錯就不會退出,會提示錯誤信息,方便找到原因。
修改zoo.cfg配置文件
將conf文件夾下面的zoo_sample.cfg復制一份改名為zoo.cfg即可。
注意幾個重要位置:
dataDir=./ 臨時數據存儲的目錄(可寫相對路徑)
clientPort=2181 zookeeper的端口號
修改完成后再次啟動zookeeper
使用zkCli.cmd測試
ls /:列出zookeeper根下保存的所有節點
[zk: 127.0.0.1:2181(CONNECTED) 4] ls / [zookeeper]create –e /kuangshen 123:創建一個kuangshen節點,值為123
get /kuangshen:獲取/kuangshen節點的值
我們再來查看一下節點
下載dubbo-admin
dubbo本身并不是一個服務軟件。它其實就是一個jar包,能夠幫你的java程序連接到zookeeper,并利用zookeeper消費、提供服務。
但是為了讓用戶更好的管理監控眾多的dubbo服務,官方提供了一個可視化的監控程序dubbo-admin,不過這個監控即使不裝也不影響使用。
我們這里來安裝一下:
1、下載dubbo-admin
地址 :https://github.com/apache/dubbo-admin/tree/master
2、解壓進入目錄
修改 dubbo-admin\src\main\resources \application.properties 指定zookeeper地址
server.port=7001 spring.velocity.cache=false spring.velocity.charset=UTF-8 spring.velocity.layout-url=/templates/default.vm spring.messages.fallback-to-system-locale=false spring.messages.basename=i18n/message spring.root.password=root spring.guest.password=guestdubbo.registry.address=zookeeper://127.0.0.1:21813、在項目目錄下打包dubbo-admin
mvn clean package -Dmaven.test.skip=true第一次打包的過程有點慢,需要耐心等待!直到成功!
4、執行 dubbo-admin\target 下的dubbo-admin-0.0.1-SNAPSHOT.jar
java -jar dubbo-admin-0.0.1-SNAPSHOT.jar【注意:zookeeper的服務一定要打開!】
執行完畢,我們去訪問一下 http://localhost:7001/ , 這時候我們需要輸入登錄賬戶和密碼,我們都是默認的root-root;
登錄成功后,查看界面
總結
zookeeper: 注冊中心 必須要
dubbo-admin:是一個監控管理后臺~查看我們注冊了那些服務,那些服務被消費了
Dubbo:jar包
測試
服務提供者
1、將服務提供者注冊到注冊中心,我們需要整合Dubbo和zookeeper,所以需要導包
我們從dubbo官網進入github,看下方的幫助文檔,找到dubbo-springboot,找到依賴包****
<!-- Dubbo Spring Boot Starter --> <dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-spring-boot-starter</artifactId><version>2.7.3</version> </dependency>zookeeper的包我們去maven倉庫下載,zkclient;
<!-- https://mvnrepository.com/artifact/com.github.sgroschupf/zkclient --> <dependency><groupId>com.github.sgroschupf</groupId><artifactId>zkclient</artifactId><version>0.1</version> </dependency>【新版的坑】zookeeper及其依賴包,解決日志沖突,還需要剔除日志依賴;
<!-- 引入zookeeper --> <dependency><groupId>org.apache.curator</groupId><artifactId>curator-framework</artifactId><version>2.12.0</version> </dependency> <dependency><groupId>org.apache.curator</groupId><artifactId>curator-recipes</artifactId><version>2.12.0</version> </dependency> <dependency><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId><version>3.4.14</version><!--排除這個slf4j-log4j12--><exclusions><exclusion><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId></exclusion></exclusions> </dependency>2、在springboot配置文件中配置dubbo相關屬性!
#當前應用名字 dubbo.application.name=provider-server #注冊中心地址 dubbo.registry.address=zookeeper://172.30.72.23:2181 #掃描指定包下服務 dubbo.scan.base-packages=com.kuang.provider.service3、在service的實現類中配置服務注解,發布服務!注意導包問題
//Zookeeper:服務注冊與發現@Service //dubbo.config.annotation 注意導的包 要是dubbo的 // 在項目一啟動就自動注冊到Zookeeper的注冊中心 @Component // 使用了dubbo后盡量使用這個 不使用上面的 // 因為和spring的注解一樣 容易導錯包 public class TicketServiceImpl implements TicketService{@Overridepublic String getTicket() {return "《狂神說JAVA》";} }啟動Zookeeper 同時也可以啟動監控界面 duubo-admin 界面有緩存 刷新較慢 但是控制臺實時刷新 注意不要關掉控制臺
服務消費者
1、導入依賴,和之前的依賴一樣;
<!--dubbo--> <!-- Dubbo Spring Boot Starter --> <dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-spring-boot-starter</artifactId><version>2.7.3</version> </dependency> <!--zookeeper--> <!-- https://mvnrepository.com/artifact/com.github.sgroschupf/zkclient --> <dependency><groupId>com.github.sgroschupf</groupId><artifactId>zkclient</artifactId><version>0.1</version> </dependency> <!-- 引入zookeeper --> <dependency><groupId>org.apache.curator</groupId><artifactId>curator-framework</artifactId><version>2.12.0</version> </dependency> <dependency><groupId>org.apache.curator</groupId><artifactId>curator-recipes</artifactId><version>2.12.0</version> </dependency> <dependency><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId><version>3.4.14</version><!--排除這個slf4j-log4j12--><exclusions><exclusion><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId></exclusion></exclusions> </dependency>2、配置參數
#當前應用名字 dubbo.application.name=consumer-server #注冊中心地址 dubbo.registry.address=zookeeper://172.30.72.23:21813、本來正常步驟是需要將服務提供者的接口打包,然后用pom文件導入,我們這里使用簡單的方式,直接將服務的接口拿過來,路徑必須保證正確,即和服務提供者相同;
聊聊
分布式架構會遇到的四個核心問題?
1.這么多服務,客戶端該如何去訪問?
2.這么多服務,服務之間如何進行通信?
3.這么多服務,如何治理呢?
4.服務掛了,怎么辦?
解決方案:
springcloud,是一套生態,就是來解決以上分布式架構的4個問題
想使用springcloud,必須要掌握springBoot,因為springcloud是基于springBoot;
Spring cloud NetFlix,出來了一套解決方案,一站式解決方案,用可以直接拿。
? Api網關,zuul組件
? Feign–>HttpClient–>HTTP的通信方式,同步并阻塞
? 服務注冊與發現,Eureka
? 熔斷機制,Hystrix
? 2018年年底,NetFlix宣布無限期停止維護。生態不再維護,就會脫節。
Apache Dubbo Zookeeper,第二套解決方案
? API: 沒有! 要么找第三方組件,要么自己實現
? Dubbo是一個高性能的基于Java實現的RPC通訊框架!
? 服務注冊與發現,Zookeeper:動物園管理者(Hadoop,Hive)
? 熔斷機制:沒有,借助了Hystrix。
?
? 所以 不完善,Dubbo3.0
SpringCloud Alibaba 一站式解決方案
目前,又提出了一種方案:
? 服務網格:下一代微服務標準,Server Mesh
? 代表解決方案:istio(未來)
萬變不離其宗,一通百通!
為什么要解決這些問題,網絡不可靠。加載器
總結
以上是生活随笔為你收集整理的SpringBoot学习笔记~狂神的全部內容,希望文章能夠幫你解決所遇到的問題。