狂神說SpringBoot

轉自狂神老師,很是感謝老師,僅做爲我的筆記使用javascript

一、SpringBoot: Hello,Worldphp

二、SpringBoot: 運行原理初探css

三、SpringBoot: yaml配置注入html

四、SpringBoot: JSR303數據校驗及多環境切換前端

五、SpringBoot:自動配置原理html5

六、SpringBoot: 自定義starterjava

七、SpringBoot整合JDBCmysql

八、SpringBoot整合Druidjquery

九、SpringBoot 整合mybatisgit

十、SpringBoot: Web開發靜態資源處理

十一、SpringBoot: Thymeleaf模板引擎

十二、SpringBoot: MVC自動配置原理

1三、SpringBoot: 頁面國際化

1四、SpringBoot: 集成Swagger終極版

1五、SpringBoot: 異步、定時、郵件任務

1六、SpringBoot: 富文本編輯器

1七、SpringBoot: Dubbo和Zookeeper集成

1八、SpringBoot: 集成SpringSecurity

一、SpringBoot:Hello,World!

SpringBoot簡介

1.一、回顧什麼是Spring

Spring是一個開源框架,2003 年興起的一個輕量級的Java 開發框架,做者:Rod Johnson 。

Spring是爲了解決企業級應用開發的複雜性而建立的,簡化開發。

**
**

1.二、Spring是如何簡化Java開發的

爲了下降Java開發的複雜性,Spring採用瞭如下4種關鍵策略:

一、基於POJO的輕量級和最小侵入性編程,全部東西都是bean;

二、經過IOC,依賴注入(DI)和麪向接口實現鬆耦合;

三、基於切面(AOP)和慣例進行聲明式編程;

四、經過切面和模版減小樣式代碼,RedisTemplate,xxxTemplate;

1.三、什麼是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配置的要求

真的很爽,咱們快速去體驗開發個接口的感受吧!

Hello,World

1.四、準備工做

咱們將學習如何快速的建立一個Spring Boot應用,而且實現一個簡單的Http請求處理。經過這個例子對Spring Boot有一個初步的瞭解,並體驗其結構簡單、開發快速的特性。

個人環境準備:

  • java version "1.8.0_181"
  • Maven-3.6.1
  • SpringBoot 2.x 最新版

開發工具:

  • IDEA

1.五、建立基礎項目說明

Spring官方提供了很是方便的工具讓咱們快速構建應用

Spring Initializr:https://start.spring.io/

項目建立方式一:使用Spring Initializr 的 Web頁面建立項目

一、打開 https://start.spring.io/

二、填寫項目信息

三、點擊」Generate Project「按鈕生成項目;下載此項目

四、解壓項目包,並用IDEA以Maven項目導入,一路下一步便可,直到項目導入完畢。

五、若是是第一次使用,可能速度會比較慢,包比較多、須要耐心等待一切就緒。

項目建立方式二:使用 IDEA 直接建立項目

一、建立一個新項目

二、選擇spring initalizr , 能夠看到默認就是去官網的快速構建工具那裏實現

三、填寫項目信息

四、選擇初始化的組件(初學勾選 Web 便可)

五、填寫項目路徑

六、等待項目構建成功

項目結構分析:

經過上面步驟完成了基礎項目的建立。就會自動生成如下文件。

一、程序的主啓動類

二、一個 application.properties 配置文件

三、一個 測試類

四、一個 pom.xml

1.六、pom.xml 分析

打開pom.xml,看看Spring Boot項目的依賴:

<!-- 父依賴 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.5.RELEASE</version> <relativePath/> </parent> <dependencies> <!-- web場景啓動器 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- springboot單元測試 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <!-- 剔除依賴 --> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <build> <plugins> <!-- 打包插件 --> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> 

1.七、編寫一個http接口

一、在主程序的同級目錄下,新建一個controller包,必定要在同級目錄下,不然識別不到

二、在包中新建一個HelloController類

@RestController public class HelloController { @RequestMapping("/hello") public String hello() { return "Hello World"; } } 

三、編寫完畢後,從主程序啓動項目,瀏覽器發起請求,看頁面返回;控制檯輸出了 Tomcat 訪問的端口號!

簡單幾步,就完成了一個web接口的開發,SpringBoot就是這麼簡單。因此咱們經常使用它來創建咱們的微服務項目!

1.八、將項目打成jar包,點擊 maven的 package

若是遇到以上錯誤,能夠配置打包時 跳過項目運行測試用例

<!-- 在工做中,不少狀況下咱們打包是不想執行測試用例的 多是測試用例不完事,或是測試用例會影響數據庫數據 跳過測試用例執 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> <!--跳過項目運行測試用例--> <skipTests>true</skipTests> </configuration> </plugin> 

若是打包成功,則會在target目錄下生成一個 jar 包

打成了jar包後,就能夠在任何地方運行了!OK

彩蛋

如何更改啓動時顯示的字符拼成的字母,SpringBoot呢?也就是 banner 圖案;

只需一步:到項目下的 resources 目錄下新建一個banner.txt 便可。

圖案能夠到:https://www.bootschool.net/ascii 這個網站生成,而後拷貝到文件中便可!

**SpringBoot這麼簡單的東西背後必定有故事,咱們以後

二、SpringBoot:運行原理初探

咱們以前寫的HelloSpringBoot,究竟是怎麼運行的呢,Maven項目,咱們通常從pom.xml文件探究起;

pom.xml

2.一、父依賴

其中它主要是依賴一個父項目,主要是管理項目的資源過濾及插件!

<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的版本控制中心;

之後咱們導入依賴默認是不須要寫版本;可是若是導入的包沒有在依賴中管理着就須要手動配置版本了;

2.二、啓動器 spring-boot-starter

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> 

springboot-boot-starter-xxx:就是spring-boot的場景啓動器

spring-boot-starter-web:幫咱們導入了web模塊正常運行所依賴的組件;

SpringBoot將全部的功能場景都抽取出來,作成一個個的starter (啓動器),只須要在項目中引入這些starter便可,全部相關的依賴都會導入進來 , 咱們要用什麼功能就導入什麼樣的場景啓動器便可 ;咱們將來也能夠本身自定義 starter;

主啓動類

分析完了 pom.xml 來看看這個啓動類

2.三、默認的主啓動類

//@SpringBootApplication 來標註一個主程序類 //說明這是一個Spring Boot應用 @SpringBootApplication public class SpringbootApplication { public static void main(String[] args) { //覺得是啓動了一個方法,沒想到啓動了一個服務 SpringApplication.run(SpringbootApplication.class, args); } } 

可是一個簡單的啓動類並不簡單!咱們來分析一下這些註解都幹了什麼

2.四、@SpringBootApplication

做用:標註在某個類上說明這個類是SpringBoot的主配置類 , SpringBoot就應該運行這個類的main方法來啓動SpringBoot應用;

進入這個註解:能夠看到上面還有不少其餘註解!

@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan( excludeFilters = {@Filter( type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class} ), @Filter( type = FilterType.CUSTOM, classes = {AutoConfigurationExcludeFilter.class} )} ) public @interface SpringBootApplication { // ...... } 

2.五、@ComponentScan

這個註解在Spring中很重要 ,它對應XML配置中的元素。

做用:自動掃描並加載符合條件的組件或者bean , 將這個bean定義加載到IOC容器中

2.六、@SpringBootConfiguration

做用:SpringBoot的配置類 ,標註在某個類上 , 表示這是一個SpringBoot的配置類;

咱們繼續進去這個註解查看

// 點進去獲得下面的 @Component @Configuration public @interface SpringBootConfiguration {} @Component public @interface Configuration {} 

這裏的 @Configuration,說明這是一個配置類 ,配置類就是對應Spring的xml 配置文件;

裏面的 @Component 這就說明,啓動類自己也是Spring中的一個組件而已,負責啓動應用!

咱們回到 SpringBootApplication 註解中繼續看。

2.七、@EnableAutoConfiguration

@EnableAutoConfiguration :開啓自動配置功能

之前咱們須要本身配置的東西,而如今SpringBoot能夠自動幫咱們配置 ;@EnableAutoConfiguration告訴SpringBoot開啓自動配置功能,這樣自動配置才能生效;

點進註解接續查看:

@AutoConfigurationPackage :自動配置包

@Import({Registrar.class}) public @interface AutoConfigurationPackage { } 

@import :Spring底層註解@import , 給容器中導入一個組件

Registrar.class 做用:將主啓動類的所在包及包下面全部子包裏面的全部組件掃描到Spring容器 ;

這個分析完了,退到上一步,繼續看

@Import({AutoConfigurationImportSelector.class}) :給容器導入組件 ;

AutoConfigurationImportSelector :自動配置導入選擇器,那麼它會導入哪些組件的選擇器呢?咱們點擊去這個類看源碼:

一、這個類中有一個這樣的方法

// 得到候選的配置 protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { //這裏的getSpringFactoriesLoaderFactoryClass()方法 //返回的就是咱們最開始看的啓動自動導入配置文件的註解類;EnableAutoConfiguration List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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; } 

二、這個方法又調用了 SpringFactoriesLoader 類的靜態方法!咱們進入SpringFactoriesLoader類loadFactoryNames() 方法

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); //這裏它又調用了 loadSpringFactories 方法 return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); } 

三、咱們繼續點擊查看 loadSpringFactories 方法

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { //得到classLoader , 咱們返回能夠看到這裏獲得的就是EnableAutoConfiguration標註的類自己 MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader); if (result != null) { return result; } else { try { //去獲取一個資源 "META-INF/spring.factories" Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories"); LinkedMultiValueMap result = new LinkedMultiValueMap(); //將讀取到的資源遍歷,封裝成爲一個Properties while(urls.hasMoreElements()) { URL url = (URL)urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); Iterator var6 = properties.entrySet().iterator(); while(var6.hasNext()) { Entry<?, ?> entry = (Entry)var6.next(); String factoryClassName = ((String)entry.getKey()).trim(); String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue()); int var10 = var9.length; for(int var11 = 0; var11 < var10; ++var11) { String factoryName = var9[var11]; result.add(factoryClassName, factoryName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException var13) { throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13); } } } 

四、發現一個屢次出現的文件:spring.factories,全局搜索它

2.八、spring.factories

咱們根據源頭打開spring.factories , 看到了不少自動配置的文件;這就是自動配置根源所在!

WebMvcAutoConfiguration

咱們在上面的自動配置類隨便找一個打開看看,好比 :WebMvcAutoConfiguration

能夠看到這些一個個的都是JavaConfig配置類,並且都注入了一些Bean,能夠找一些本身認識的類,看着熟悉一下!

因此,自動配置真正實現是從classpath中搜尋全部的META-INF/spring.factories配置文件 ,並將其中對應的 org.springframework.boot.autoconfigure. 包下的配置項,經過反射實例化爲對應標註了 @Configuration的JavaConfig形式的IOC容器配置類 , 而後將這些都彙總成爲一個實例並加載到IOC容器中。

結論:

  1. SpringBoot在啓動的時候從類路徑下的META-INF/spring.factories中獲取EnableAutoConfiguration指定的值
  2. 將這些值做爲自動配置類導入容器 , 自動配置類就生效 , 幫咱們進行自動配置工做;
  3. 整個J2EE的總體解決方案和自動配置都在springboot-autoconfigure的jar包中;
  4. 它會給容器中導入很是多的自動配置類 (xxxAutoConfiguration), 就是給容器中導入這個場景須要的全部組件 , 並配置好這些組件 ;
  5. 有了自動配置類 , 免去了咱們手動編寫配置注入功能組件等的工做;

如今你們應該大概的瞭解了下,SpringBoot的運行原理,後面咱們還會深化一次!

SpringApplication

2.九、不簡單的方法

我最初覺得就是運行了一個main方法,沒想到卻開啓了一個服務;

@SpringBootApplication public class SpringbootApplication { public static void main(String[] args) { SpringApplication.run(SpringbootApplication.class, args); } } 

SpringApplication.run分析

分析該方法主要分兩部分,一部分是SpringApplication的實例化,二是run方法的執行;

2.十、SpringApplication

這個類主要作了如下四件事情:

一、推斷應用的類型是普通的項目仍是Web項目

二、查找並加載全部可用初始化器 , 設置到initializers屬性中

三、找出全部的應用程序監聽器,設置到listeners屬性中

四、推斷並設置main方法的定義類,找到運行的主類

查看構造器:

public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) { // ...... this.webApplicationType = WebApplicationType.deduceFromClasspath(); this.setInitializers(this.getSpringFactoriesInstances(); this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = this.deduceMainApplicationClass(); } 

2.十一、run方法流程分析


跟着源碼和這幅圖就能夠一探究竟了!

三、SpringBoot:yaml配置注入

yaml語法學習

3.一、配置文件

SpringBoot使用一個全局的配置文件 , 配置文件名稱是固定的

  • application.properties

    • 語法結構 :key=value
  • application.yml

    • 語法結構 :key:空格 value

配置文件的做用 :修改SpringBoot自動配置的默認值,由於SpringBoot在底層都給咱們自動配置好了;

好比咱們能夠在配置文件中修改Tomcat 默認啓動的端口號!測試一下!

server.port=8081 

3.二、yaml概述

YAML是 "YAML Ain't a Markup Language" (YAML不是一種標記語言)的遞歸縮寫。在開發的這種語言時,YAML 的意思實際上是:"Yet Another Markup Language"(還是一種標記語言)

這種語言以數據爲中心,而不是以標記語言爲重點!

之前的配置文件,大多數都是使用xml來配置;好比一個簡單的端口配置,咱們來對比下yaml和xml

傳統xml配置:

<server> <port>8081<port> </server> 

yaml配置:

server: prot: 8080 

3.三、yaml基礎語法

說明:語法要求嚴格!

一、空格不能省略

二、以縮進來控制層級關係,只要是左邊對齊的一列數據都是同一個層級的。

三、屬性和值的大小寫都是十分敏感的。

字面量:普通的值 [ 數字,布爾值,字符串 ]

字面量直接寫在後面就能夠 , 字符串默認不用加上雙引號或者單引號;

k: v 

注意:

  • 「 」 雙引號,不會轉義字符串裏面的特殊字符 , 特殊字符會做爲自己想表示的意思;

    好比 :name: "kuang \n shen" 輸出 :kuang 換行 shen

  • '' 單引號,會轉義特殊字符 , 特殊字符最終會變成和普通字符同樣輸出

    好比 :name: ‘kuang \n shen’ 輸出 :kuang \n shen

對象、Map(鍵值對)

#對象、Map格式 k: v1: v2: 

在下一行來寫對象的屬性和值得關係,注意縮進;好比:

student: name: qinjiang age: 3 

行內寫法

student: {name: qinjiang,age: 3} 

數組( List、set )

用 - 值表示數組中的一個元素,好比:

pets: - cat - dog - pig 

行內寫法

pets: [cat,dog,pig] 

修改SpringBoot的默認端口號

配置文件中添加,端口號的參數,就能夠切換端口;

server: port: 8082 

注入配置文件

yaml文件更強大的地方在於,他能夠給咱們的實體類直接注入匹配值!

3.四、yaml注入配置文件

一、在springboot項目中的resources目錄下新建一個文件 application.yml

二、編寫一個實體類 Dog;

package com.kuang.springboot.pojo; @Component //註冊bean到容器中 public class Dog { private String name; private Integer age; //有參無參構造、get、set方法、toString()方法 } 

三、思考,咱們原來是如何給bean注入屬性值的!@Value,給狗狗類測試一下:

@Component //註冊bean public class Dog { @Value("阿黃") private String name; @Value("18") private Integer age; } 

四、在SpringBoot的測試類下注入狗狗輸出一下;

@SpringBootTest class DemoApplicationTests { @Autowired //將狗狗自動注入進來 Dog dog; @Test public void contextLoads() { System.out.println(dog); //打印看下狗狗對象 } } 

結果成功輸出,@Value注入成功,這是咱們原來的辦法對吧。

五、咱們在編寫一個複雜一點的實體類:Person 類

@Component //註冊bean到容器中 public class Person { private String name; private Integer age; private Boolean happy; private Date birth; private Map<String,Object> maps; private List<Object> lists; private Dog dog; //有參無參構造、get、set方法、toString()方法 } 

六、咱們來使用yaml配置的方式進行注入,你們寫的時候注意區別和優點,咱們編寫一個yaml配置!

person: name: qinjiang age: 3 happy: false birth: 2000/01/01 maps: {k1: v1,k2: v2} lists: - code - girl - music dog: name: 旺財 age: 1 

七、咱們剛纔已經把person這個對象的全部值都寫好了,咱們如今來注入到咱們的類中!

/* @ConfigurationProperties做用: 將配置文件中配置的每個屬性的值,映射到這個組件中; 告訴SpringBoot將本類中的全部屬性和配置文件中相關的配置進行綁定 參數 prefix = 「person」 : 將配置文件中的person下面的全部屬性一一對應 */ @Component //註冊bean @ConfigurationProperties(prefix = "person") public class Person { private String name; private Integer age; private Boolean happy; private Date birth; private Map<String,Object> maps; private List<Object> lists; private Dog dog; } 

八、IDEA 提示,springboot配置註解處理器沒有找到,讓咱們看文檔,咱們能夠查看文檔,找到一個依賴!

<!-- 導入配置文件處理器,配置文件進行綁定就會有提示,須要重啓 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> 

九、確認以上配置都OK以後,咱們去測試類中測試一下:

@SpringBootTest class DemoApplicationTests { @Autowired Person person; //將person自動注入進來 @Test public void contextLoads() { System.out.println(person); //打印person信息 } } 

結果:全部值所有注入成功!

yaml配置注入到實體類徹底OK!

課堂測試:

一、將配置文件的key 值 和 屬性的值設置爲不同,則結果輸出爲null,注入失敗

二、在配置一個person2,而後將 @ConfigurationProperties(prefix = "person2") 指向咱們的person2;

3.五、加載指定的配置文件

@PropertySource :加載指定的配置文件;

@configurationProperties:默認從全局配置文件中獲取值;

一、咱們去在resources目錄下新建一個person.properties文件

name=kuangshen 

二、而後在咱們的代碼中指定加載person.properties文件

@PropertySource(value = "classpath:person.properties") @Component //註冊bean public class Person { @Value("${name}") private String name; ...... } 

三、再次輸出測試一下:指定配置文件綁定成功!

3.六、配置文件佔位符

配置文件還能夠編寫佔位符生成隨機數

person: name: qinjiang${random.uuid} # 隨機uuid age: ${random.int} # 隨機int happy: false birth: 2000/01/01 maps: {k1: v1,k2: v2} lists: - code - girl - music dog: name: ${person.hello:other}_旺財 age: 1 

3.七、回顧properties配置

咱們上面採用的yaml方法都是最簡單的方式,開發中最經常使用的;也是springboot所推薦的!那咱們來嘮嘮其餘的實現方式,道理都是相同的;寫仍是那樣寫;配置文件除了yml還有咱們以前經常使用的properties , 咱們沒有講,咱們來嘮嘮!

【注意】properties配置文件在寫中文的時候,會有亂碼 , 咱們須要去IDEA中設置編碼格式爲UTF-8;

settings-->FileEncodings 中配置;

測試步驟:

一、新建一個實體類User

@Component //註冊bean public class User { private String name; private int age; private String sex; } 

二、編輯配置文件 user.properties

user1.name=kuangshen user1.age=18user1.sex=男 

三、咱們在User類上使用@Value來進行注入!

@Component //註冊bean @PropertySource(value = "classpath:user.properties") public class User { //直接使用@value @Value("${user.name}") //從配置文件中取值 private String name; @Value("#{9*2}") // #{SPEL} Spring表達式 private int age; @Value("男") // 字面量 private String sex; } 

四、Springboot測試

user1.name=kuangshen user1.age=18 user1.sex=男 

結果正常輸出:

3.八、對比小結

@Value這個使用起來並不友好!咱們須要爲每一個屬性單獨註解賦值,比較麻煩;咱們來看個功能對比圖

一、@ConfigurationProperties只須要寫一次便可 , @Value則須要每一個字段都添加

二、鬆散綁定:這個什麼意思呢? 好比個人yml中寫的last-name,這個和lastName是同樣的, - 後面跟着的字母默認是大寫的。這就是鬆散綁定。能夠測試一下

三、JSR303數據校驗 , 這個就是咱們能夠在字段是增長一層過濾器驗證 , 能夠保證數據的合法性

四、複雜類型封裝,yml中能夠封裝對象 , 使用value就不支持

結論:

配置yml和配置properties均可以獲取到值 , 強烈推薦 yml;

若是咱們在某個業務中,只須要獲取配置文件中的某個值,能夠使用一下 @value;

若是說,咱們專門編寫了一個JavaBean來和配置文件進行一一映射,就直接@configurationProperties,不要猶豫!

四、SpringBoot:JSR303數據校驗及多環境切換

JSR303數據校驗

4.一、先看看如何使用

Springboot中能夠用@validated來校驗數據,若是數據異常則會統一拋出異常,方便異常中心統一處理。咱們這裏來寫個註解讓咱們的name只能支持Email格式;

@Component //註冊bean @ConfigurationProperties(prefix = "person") @Validated //數據校驗 public class Person { @Email(message="郵箱格式錯誤") //name必須是郵箱格式 private String name; } 

運行結果 :default message [不是一個合法的電子郵件地址];

使用數據校驗,能夠保證數據的正確性;

4.二、常見參數

@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 對象是否符合正則表達式的規則 .......等等 除此之外,咱們還能夠自定義一些數據校驗規則 

多環境切換

profile是Spring對不一樣環境提供不一樣配置功能的支持,能夠經過激活不一樣的環境版本,實現快速切換環境;

4.三、多配置文件

咱們在主配置文件編寫的時候,文件名能夠是 application-{profile}.properties/yml , 用來指定多個環境版本;

例如:

application-test.properties 表明測試環境配置

application-dev.properties 表明開發環境配置

可是Springboot並不會直接啓動這些配置文件,它默認使用application.properties主配置文件

咱們須要經過一個配置來選擇須要激活的環境:

#好比在配置文件中指定使用dev環境,咱們能夠經過設置不一樣的端口號進行測試; #咱們啓動SpringBoot,就能夠看到已經切換到dev下的配置了; spring.profiles.active=dev 

4.四、yaml的多文檔塊

和properties配置文件中同樣,可是使用yml去實現不須要建立多個配置文件,更加方便了 !

server: port: 8081 #選擇要激活那個環境塊 spring: profiles: active: prod --- server: port: 8083 spring: profiles: dev #配置環境的名稱 --- server: port: 8084 spring: profiles: prod #配置環境的名稱 

注意:若是yml和properties同時都配置了端口,而且沒有激活其餘環境 , 默認會使用properties配置文件的!

4.五、配置文件加載位置

外部加載配置文件的方式十分多,咱們選擇最經常使用的便可,在開發的資源文件中進行配置!

官方外部配置文件說明參考文檔

springboot 啓動會掃描如下位置的application.properties或者application.yml文件做爲Spring boot的默認配置文件:

優先級1:項目路徑下的config文件夾配置文件 優先級2:項目路徑下配置文件 優先級3:資源路徑下的config文件夾配置文件 優先級4:資源路徑下配置文件 

優先級由高到底,高優先級的配置會覆蓋低優先級的配置;

SpringBoot會從這四個位置所有加載主配置文件;互補配置;

咱們在最低級的配置文件中設置一個項目訪問路徑的配置來測試互補問題;

#配置項目的訪問路徑 server.servlet.context-path=/kuang 

4.六、拓展,運維小技巧

指定位置加載配置文件

咱們還能夠經過spring.config.location來改變默認的配置文件位置

項目打包好之後,咱們能夠使用命令行參數的形式,啓動項目的時候來指定配置文件的新位置;這種狀況,通常是後期運維作的多,相同配置,外部指定的配置文件優先級最高

五、SpringBoot:自動配置原理

自動配置原理

配置文件到底能寫什麼?怎麼寫?

SpringBoot官方文檔中有大量的配置,咱們沒法所有記住

5.一、分析自動配置原理

咱們以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類中封裝着;
  • 配置文件能配置什麼就能夠參照某個功能對應的這個屬性類
//從配置文件中獲取指定的值和bean的屬性進行綁定 @ConfigurationProperties(prefix = "spring.http") public class HttpProperties { // ..... } 

咱們去配置文件裏面試試前綴,看提示!

這就是自動裝配的原理!

5.二、精髓

一、SpringBoot啓動會加載大量的自動配置類

二、咱們看咱們須要的功能有沒有在SpringBoot默認寫好的自動配置類當中;

三、咱們再來看這個自動配置類中到底配置了哪些組件;(只要咱們要用的組件存在在其中,咱們就不須要再手動配置了)

四、給容器中自動配置類添加組件的時候,會從properties類中獲取某些屬性。咱們只須要在配置文件中指定這些屬性的值便可;

xxxxAutoConfigurartion:自動配置類;給容器中添加組件

xxxxProperties:封裝配置文件中相關屬性;

5.三、瞭解:@Conditional

瞭解完自動裝配的原理後,咱們來關注一個細節問題,自動配置類必須在必定的條件下才能生效;

@Conditional派生註解(Spring註解版原生的@Conditional做用)

做用:必須是@Conditional指定的條件成立,纔給容器中添加組件,配置配裏面的全部內容才生效;

那麼多的自動配置類,必須在必定的條件下才能生效;也就是說,咱們加載了這麼多的配置類,但不是全部的都生效了。

咱們怎麼知道哪些自動配置類生效?

咱們能夠經過啓用 debug=true屬性;來讓控制檯打印自動配置報告,這樣咱們就能夠很方便的知道哪些自動配置類生效;

#開啓springboot的調試類 debug=true 

Positive matches:(自動配置類啓用的:正匹配)

Negative matches:(沒有啓動,沒有匹配成功的自動配置類:負匹配)

Unconditional classes: (沒有條件的類)

【演示:查看輸出的日誌】

掌握吸取理解原理,便可以不變應萬變!

六、SpringBoot:自定義starter

咱們分析完畢了源碼以及自動裝配的過程,咱們能夠嘗試自定義一個啓動器來玩玩!

6.一、說明

啓動器模塊是一個 空 jar 文件,僅提供輔助性依賴管理,這些依賴可能用於自動裝配或者其餘類庫;

命名歸約:

官方命名:

  • 前綴:spring-boot-starter-xxx
  • 好比:spring-boot-starter-web....

自定義命名:

  • xxx-spring-boot-starter
  • 好比:mybatis-spring-boot-starter

6.二、編寫啓動器

一、在IDEA中新建一個空項目 spring-boot-starter-diy

二、新建一個普通Maven模塊:kuang-spring-boot-starter

三、新建一個Springboot模塊:kuang-spring-boot-starter-autoconfigure

四、點擊apply便可,基本結構

五、在咱們的 starter 中 導入 autoconfigure 的依賴!

<!-- 啓動器 --> <dependencies> <!-- 引入自動配置模塊 --> <dependency> <groupId>com.kuang</groupId> <artifactId>kuang-spring-boot-starter-autoconfigure</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> </dependencies> 

六、將 autoconfigure 項目下多餘的文件都刪掉,Pom中只留下一個 starter,這是全部的啓動器基本配置!

七、咱們編寫一個本身的服務

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(); } } 

八、編寫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; } } 

九、編寫咱們的自動配置類並注入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 { @Autowired HelloProperties helloProperties; @Bean public HelloService helloService(){ HelloService service = new HelloService(); service.setHelloProperties(helloProperties); return service; } } 

十、在resources編寫一個本身的 META-INF\spring.factories

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.kuang.HelloServiceAutoConfiguration

十一、編寫完成後,能夠安裝到maven倉庫中!

6.三、新建項目測試咱們本身寫的啓動器

一、新建一個SpringBoot 項目

二、導入咱們本身寫的啓動器

<dependency> <groupId>com.kuang</groupId> <artifactId>kuang-spring-boot-starter</artifactId> <version>1.0-SNAPSHOT</version> </dependency> 

三、編寫一個 HelloController 進行測試咱們本身的寫的接口!

package com.kuang.controller; @RestController public class HelloController { @Autowired HelloService helloService; @RequestMapping("/hello") public String hello(){ return helloService.sayHello("zxc"); } } 

四、編寫配置文件 application.properties

kuang.hello.prefix="ppp" kuang.hello.suffix="sss" 

五、啓動項目進行測試,結果成功 !

七、SpringBoot整合JDBC

7.一、SpringData簡介

對於數據訪問層,不管是 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

整合JDBC

7.二、建立測試項目測試數據源

一、我去新建一個項目測試:springboot-data-jdbc ; 引入相應的模塊!基礎模塊

二、項目建好以後,發現自動幫咱們導入了以下的啓動器

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> 

三、編寫yaml配置文件鏈接數據庫;

spring: datasource: username: root password: 123456 #?serverTimezone=UTC解決時區的報錯 url: jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8 driver-class-name: com.mysql.cj.jdbc.Driver 

四、配置完這一些東西后,咱們就能夠直接去使用了,由於SpringBoot已經默認幫咱們進行了自動配置;去測試類測試一下

@SpringBootTest class SpringbootDataJdbcApplicationTests { //DI注入數據源 @Autowired DataSource dataSource; @Test public void contextLoads() throws SQLException { //看一下默認數據源 System.out.println(dataSource.getClass()); //得到鏈接 Connection connection = dataSource.getConnection(); System.out.println(connection); //關閉鏈接 connection.close(); } } 

結果:咱們能夠看到他默認給咱們配置的數據源爲 : class com.zaxxer.hikari.HikariDataSource , 咱們並無手動配置

咱們來全局搜索一下,找到數據源的全部自動配置都在 :DataSourceAutoConfiguration文件:

@Import( {Hikari.class, Tomcat.class, Dbcp2.class, Generic.class, DataSourceJmxConfiguration.class} ) protected static class PooledDataSourceConfiguration { protected PooledDataSourceConfiguration() { } } 

這裏導入的類都在 DataSourceConfiguration 配置類下,能夠看出 Spring Boot 2.2.5 默認使用HikariDataSource 數據源,而之前版本,如 Spring Boot 1.5 默認使用 org.apache.tomcat.jdbc.pool.DataSource 做爲數據源;

HikariDataSource 號稱 Java WEB 當前速度最快的數據源,相比於傳統的 C3P0 、DBCP、Tomcat jdbc 等鏈接池更加優秀;

能夠使用 spring.datasource.type 指定自定義的數據源類型,值爲 要使用的鏈接池實現的徹底限定名。

關於數據源咱們並不作介紹,有了數據庫鏈接,顯然就能夠 CRUD 操做數據庫了。可是咱們須要先了解一個對象 JdbcTemplate

7.三、JDBCTemplate

一、有了數據源(com.zaxxer.hikari.HikariDataSource),而後能夠拿到數據庫鏈接(java.sql.Connection),有了鏈接,就能夠使用原生的 JDBC 語句來操做數據庫;

二、即便不使用第三方第數據庫操做框架,如 MyBatis等,Spring 自己也對原生的JDBC 作了輕量級的封裝,即JdbcTemplate。

三、數據庫操做的全部 CRUD 方法都在 JdbcTemplate 中。

四、Spring Boot 不只提供了默認的數據源,同時默認已經配置好了 JdbcTemplate 放在了容器中,程序員只需本身注入便可使用

五、JdbcTemplate 的自動配置是依賴 org.springframework.boot.autoconfigure.jdbc 包下的 JdbcTemplateConfiguration 類

JdbcTemplate主要提供如下幾類方法:

  • execute方法:能夠用於執行任何SQL語句,通常用於執行DDL語句;
  • update方法及batchUpdate方法:update方法用於執行新增、修改、刪除等語句;batchUpdate方法用於執行批處理相關語句;
  • query方法及queryForXXX方法:用於執行查詢相關語句;
  • call方法:用於執行存儲過程、函數相關語句。

7.四、測試

編寫一個Controller,注入 jdbcTemplate,編寫測試方法進行訪問測試;

package com.kuang.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Date; import java.util.List; import java.util.Map; @RestController @RequestMapping("/jdbc") public class JdbcController { /** * Spring Boot 默認提供了數據源,默認提供了 org.springframework.jdbc.core.JdbcTemplate * JdbcTemplate 中會本身注入數據源,用於簡化 JDBC操做 * 還能避免一些常見的錯誤,使用起來也不用再本身來關閉數據庫鏈接 */ @Autowired JdbcTemplate jdbcTemplate; //查詢employee表中全部數據 //List 中的1個 Map 對應數據庫的 1行數據 //Map 中的 key 對應數據庫的字段名,value 對應數據庫的字段值 @GetMapping("/list") public List<Map<String, Object>> userList(){ String sql = "select * from employee"; List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql); return maps; } //新增一個用戶 @GetMapping("/add") public String addUser(){ //插入語句,注意時間問題 String sql = "insert into employee(last_name, email,gender,department,birth)" + " values ('狂神說','24736743@qq.com',1,101,'"+ new Date().toLocaleString() +"')"; jdbcTemplate.update(sql); //查詢 return "addOk"; } //修改用戶信息 @GetMapping("/update/{id}") public String updateUser(@PathVariable("id") int id){ //插入語句 String sql = "update employee set last_name=?,email=? where id="+id; //數據 Object[] objects = new Object[2]; objects[0] = "秦疆"; objects[1] = "24736743@sina.com"; jdbcTemplate.update(sql,objects); //查詢 return "updateOk"; } //刪除用戶 @GetMapping("/delete/{id}") public String delUser(@PathVariable("id") int id){ //插入語句 String sql = "delete from employee where id=?"; jdbcTemplate.update(sql,id); //查詢 return "deleteOk"; } } 

測試請求,結果正常;

到此,CURD的基本操做,使用 JDBC 就搞定了。https://docs.spring.io/spring-boot/docs/2.0.4.RELEASE/reference/htmlsingle/#using-boot-starter)

 

7.五、原理探究 :

org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration 數據源配置類做用 :根據邏輯判斷以後,添加數據源;

SpringBoot默認支持如下數據源:

com.zaxxer.hikari.HikariDataSource (Spring Boot 2.0 以上,默認使用此數據源)

org.apache.tomcat.jdbc.pool.DataSource

org.apache.commons.dbcp2.BasicDataSource

能夠使用 spring.datasource.type 指定自定義的數據源類型,值爲 要使用的鏈接池實現的徹底限定名。默認狀況下,它是從類路徑自動檢測的。

@Configuration @ConditionalOnMissingBean({DataSource.class}) @ConditionalOnProperty( name = {"spring.datasource.type"} ) static class Generic { Generic() { } @Bean public DataSource dataSource(DataSourceProperties properties) { return properties.initializeDataSourceBuilder().build(); } } 

八、SpringBoot整合Druid

8.一、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 基本配置參數以下:

配置 缺省值 說明
name   配置這個屬性的意義在於,若是存在多個數據源,監控的時候能夠經過名字來區分開來。 若是沒有配置,將會生成一個名字,格式是:"DataSource-" + System.identityHashCode(this). 另外配置此屬性至少在1.0.5版本中是不起做用的,強行設置name會出錯 詳情-點此處
url   鏈接數據庫的url,不一樣數據庫不同。例如: mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto
username   鏈接數據庫的用戶名
password   鏈接數據庫的密碼。若是你不但願密碼直接寫在配置文件中,能夠使用ConfigFilter。詳細看這裏:https://github.com/alibaba/druid/wiki/使用ConfigFilter
driverClassName 根據url自動識別 這一項可配可不配,若是不配置druid會根據url自動識別dbType,而後選擇相應的driverClassName
initialSize 0 初始化時創建物理鏈接的個數。初始化發生在顯示調用init方法,或者第一次getConnection時
maxActive 8 最大鏈接池數量
maxIdle 8 已經再也不使用,配置了也沒效果
minIdle   最小鏈接池數量
maxWait   獲取鏈接時最大等待時間,單位毫秒。配置了maxWait以後,缺省啓用公平鎖,併發效率會有所降低,若是須要能夠經過配置useUnfairLock屬性爲true使用非公平鎖。
poolPreparedStatements false 是否緩存preparedStatement,也就是PSCache。PSCache對支持遊標的數據庫性能提高巨大,好比說oracle。在mysql下建議關閉。
maxOpenPreparedStatements -1 要啓用PSCache,必須配置大於0,當大於0時,poolPreparedStatements自動觸發修改成true。在Druid中,不會存在Oracle下PSCache佔用內存過多的問題,能夠把這個數值配置大一些,好比說100
validationQuery   用來檢測鏈接是否有效的sql,要求是一個查詢語句。若是validationQuery爲null,testOnBorrow、testOnReturn、testWhileIdle都不會其做用。
validationQueryTimeout   單位:秒,檢測鏈接是否有效的超時時間。底層調用jdbc Statement對象的void setQueryTimeout(int seconds)方法
testOnBorrow true 申請鏈接時執行validationQuery檢測鏈接是否有效,作了這個配置會下降性能。
testOnReturn false 歸還鏈接時執行validationQuery檢測鏈接是否有效,作了這個配置會下降性能
testWhileIdle false 建議配置爲true,不影響性能,而且保證安全性。申請鏈接的時候檢測,若是空閒時間大於timeBetweenEvictionRunsMillis,執行validationQuery檢測鏈接是否有效。
timeBetweenEvictionRunsMillis 1分鐘(1.0.14) 有兩個含義: 1) Destroy線程會檢測鏈接的間隔時間,若是鏈接空閒時間大於等於minEvictableIdleTimeMillis則關閉物理鏈接 2) testWhileIdle的判斷依據,詳細看testWhileIdle屬性的說明
numTestsPerEvictionRun   再也不使用,一個DruidDataSource只支持一個EvictionRun
minEvictableIdleTimeMillis 30分鐘(1.0.14) 鏈接保持空閒而不被驅逐的最長時間
connectionInitSqls   物理鏈接初始化的時候執行的sql
exceptionSorter 根據dbType自動識別 當數據庫拋出一些不可恢復的異常時,拋棄鏈接
filters   屬性類型是字符串,經過別名的方式配置擴展插件,經常使用的插件有: 監控統計用的filter:stat 日誌用的filter:log4j 防護sql注入的filter:wall
proxyFilters   類型是List<com.alibaba.druid.filter.Filter>,若是同時配置了filters和proxyFilters,是組合關係,並不是替換關係

8.二、配置數據源

一、添加上 Druid 數據源依賴。

<!-- https://mvnrepository.com/artifact/com.alibaba/druid --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.12</version> </dependency> 

二、切換數據源;以前已經說過 Spring Boot 2.0 以上默認使用 com.zaxxer.hikari.HikariDataSource 數據源,但能夠 經過 spring.datasource.type 指定數據源。

spring: datasource: username: root password: 123456 url: jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8 driver-class-name: com.mysql.cj.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource # 自定義數據源 

三、數據源切換以後,在測試類中注入 DataSource,而後獲取到它,輸出一看便知是否成功切換;

四、切換成功!既然切換成功,就能夠設置數據源鏈接初始化大小、最大鏈接數、等待時間、最小鏈接數 等設置項;能夠查看源碼

spring: datasource: username: root password: 123456 #?serverTimezone=UTC解決時區的報錯 url: jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8 driver-class-name: com.mysql.cj.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource #Spring Boot 默認是不注入這些屬性值的,須要本身綁定 #druid 數據源專有配置 initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true #配置監控統計攔截的filters,stat:監控統計、log4j:日誌記錄、wall:防護sql注入 #若是容許時報錯 java.lang.ClassNotFoundException: org.apache.log4j.Priority #則導入 log4j 依賴便可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j filters: stat,wall,log4j maxPoolPreparedStatementPerConnectionSize: 20 useGlobalDataSourceStat: true connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500 

五、導入Log4j 的依賴

<!-- https://mvnrepository.com/artifact/log4j/log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> 

六、如今須要程序員本身爲 DruidDataSource 綁定全局配置文件中的參數,再添加到容器中,而再也不使用 Spring Boot 的自動生成了;咱們須要 本身添加 DruidDataSource 組件到容器中,並綁定屬性;

package com.kuang.config; import com.alibaba.druid.pool.DruidDataSource; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.sql.DataSource; @Configuration public class DruidConfig { /* 將自定義的 Druid數據源添加到容器中,再也不讓 Spring Boot 自動建立 綁定全局配置文件中的 druid 數據源屬性到 com.alibaba.druid.pool.DruidDataSource從而讓它們生效 @ConfigurationProperties(prefix = "spring.datasource"):做用就是將 全局配置文件中 前綴爲 spring.datasource的屬性值注入到 com.alibaba.druid.pool.DruidDataSource 的同名參數中 */ @ConfigurationProperties(prefix = "spring.datasource") @Bean public DataSource druidDataSource() { return new DruidDataSource(); } } 

七、去測試類中測試一下;看是否成功!

@SpringBootTest class SpringbootDataJdbcApplicationTests { //DI注入數據源 @Autowired DataSource dataSource; @Test public void contextLoads() throws SQLException { //看一下默認數據源 System.out.println(dataSource.getClass()); //得到鏈接 Connection connection = dataSource.getConnection(); System.out.println(connection); DruidDataSource druidDataSource = (DruidDataSource) dataSource; System.out.println("druidDataSource 數據源最大鏈接數:" + druidDataSource.getMaxActive()); System.out.println("druidDataSource 數據源初始化鏈接數:" + druidDataSource.getInitialSize()); //關閉鏈接 connection.close(); } } 

輸出結果 :可見配置參數已經生效!

8.三、配置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

進入以後

配置 Druid web 監控 filter 過濾器

//配置 Druid 監控 之 web 監控的 filter //WebStatFilter:用於配置Web和Druid數據源之間的管理關聯監控統計 @Bean public FilterRegistrationBean webStatFilter() { FilterRegistrationBean bean = new FilterRegistrationBean(); bean.setFilter(new WebStatFilter()); //exclusions:設置哪些請求進行過濾排除掉,從而不進行統計 Map<String, String> initParams = new HashMap<>(); initParams.put("exclusions", "*.js,*.css,/druid/*,/jdbc/*"); bean.setInitParameters(initParams); //"/*" 表示過濾全部請求 bean.setUrlPatterns(Arrays.asList("/*")); return bean; } 

平時在工做中,按需求進行配置便可,主要用做監

九、SpringBoot 整合mybatis

9.一、導入mybatis所須要的依賴

<!-- 引入 myBatis,這是 MyBatis官方提供的適配 Spring Boot 的,而不是Spring Boot本身的--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.0</version> </dependency> 

9.二、配置數據庫鏈接信息

spring.datasource.username=root spring.datasource.password=123456 spring.datasource.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8 spring.datasource.driver-class-name=com.mysql.jdbc.Driver 

9.三、咱們這裏就是用默認的數據源了;先去測試一下鏈接是否成功!

@RunWith(SpringRunner.class) @SpringBootTest public class SpringbootDemoMybatisApplicationTests { @Autowired DataSource dataSource; @Test public void contextLoads() throws SQLException { System.out.println("數據源>>>>>>" + dataSource.getClass()); Connection connection = dataSource.getConnection(); System.out.println("鏈接>>>>>>>>>" + connection); System.out.println("鏈接地址>>>>>" + connection.getMetaData().getURL()); connection.close(); } } 

查看輸出結果,數據庫配置OK!

9.四、建立實體類

package com.kuang.mybatis.pojo; public class User { private int id; private String name; private String pwd; public User() { } public User(int id, String name, String pwd) { this.id = id; this.name = name; this.pwd = pwd; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", pwd='" + pwd + '\'' + '}'; } } 

9.五、配置Mapper接口類

package com.kuang.mybatis.pojo.mapper; import com.kuang.mybatis.pojo.User; import org.apache.ibatis.annotations.Mapper; import org.springframework.stereotype.Repository; import java.util.List; //@Mapper : 表示本類是一個 MyBatis 的 Mapper,等價於之前 Spring 整合 MyBatis 時的 Mapper 接口 @Mapper @Repository public interface UserMapper { //選擇所有用戶 List<User> selectUser(); //根據id選擇用戶 User selectUserById(int id); //添加一個用戶 int addUser(User user); //修改一個用戶 int updateUser(User user); //根據id刪除用戶 int deleteUser(int id); } 

9.六、對應Mapper映射文件

<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.kuang.mybatis.pojo.mapper.UserMapper"> <select id="selectUser" resultType="User"> select * from user </select> <select id="selectUserById" resultType="User"> select * from user where id = #{id} </select> <insert id="addUser" parameterType="User"> insert into user (id,name,pwd) values (#{id},#{name},#{pwd}) </insert> <update id="updateUser" parameterType="User"> update user set name=#{name},pwd=#{pwd} where id = #{id} </update> <delete id="deleteUser" parameterType="int"> delete from user where id = #{id} </delete> </mapper> 

9.七、maven配置資源過濾問題

<resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> </resources> 

9.八、SpringBoot 整合!

之前 MyBatis 未與 spring 整合時,配置數據源、事務、鏈接數據庫的帳號、密碼等都是在 myBatis 核心配置文件中進行的myBatis 與 spring 整合後,配置數據源、事務、鏈接數據庫的帳號、密碼等就交由 spring 管理。所以,在這裏咱們即便不使用mybatis配置文件也徹底ok!
既然已經提供了 myBatis 的映射配置文件,天然要告訴 spring boot 這些文件的位置

spring.datasource.username=root spring.datasource.password=123456 spring.datasource.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8 spring.datasource.driver-class-name=com.mysql.jdbc.Driver  #指定myBatis的核心配置文件與Mapper映射文件 mybatis.mapper-locations=classpath:mybatis/mapper/*.xml # 注意:對應實體類的路徑 mybatis.type-aliases-package=com.kuang.mybatis.pojo 

已經說過 spring boot 官方並無提供 myBaits 的啓動器,是 myBatis 官方提供的開發包來適配的 spring boot,從 pom.xml 文件中的依賴包名也能看出來,並不是是以 spring-boot 開頭的;

同理上面全局配置文件中的這兩行配置也是以 mybatis 開頭 而非 spring 開頭也充分說明這些都是 myBatis 官方提供的

能夠從 org.mybatis.spring.boot.autoconfigure.MybatisProperties 中查看全部配置項

@ConfigurationProperties( prefix = "mybatis" ) public class MybatisProperties { public static final String MYBATIS_PREFIX = "mybatis"; private static final ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver(); private String configLocation; private String[] mapperLocations; private String typeAliasesPackage; private Class<?> typeAliasesSuperType; private String typeHandlersPackage; private boolean checkConfigLocation = false; private ExecutorType executorType; private Class<? extends LanguageDriver> defaultScriptingLanguageDriver; private Properties configurationProperties; @NestedConfigurationProperty private Configuration configuration; 

也能夠直接去查看 官方文檔

9.九、編寫controller

package com.kuang.mybatis.controller; import com.kuang.mybatis.pojo.User; import com.kuang.mybatis.pojo.mapper.UserMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RestController public class UserController { @Autowired private UserMapper userMapper; //選擇所有用戶 @GetMapping("/selectUser") public String selectUser(){ List<User> users = userMapper.selectUser(); for (User user : users) { System.out.println(user); } return "ok"; } //根據id選擇用戶 @GetMapping("/selectUserById") public String selectUserById(){ User user = userMapper.selectUserById(1); System.out.println(user); return "ok"; } //添加一個用戶 @GetMapping("/addUser") public String addUser(){ userMapper.addUser(new User(5,"阿毛","456789")); return "ok"; } //修改一個用戶 @GetMapping("/updateUser") public String updateUser(){ userMapper.updateUser(new User(5,"阿毛","421319")); return "ok"; } //根據id刪除用戶 @GetMapping("/deleteUser") public String deleteUser(){ userMapper.deleteUser(5); return "ok"; } } 

9.十、啓動項目訪問進行測試!

步驟:

Mybatis整合包

mybatis-spring-boot-starter

1.導入包

2.配置文件

3.mybatis配置

4.編寫sql

5.service層調用dao層

6.controller調用service層

注:配置數據庫鏈接信息(不變)

spring: datasource: username: root password: 123456 #?serverTimezone=UTC解決時區的報錯 url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8 driver-class-name: com.mysql.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource #Spring Boot 默認是不注入這些屬性值的,須要本身綁定 #druid 數據源專有配置 initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true #配置監控統計攔截的filters,stat:監控統計、log4j:日誌記錄、wall:防護sql注入 #若是容許時報錯 java.lang.ClassNotFoundException: org.apache.log4j.Priority #則導入 log4j 依賴便可,Maven 地址: https://mvnrepository.com/artifact/log4j/log4j filters: stat,wall,log4j maxPoolPreparedStatementPerConnectionSize: 20 useGlobalDataSourceStat: true connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500 

十、SpringBoot:Web開發靜態資源處理

Web開發探究

10.一、簡介

好的,同窗們,那麼接下來呢,咱們開始學習SpringBoot與Web開發,從這一章日後,就屬於咱們實戰部分的內容了;

其實SpringBoot的東西用起來很是簡單,由於SpringBoot最大的特色就是自動裝配。

使用SpringBoot的步驟:

一、建立一個SpringBoot應用,選擇咱們須要的模塊,SpringBoot就會默認將咱們的須要的模塊自動配置好

二、手動在配置文件中配置部分配置項目就能夠運行起來了

三、專一編寫業務代碼,不須要考慮之前那樣一大堆的配置了。

要熟悉掌握開發,以前學習的自動配置的原理必定要搞明白!

好比SpringBoot到底幫咱們配置了什麼?咱們能不能修改?咱們能修改哪些配置?咱們能不能擴展?

  • 向容器中自動配置組件 :*** Autoconfiguration
  • 自動配置類,封裝配置文件的內容:***Properties

沒事就找找類,看看自動裝配原理!

咱們以後來進行一個單體項目的小項目測試,讓你們可以快速上手開發!

靜態資源處理

10.二、靜態資源映射規則

首先,咱們搭建一個普通的SpringBoot項目,回顧一下HelloWorld程序!

寫請求很是簡單,那咱們要引入咱們前端資源,咱們項目中有許多的靜態資源,好比css,js等文件,這個SpringBoot怎麼處理呢?

若是咱們是一個web應用,咱們的main下會有一個webapp,咱們之前都是將全部的頁面導在這裏面的,對吧!可是咱們如今的pom呢,打包方式是爲jar的方式,那麼這種方式SpringBoot能不能來給咱們寫頁面呢?固然是能夠的,可是SpringBoot對於靜態資源放置的位置,是有規定的!

咱們先來聊聊這個靜態資源映射規則:

SpringBoot中,SpringMVC的web配置都在 WebMvcAutoConfiguration 這個配置類裏面;

咱們能夠去看看 WebMvcAutoConfigurationAdapter 中有不少配置方法;

有一個方法:addResourceHandlers 添加資源處理

@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { if (!this.resourceProperties.isAddMappings()) { // 已禁用默認資源處理 logger.debug("Default resource handling disabled"); return; } // 緩存控制 Duration cachePeriod = this.resourceProperties.getCache().getPeriod(); CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl(); // webjars 配置 if (!registry.hasMappingForPattern("/webjars/**")) { customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**") .addResourceLocations("classpath:/META-INF/resources/webjars/") .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)); } // 靜態資源配置 String staticPathPattern = this.mvcProperties.getStaticPathPattern(); if (!registry.hasMappingForPattern(staticPathPattern)) { customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern) .addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations())) .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)); } } 

讀一下源代碼:好比全部的 /webjars/** , 都須要去 classpath:/META-INF/resources/webjars/ 找對應的資源;

10.三、什麼是webjars 呢?

Webjars本質就是以jar包的方式引入咱們的靜態資源 , 咱們之前要導入一個靜態資源文件,直接導入便可。

使用SpringBoot須要使用Webjars,咱們能夠去搜索一下:

網站:https://www.webjars.org

要使用jQuery,咱們只要要引入jQuery對應版本的pom依賴便可!

<dependency> <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>3.4.1</version> </dependency> 

導入完畢,查看webjars目錄結構,並訪問Jquery.js文件!

訪問:只要是靜態資源,SpringBoot就會去對應的路徑尋找資源,咱們這裏訪問:http://localhost:8080/webjars/jquery/3.4.1/jquery.js

10.四、第二種靜態資源映射規則

那咱們項目中要是使用本身的靜態資源該怎麼導入呢?咱們看下一行代碼;

咱們去找staticPathPattern發現第二種映射規則 :/** , 訪問當前的項目任意資源,它會去找 resourceProperties 這個類,咱們能夠點進去看一下分析:

// 進入方法 public String[] getStaticLocations() { return this.staticLocations; } // 找到對應的值 private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS; // 找到路徑 private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/" }; 

ResourceProperties 能夠設置和咱們靜態資源有關的參數;這裏面指向了它會去尋找資源的文件夾,即上面數組的內容。

因此得出結論,如下四個目錄存放的靜態資源能夠被咱們識別:

"classpath:/META-INF/resources/" "classpath:/resources/" "classpath:/static/" "classpath:/public/" 

咱們能夠在resources根目錄下新建對應的文件夾,均可以存放咱們的靜態文件;

好比咱們訪問 http://localhost:8080/1.js , 他就會去這些文件夾中尋找對應的靜態資源文件;

10.五、自定義靜態資源路徑

咱們也能夠本身經過配置文件來指定一下,哪些文件夾是須要咱們放靜態資源文件的,在application.properties中配置;

spring.resources.static-locations=classpath:/coding/,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/ 看結果!

關於網站圖標說明

與其餘靜態資源同樣,Spring Boot在配置的靜態內容位置中查找 favicon.ico。若是存在這樣的文件,它將自動用做應用程序的favicon。

一、關閉SpringBoot默認圖標

#關閉默認圖標spring.mvc.favicon.enabled=false 

二、本身放一個圖標在靜態資源目錄下,我放在 public 目錄下

三、清除瀏覽器緩存!刷新網頁,發現圖標已經變成本身的了!

十一、SpringBoot:Thymeleaf模板引擎

11.一、模板引擎

前端交給咱們的頁面,是html頁面。若是是咱們之前開發,咱們須要把他們轉成jsp頁面,jsp好處就是當咱們查出一些數據轉發到JSP頁面之後,咱們能夠用jsp輕鬆實現數據的顯示,及交互等。

jsp支持很是強大的功能,包括能寫Java代碼,可是呢,咱們如今的這種狀況,SpringBoot這個項目首先是以jar的方式,不是war,像第二,咱們用的仍是嵌入式的Tomcat,因此呢,他如今默認是不支持jsp的

那不支持jsp,若是咱們直接用純靜態頁面的方式,那給咱們開發會帶來很是大的麻煩,那怎麼辦呢?

SpringBoot推薦你能夠來使用模板引擎:

模板引擎,咱們其實你們聽到不少,其實jsp就是一個模板引擎,還有用的比較多的freemarker,包括SpringBoot給咱們推薦的Thymeleaf,模板引擎有很是多,但再多的模板引擎,他們的思想都是同樣的,什麼樣一個思想呢咱們來看一下這張圖:

模板引擎的做用就是咱們來寫一個頁面模板,好比有些值呢,是動態的,咱們寫一些表達式。而這些值,從哪來呢,就是咱們在後臺封裝一些數據。而後把這個模板和這個數據交給咱們模板引擎,模板引擎按照咱們這個數據幫你把這表達式解析、填充到咱們指定的位置,而後把這個數據最終生成一個咱們想要的內容給咱們寫出去,這就是咱們這個模板引擎,無論是jsp仍是其餘模板引擎,都是這個思想。只不過呢,就是說不一樣模板引擎之間,他們可能這個語法有點不同。其餘的我就不介紹了,我主要來介紹一下SpringBoot給咱們推薦的Thymeleaf模板引擎,這模板引擎呢,是一個高級語言的模板引擎,他的這個語法更簡單。並且呢,功能更強大。

咱們呢,就來看一下這個模板引擎,那既然要看這個模板引擎。首先,咱們來看SpringBoot裏邊怎麼用。

11.二、引入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> 

Maven會自動下載jar包,咱們能夠去看下下載的東西;

11.三、Thymeleaf分析

前面呢,咱們已經引入了Thymeleaf,那這個要怎麼使用呢?

咱們首先得按照SpringBoot的自動配置原理看一下咱們這個Thymeleaf的自動配置規則,在按照那個規則,咱們進行使用。

咱們去找一下Thymeleaf的自動配置類:ThymeleafPropert

@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什麼都不須要配置,只須要將他放在指定的文件夾下便可!

測試

一、編寫一個TestController

@Controller public class TestController { @RequestMapping("/t1") public String test1(){ //classpath:/templates/test.html return "test"; } } 

二、編寫一個測試頁面 test.html 放在 templates 目錄下

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>測試頁面</h1> </body> </html> 

三、啓動項目請求測試

11.四、Thymeleaf 語法學習

要學習語法,仍是參考官網文檔最爲準確,咱們找到對應的版本看一下;

Thymeleaf 官網:https://www.thymeleaf.org/ , 簡單看一下官網!咱們去下載Thymeleaf的官方文檔!

咱們作個最簡單的練習 :咱們須要查出一些數據,在頁面中展現

一、修改測試請求,增長數據傳輸;

@RequestMapping("/t1") public String test1(Model model){ //存入數據 model.addAttribute("msg","Hello,Thymeleaf"); //classpath:/templates/test.html return "test"; } 

二、咱們要使用thymeleaf,須要在html文件中導入命名空間的約束,方便提示。

咱們能夠去官方文檔的#3中看一下命名空間拿來過來:

xmlns:th="http://www.thymeleaf.org"

三、咱們去編寫下前端頁面

<!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> 

四、啓動測試!

OK,入門搞定,咱們來認真研習一下Thymeleaf的使用語法!

一、咱們能夠使用任意的 th:attr 來替換Html中原生屬性的值!

二、咱們能寫哪些表達式呢?

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 , false Null literal: null Literal 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 , or Boolean negation (unary operator): ! , not Comparisons 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: _ 

練習測試:

一、 咱們編寫一個Controller,放一些數據

@RequestMapping("/t2") public String test2(Map<String,Object> map){ //存入數據 map.put("msg","<h1>Hello</h1>"); map.put("users", Arrays.asList("qinjiang","kuangshen")); //classpath:/templates/test.html return "test"; } 

二、測試頁面取出數據

<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>狂神說</title> </head> <body> <h1>測試頁面</h1> <div th:text="${msg}"></div> <!--不轉義--> <div th:utext="${msg}"></div> <!--遍歷數據--> <!--th:each每次遍歷都會生成當前這個標籤:官網#9--> <h4 th:each="user :${users}" th:text="${user}"></h4> <h4> <!--行內寫法:官網#12--> <span th:each="user:${users}">[[${user}]]</span> </h4> </body> </html> 

三、啓動項目測試!

咱們看完語法,不少樣式,咱們即便如今學習了,也會忘記,因此咱們在學習過程當中,須要使用什麼,根據官方文檔來查詢,纔是最重要的,要熟練使用官方文檔!

十二、SpringBoot:MVC自動配置原理

12.一、官網閱讀

在進行項目編寫前,咱們還須要知道一個東西,就是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. 

咱們來仔細對照,看一下它怎麼實現的,它告訴咱們SpringBoot已經幫咱們自動配置好了SpringMVC,而後自動配置了哪些東西呢?

12.二、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; } 

咱們能夠點進這類看看!找到對應的解析視圖的代碼;

@Nullable // 註解說明:@Nullable 即參數可爲null public View resolveViewName(String viewName, Locale locale) throws Exception { RequestAttributes attrs = RequestContextHolder.getRequestAttributes(); Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes"); List<MediaType> requestedMediaTypes = this.getMediaTypes(((ServletRequestAttributes)attrs).getRequest()); if (requestedMediaTypes != null) { // 獲取候選的視圖對象 List<View> candidateViews = this.getCandidateViews(viewName, locale, requestedMediaTypes); // 選擇一個最適合的視圖對象,而後把這個對象返回 View bestView = this.getBestView(candidateViews, requestedMediaTypes, attrs); if (bestView != null) { return bestView; } } // ..... } 

咱們繼續點進去看,他是怎麼得到候選的視圖的呢?

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()); } // ............... } 

既然它是在容器中去找視圖解析器,咱們是否能夠猜測,咱們就能夠去實現一個視圖解析器了呢?

咱們能夠本身給容器中去添加一個視圖解析器;這個類就會幫咱們自動的將它組合進來;咱們去實現一下

一、咱們在咱們的主程序中去寫一個視圖解析器來試試;

@Bean //放到bean中 public ViewResolver myViewResolver(){ return new MyViewResolver(); } //咱們寫一個靜態內部類,視圖解析器就須要實現ViewResolver接口 private static class MyViewResolver implements ViewResolver{ @Override public View resolveViewName(String s, Locale locale) throws Exception { return null; } } 

二、怎麼看咱們本身寫的視圖解析器有沒有起做用呢?

咱們給 DispatcherServlet 中的 doDispatch方法 加個斷點進行調試一下,由於全部的請求都會走到這個方法中

三、咱們啓動咱們的項目,而後隨便訪問一個頁面,看一下Debug信息;

找到this

找到視圖解析器,咱們看到咱們本身定義的就在這裏了;

因此說,咱們若是想要使用本身定製化的東西,咱們只須要給容器中添加這個組件就行了!剩下的事情SpringBoot就會幫咱們作了!

12.三、轉換器和格式化器

找到格式化轉換器:

@Bean @Override public FormattingConversionService mvcConversionService() { // 拿到配置文件中的格式化規則 WebConversionService conversionService = new WebConversionService(this.mvcProperties.getDateFormat()); addFormatters(conversionService); return conversionService; } 

點擊去:

public String getDateFormat() { return this.dateFormat; } /** * Date format to use. For instance, `dd/MM/yyyy`. 默認的 */ private String dateFormat; 

能夠看到在咱們的Properties文件中,咱們能夠進行自動配置它!

若是配置了本身的格式化方式,就會註冊到Bean中生效,咱們能夠在配置文件中配置日期格式化的規則:

其他的就不一一舉例了,你們能夠下去多研究探討便可!

12.四、修改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 { @Override public void addViewControllers(ViewControllerRegistry registry) { // 瀏覽器發送/test , 就會跳轉到test頁面; registry.addViewController("/test").setViewName("test"); } } 

咱們去瀏覽器訪問一下:

確實也跳轉過來了!因此說,咱們要擴展SpringMVC,官方就推薦咱們這麼去使用,既保SpringBoot留全部的自動配置,也能用咱們擴展的配置!

咱們能夠去分析一下原理:

一、WebMvcAutoConfiguration 是 SpringMVC的自動配置類,裏面有一個類WebMvcAutoConfigurationAdapter

二、這個類上有一個註解,在作其餘自動配置時會導入:@Import(EnableWebMvcConfiguration.class)

三、咱們點進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); } } } 

四、咱們能夠在這個類中去尋找一個咱們剛纔設置的viewController當作參考,發現它調用了一個

protected void addViewControllers(ViewControllerRegistry registry) { this.configurers.addViewControllers(registry); } 

五、咱們點進去看一下

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本身的配置類,咱們本身的配置類固然也會被調用;

12.五、全面接管SpringMVC

官方文檔:

If you want to take complete control of Spring MVC
you can add your own @Configuration annotated with @EnableWebMvc. 

全面接管即:SpringBoot對SpringMVC的自動配置不須要了,全部都是咱們本身去配置!

只需在咱們的配置類中要加一個@EnableWebMvc。

咱們看下若是咱們全面接管了SpringMVC了,咱們以前SpringBoot給咱們配置的靜態資源映射必定會無效,咱們能夠去測試一下;

不加註解以前,訪問首頁:

給配置類加上註解:@EnableWebMvc

咱們發現全部的SpringMVC自動配置都失效了!迴歸到了最初的樣子;

固然,咱們開發中,不推薦使用全面接管SpringMVC

思考問題?爲何加了一個註解,自動配置就失效了!咱們看下源碼:

一、這裏發現它是導入了一個類,咱們能夠繼續進去看

@Import({DelegatingWebMvcConfiguration.class})public @interface EnableWebMvc {} 

二、它繼承了一個父類 WebMvcConfigurationSupport

public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport { // ......} 

三、咱們來回顧一下Webmvc自動配置類

@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中會有很是多的擴展配置,只要看見了這個,咱們就應該多留心注意~

1三、SpringBoot:頁面國際化

有的時候,咱們的網站會去涉及中英文甚至多語言的切換,這時候咱們就須要學習國際化了!

13.一、準備工做

先在IDEA中統一設置properties的編碼問題!

編寫國際化配置文件,抽取頁面須要顯示的國際化頁面消息。咱們能夠去登陸頁面查看一下,哪些內容咱們須要編寫國際化的配置!

13.二、配置文件編寫

一、咱們在resources資源文件下新建一個i18n目錄,存放國際化配置文件

二、創建一個login.properties文件,還有一個login_zh_CN.properties;發現IDEA自動識別了咱們要作國際化操做;文件夾變了!

三、咱們能夠在這上面去新建一個文件;

彈出以下頁面:咱們再添加一個英文的;

這樣就快捷多了!

四、接下來,咱們就來編寫配置,咱們能夠看到idea下面有另一個視圖;

這個視圖咱們點擊 + 號就能夠直接添加屬性了;咱們新建一個login.tip,能夠看到邊上有三個文件框能夠輸入

咱們添加一下首頁的內容!

而後依次添加其餘頁面內容便可!

而後去查看咱們的配置文件;

login.properties :默認

login.btn=登陸 login.password=密碼 login.remember=記住我 login.tip=請登陸 login.username=用戶名 

英文:

login.btn=Sign in login.password=Password login.remember=Remember me login.tip=Please sign in login.username=Username 

中文:

login.btn=登陸 login.password=密碼 login.remember=記住我 login.tip=請登陸 login.username=用戶名 

OK,配置文件步驟搞定!

13.三、配置文件生效探究

咱們去看一下SpringBoot對國際化的自動配置!這裏又涉及到一個類:MessageSourceAutoConfiguration

裏面有一個方法,這裏發現SpringBoot已經自動配置好了管理咱們國際化資源文件的組件 ResourceBundleMessageSource;

// 獲取 properties 傳遞過來的值進行判斷 @Bean public MessageSource messageSource(MessageSourceProperties properties) { ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); if (StringUtils.hasText(properties.getBasename())) { // 設置國際化文件的基礎名(去掉語言國家代碼的) messageSource.setBasenames( StringUtils.commaDelimitedListToStringArray( StringUtils.trimAllWhitespace(properties.getBasename()))); } if (properties.getEncoding() != null) { messageSource.setDefaultEncoding(properties.getEncoding().name()); } messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale()); Duration cacheDuration = properties.getCacheDuration(); if (cacheDuration != null) { messageSource.setCacheMillis(cacheDuration.toMillis()); } messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat()); messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage()); return messageSource; } 

咱們真實 的狀況是放在了i18n目錄下,因此咱們要去配置這個messages的路徑;

spring.messages.basename=i18n.login

13.四、配置頁面國際化值

去頁面獲取國際化的值,查看Thymeleaf的文檔,找到message取值操做爲:#{...}。咱們去頁面測試下:

IDEA還有提示,很是智能的!

咱們能夠去啓動項目,訪問一下,發現已經自動識別爲中文的了!

可是咱們想要更好!能夠根據按鈕自動切換中文英文!

13.五、配置國際化解析

在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; } } } 

那假如咱們如今想點擊連接讓咱們的國際化資源生效,就須要讓咱們本身的Locale生效!

咱們去本身寫一個本身的LocaleResolver,能夠在連接上攜帶區域信息!

修改一下前端頁面的跳轉鏈接:

<!-- 這裏傳入參數不須要使用 ?使用 (key=value)--> <a class="btn btn-sm" th:href="@{/index.html(l='zh_CN')}">中文</a> <a class="btn btn-sm" th:href="@{/index.html(l='en_US')}">English</a> 

咱們去寫一個處理的組件類!

package com.kuang.component; import org.springframework.util.StringUtils; import org.springframework.web.servlet.LocaleResolver; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Locale; //能夠在連接上攜帶區域信息 public class MyLocaleResolver implements LocaleResolver { //解析請求 @Override public Locale resolveLocale(HttpServletRequest request) { String language = request.getParameter("l"); Locale locale = Locale.getDefault(); // 若是沒有獲取到就使用系統默認的 //若是請求連接不爲空 if (!StringUtils.isEmpty(language)){ //分割請求參數 String[] split = language.split("_"); //國家,地區 locale = new Locale(split[0],split[1]); } return locale; } @Override public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) { } } 

爲了讓咱們的區域化信息可以生效,咱們須要再配置一下這個組件!在咱們本身的MvcConofig下添加bean;

@Beanpublic LocaleResolver localeResolver(){ return new MyLocaleResolver();} 

咱們重啓項目,來訪問一下,發現點擊按鈕能夠實現成功切換!搞定收工!

1四、SpringBoot:集成Swagger終極版

學習目標:

  • 瞭解Swagger的概念及做用
  • 掌握在項目中集成Swagger自動生成API文檔

14.一、Swagger簡介

先後端分離

  • 前端 -> 前端控制層、視圖層
  • 後端 -> 後端控制層、服務層、數據訪問層
  • 先後端經過API進行交互
  • 先後端相對獨立且鬆耦合

產生的問題

  • 先後端集成,前端或者後端沒法作到「及時協商,儘早解決」,最終致使問題集中爆發

解決方案

  • 首先定義schema [ 計劃的提綱 ],並實時跟蹤最新的API,下降集成風險

Swagger

  • 號稱世界上最流行的API框架
  • Restful Api 文檔在線自動生成器 => API 文檔 與API 定義同步更新
  • 直接運行,在線測試API
  • 支持多種語言 (如:Java,PHP等)
  • 官網:https://swagger.io/

14.二、SpringBoot集成Swagger

SpringBoot集成Swagger => springfox,兩個jar包

  • Springfox-swagger2
  • swagger-springmvc

使用Swagger

要求:jdk 1.8 + 不然swagger2沒法運行

步驟:

一、新建一個SpringBoot-web項目

二、添加Maven依賴

<!-- 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> 

三、編寫HelloController,測試確保運行成功!

四、要使用Swagger,咱們須要編寫一個配置類-SwaggerConfig來配置 Swagger

@Configuration //配置類 @EnableSwagger2// 開啓Swagger2的自動配置 public class SwaggerConfig { }j 

五、訪問測試 :http://localhost:8080/swagger-ui.html ,能夠看到swagger的界面;

14.三、配置Swagger

一、Swagger實例Bean是Docket,因此經過配置Docket實例來配置Swaggger。

@Bean //配置docket以配置Swagger具體參數 public Docket docket() { return new Docket(DocumentationType.SWAGGER_2); } 

二、能夠經過apiInfo()屬性配置文檔信息

//配置文檔信息 private ApiInfo apiInfo() { Contact contact = new Contact("聯繫人名字", "http://xxx.xxx.com/聯繫人訪問連接", "聯繫人郵箱"); return new ApiInfo( "Swagger學習", // 標題 "學習演示如何配置Swagger", // 描述 "v1.0", // 版本 "http://terms.service.url/組織連接", // 組織連接 contact, // 聯繫人信息 "Apach 2.0 許可", // 許可 "許可連接", // 許可鏈接 new ArrayList<>()// 擴展 ); } 

三、Docket 實例關聯上 apiInfo()

@Bean public Docket docket() { return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()); } 

四、重啓項目,訪問測試 http://localhost:8080/swagger-ui.html 看下效果;

14.四、配置掃描接口

一、構建Docket時經過select()方法配置怎麼掃描接口。

any() // 掃描全部,項目中的全部接口都會被掃描到 none() // 不掃描接口 // 經過方法上的註解掃描,如withMethodAnnotation(GetMapping.class)只掃描get請求 withMethodAnnotation(final Class<? extends Annotation> annotation) // 經過類上的註解掃描,如.withClassAnnotation(Controller.class)只掃描有controller註解的類中的接口 withClassAnnotation(final Class<? extends Annotation> annotation) basePackage(final String basePackage) // 根據包路徑掃描接口 

二、重啓項目測試,因爲咱們配置根據包的路徑掃描接口,因此咱們只能看到一個類

三、除了經過包路徑配置掃描接口外,還能夠經過配置其餘方式掃描接口,這裏註釋一下全部的配置方式:

any() // 掃描全部,項目中的全部接口都會被掃描到 none() // 不掃描接口 // 經過方法上的註解掃描,如withMethodAnnotation(GetMapping.class)只掃描get請求 withMethodAnnotation(final Class<? extends Annotation> annotation) // 經過類上的註解掃描,如.withClassAnnotation(Controller.class)只掃描有controller註解的類中的接口 withClassAnnotation(final Class<? extends Annotation> annotation) basePackage(final String basePackage) // 根據包路徑掃描接口 

四、除此以外,咱們還能夠配置接口掃描過濾:

@Bean public Docket docket() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select()// 經過.select()方法,去配置掃描接口,RequestHandlerSelectors配置如何掃描接口 .apis(RequestHandlerSelectors.basePackage("com.kuang.swagger.controller")) // 配置如何經過path過濾,即這裏只掃描請求以/kuang開頭的接口 .paths(PathSelectors.ant("/kuang/**")) .build(); } 

五、這裏的可選值還有

any() // 任何請求都掃描 none() // 任何請求都不掃描 regex(final String pathRegex) // 經過正則表達式控制 ant(final String antPattern) // 經過ant()控制 

14.五、配置Swagger開關

一、經過enable()方法配置是否啓用swagger,若是是false,swagger將不能在瀏覽器中訪問了

@Bean public Docket docket() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .enable(false) //配置是否啓用Swagger,若是是false,在瀏覽器將沒法訪問 .select()// 經過.select()方法,去配置掃描接口,RequestHandlerSelectors配置如何掃描接口 .apis(RequestHandlerSelectors.basePackage("com.kuang.swagger.controller")) // 配置如何經過path過濾,即這裏只掃描請求以/kuang開頭的接口 .paths(PathSelectors.ant("/kuang/**")) .build(); } 

二、如何動態配置當項目處於test、dev環境時顯示swagger,處於prod時不顯示?

@Bean public Docket docket(Environment environment) { // 設置要顯示swagger的環境 Profiles of = Profiles.of("dev", "test"); // 判斷當前是否處於該環境 // 經過 enable() 接收此參數判斷是否要顯示 boolean b = environment.acceptsProfiles(of); return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .enable(b) //配置是否啓用Swagger,若是是false,在瀏覽器將沒法訪問 .select()// 經過.select()方法,去配置掃描接口,RequestHandlerSelectors配置如何掃描接口 .apis(RequestHandlerSelectors.basePackage("com.kuang.swagger.controller")) // 配置如何經過path過濾,即這裏只掃描請求以/kuang開頭的接口 .paths(PathSelectors.ant("/kuang/**")) .build(); } 

三、能夠在項目中增長一個dev的配置文件查看效果!

14.六、配置API分組

一、若是沒有配置分組,默認是default。經過groupName()方法便可配置分組:

@Bean public Docket docket(Environment environment) { return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()) .groupName("hello") // 配置分組 // 省略配置.... } 

二、重啓項目查看分組

三、如何配置多個分組?配置多個分組只須要配置多個docket便可:

@Bean public Docket docket1(){ return new Docket(DocumentationType.SWAGGER_2).groupName("group1"); } @Bean public Docket docket2(){ return new Docket(DocumentationType.SWAGGER_2).groupName("group2"); } @Bean public Docket docket3(){ return new Docket(DocumentationType.SWAGGER_2).groupName("group3"); } 

四、重啓項目查看便可

14.七、實體配置

一、新建一個實體類

@ApiModel("用戶實體") public class User { @ApiModelProperty("用戶名") public String username; @ApiModelProperty("密碼") public String password; } 

二、只要這個實體在請求接口的返回值上(即便是泛型),都能映射到實體項中:

@RequestMapping("/getUser") public User getUser(){ return new User(); } 

三、重啓查看測試

注:並非由於@ApiModel這個註解讓實體顯示在這裏了,而是隻要出如今接口方法的返回值上的實體都會顯示在這裏,而@ApiModel和@ApiModelProperty這兩個註解只是爲實體添加註釋的。

@ApiModel爲類添加註釋

@ApiModelProperty爲類屬性添加註釋

14.八、經常使用註解

Swagger的全部註解定義在io.swagger.annotations包下

下面列一些常常用到的,未列舉出來的能夠另行查閱說明:

Swagger註解 簡單說明
@Api(tags = "xxx模塊說明") 做用在模塊類上
@ApiOperation("xxx接口說明") 做用在接口方法上
@ApiModel("xxxPOJO說明") 做用在模型類上:如VO、BO
@ApiModelProperty(value = "xxx屬性說明",hidden = true) 做用在類方法和屬性上,hidden設置爲true能夠隱藏該屬性
@ApiParam("xxx參數說明") 做用在參數、方法和字段上,相似@ApiModelProperty

咱們也能夠給請求的接口配置一些註釋

@ApiOperation("狂神的接口") @PostMapping("/kuang") @ResponseBody public String kuang(@ApiParam("這個名字會被返回")String username){ return username; } 

這樣的話,能夠給一些比較難理解的屬性或者接口,增長一些配置信息,讓人更容易閱讀!

相較於傳統的Postman或Curl方式測試接口,使用swagger簡直就是傻瓜式操做,不須要額外說明文檔(寫得好自己就是文檔)並且更不容易出錯,只須要錄入數據而後點擊Execute,若是再配合自動化框架,能夠說基本就不須要人爲操做了。

Swagger是個優秀的工具,如今國內已經有不少的中小型互聯網公司都在使用它,相較於傳統的要先出Word接口文檔再測試的方式,顯然這樣也更符合如今的快速迭代開發行情。固然了,提醒下你們在正式環境要記得關閉Swagger,一來出於安全考慮二來也能夠節省運行時內存。

14.九、拓展:其餘皮膚

咱們能夠導入不一樣的包實現不一樣的皮膚定義:

一、默認的 訪問 http://localhost:8080/swagger-ui.html

<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.9.2</version> </dependency> 

二、bootstrap-ui 訪問 http://localhost:8080/doc.html

<!-- 引入swagger-bootstrap-ui包 /doc.html--> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>swagger-bootstrap-ui</artifactId> <version>1.9.1</version> </dependency> 

三、Layui-ui 訪問 http://localhost:8080/docs.html

<!-- 引入swagger-ui-layer包 /docs.html--> <dependency> <groupId>com.github.caspar-chen</groupId> <artifactId>swagger-ui-layer</artifactId> <version>1.1.3</version> </dependency> 

四、mg-ui 訪問 http://localhost:8080/document.html

<!-- 引入swagger-ui-layer包 /document.html--> <dependency> <groupId>com.zyplayer</groupId> <artifactId>swagger-mg-ui</artifactId> <version>1.0.6</version> </dependency> 

1五、SpringBoot:異步、定時、郵件任務

前言

在咱們的工做中,經常會用到異步處理任務,好比咱們在網站上發送郵件,後臺會去發送郵件,此時前臺會形成響應不動,直到郵件發送完畢,響應纔會成功,因此咱們通常會採用多線程的方式去處理這些任務。還有一些定時任務,好比須要在天天凌晨的時候,分析一次前一天的日誌信息。還有就是郵件的發送,微信的前身也是郵件服務呢?這些東西都是怎麼實現的呢?其實SpringBoot都給咱們提供了對應的支持,咱們上手使用十分的簡單,只須要開啓一些註解支持,配置一些配置文件便可!那咱們來看看吧~

最後編輯於2020.3.26 做者:狂神說

15.一、異步任務

一、建立一個service包

二、建立一個類AsyncService

異步處理仍是很是經常使用的,好比咱們在網站上發送郵件,後臺會去發送郵件,此時前臺會形成響應不動,直到郵件發送完畢,響應纔會成功,因此咱們通常會採用多線程的方式去處理這些任務。

編寫方法,僞裝正在處理數據,使用線程設置一些延時,模擬同步等待的狀況;

@Service public class AsyncService { public void hello(){ try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("業務進行中...."); } } 

三、編寫controller包

四、編寫AsyncController類

咱們去寫一個Controller測試一下

@RestController public class AsyncController { @Autowired AsyncService asyncService; @GetMapping("/hello") public String hello(){ asyncService.hello(); return "success"; } } 

五、訪問http://localhost:8080/hello進行測試,3秒後出現success,這是同步等待的狀況。

問題:咱們若是想讓用戶直接獲得消息,就在後臺使用多線程的方式進行處理便可,可是每次都須要本身手動去編寫多線程的實現的話,太麻煩了,咱們只須要用一個簡單的辦法,在咱們的方法上加一個簡單的註解便可,以下:

六、給hello方法添加@Async註解;

//告訴Spring這是一個異步方法 @Async public void hello(){ try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("業務進行中...."); } 

SpringBoot就會本身開一個線程池,進行調用!可是要讓這個註解生效,咱們還須要在主程序上添加一個註解@EnableAsync ,開啓異步註解功能;

@EnableAsync //開啓異步註解功能 @SpringBootApplication public class SpringbootTaskApplication { public static void main(String[] args) { SpringApplication.run(SpringbootTaskApplication.class, args); } } 

七、重啓測試,網頁瞬間響應,後臺代碼依舊執行!

15.二、定時任務

項目開發中常常須要執行一些定時任務,好比須要在天天凌晨的時候,分析一次前一天的日誌信息,Spring爲咱們提供了異步執行任務調度的方式,提供了兩個接口。

  • TaskExecutor接口
  • TaskScheduler接口

兩個註解:

  • @EnableScheduling
  • @Scheduled

cron表達式:

測試步驟:

一、建立一個ScheduledService

咱們裏面存在一個hello方法,他須要定時執行,怎麼處理呢?

@Service public class ScheduledService { //秒 分 時 日 月 周幾 //0 * * * * MON-FRI //注意cron表達式的用法; @Scheduled(cron = "0 * * * * 0-7") public void hello(){ System.out.println("hello....."); } } 

二、這裏寫完定時任務以後,咱們須要在主程序上增長@EnableScheduling 開啓定時任務功能

@EnableAsync //開啓異步註解功能 @EnableScheduling //開啓基於註解的定時任務 @SpringBootApplication public class SpringbootTaskApplication { public static void main(String[] args) { SpringApplication.run(SpringbootTaskApplication.class, args); } } 

三、咱們來詳細瞭解下cron表達式;

http://www.bejson.com/othertools/cron/

四、經常使用的表達式

(1)0/2 * * * * ? 表示每2秒 執行任務 (1)0 0/2 * * * ? 表示每2分鐘 執行任務 (1)0 0 2 1 * ? 表示在每個月的1日的凌晨2點調整任務 (2)0 15 10 ? * MON-FRI 表示週一到週五天天上午10:15執行做業 (3)0 15 10 ? 6L 2002-2006 表示2002-2006年的每月的最後一個星期五上午10:15執行做 (4)0 0 10,14,16 * * ? 天天上午10點,下午2點,4點 (5)0 0/30 9-17 * * ? 朝九晚五工做時間內每半小時 (6)0 0 12 ? * WED 表示每一個星期三中午12點 (7)0 0 12 * * ? 天天中午12點觸發 (8)0 15 10 ? * * 天天上午10:15觸發 (9)0 15 10 * * ? 天天上午10:15觸發 (10)0 15 10 * * ? 天天上午10:15觸發 (11)0 15 10 * * ? 2005 2005年的天天上午10:15觸發 (12)0 * 14 * * ? 在天天下午2點到下午2:59期間的每1分鐘觸發 (13)0 0/5 14 * * ? 在天天下午2點到下午2:55期間的每5分鐘觸發 (14)0 0/5 14,18 * * ? 在天天下午2點到2:55期間和下午6點到6:55期間的每5分鐘觸發 (15)0 0-5 14 * * ? 在天天下午2點到下午2:05期間的每1分鐘觸發 (16)0 10,44 14 ? 3 WED 每一年三月的星期三的下午2:10和2:44觸發 (17)0 15 10 ? * MON-FRI 週一至週五的上午10:15觸發 (18)0 15 10 15 * ? 每個月15日上午10:15觸發 (19)0 15 10 L * ? 每個月最後一日的上午10:15觸發 (20)0 15 10 ? * 6L 每個月的最後一個星期五上午10:15觸發 (21)0 15 10 ? * 6L 2002-2005 2002年至2005年的每個月的最後一個星期五上午10:15觸發 (22)0 15 10 ? * 6#3 每個月的第三個星期五上午10:15觸發 

15.三、郵件任務

郵件發送,在咱們的平常開發中,也很是的多,Springboot也幫咱們作了支持

  • 郵件發送須要引入spring-boot-start-mail
  • SpringBoot 自動配置MailSenderAutoConfiguration
  • 定義MailProperties內容,配置在application.yml中
  • 自動裝配JavaMailSender
  • 測試郵件發送

測試:

一、引入pom依賴

<<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-mail</artifactId>
</dependency>

看它引入的依賴,能夠看到 jakarta.mail

<dependency>
   <groupId>com.sun.mail</groupId>
   <artifactId>jakarta.mail</artifactId>
   <version>1.6.4</version> <scope>compile</scope> </dependency> 

二、查看自動配置類:MailSenderAutoConfiguration

這個類中存在bean,JavaMailSenderImpl

而後咱們去看下配置文件

@ConfigurationProperties( prefix = "spring.mail" ) public class MailProperties { private static final Charset DEFAULT_CHARSET; private String host; private Integer port; private String username; private String password; private String protocol = "smtp"; private Charset defaultEncoding; private Map<String, String> properties; private String jndiName; } 

三、配置文件:

spring.mail.username=24736743@qq.comspring.mail.password=你的qq受權碼spring.mail.host=smtp.qq.com# qq須要配置sslspring.mail.properties.mail.smtp.ssl.enable=true 

獲取受權碼:在QQ郵箱中的設置->帳戶->開啓pop3和smtp服務

四、Spring單元測試

@Autowired JavaMailSenderImpl mailSender; @Test public void contextLoads() { //郵件設置1:一個簡單的郵件 SimpleMailMessage message = new SimpleMailMessage(); message.setSubject("通知-明天來狂神這聽課"); message.setText("今晚7:30開會"); message.setTo("24736743@qq.com"); message.setFrom("24736743@qq.com"); mailSender.send(message); } @Test public void contextLoads2() throws MessagingException { //郵件設置2:一個複雜的郵件 MimeMessage mimeMessage = mailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true); helper.setSubject("通知-明天來狂神這聽課"); helper.setText("<b style='color:red'>今天 7:30來開會</b>",true); //發送附件 helper.addAttachment("1.jpg",new File("")); helper.addAttachment("2.jpg",new File("")); helper.setTo("24736743@qq.com"); helper.setFrom("24736743@qq.com"); mailSender.send(mimeMessage); } 

查看郵箱,郵件接收成功!

咱們只須要使用Thymeleaf進行先後端結合便可開發本身網站郵件收發功能了!

1六、SpringBoot:富文本編輯器

16.一、簡介

思考:咱們平時在博客園,或者CSDN等平臺進行寫做的時候,有同窗思考過他們的編輯器是怎麼實現的嗎?

在博客園後臺的選項設置中,能夠看到一個文本編輯器的選項:

其實這個就是富文本編輯器,市面上有許多很是成熟的富文本編輯器,好比:

  • Editor.md——功能很是豐富的編輯器,左端編輯,右端預覽,很是方便,徹底免費

  • wangEditor——基於javascript和css開發的 Web富文本編輯器, 輕量、簡潔、界面美觀、易用、開源免費。

  • TinyMCE——TinyMCE是一個輕量級的基於瀏覽器的所見即所得編輯器,由JavaScript寫成。它對IE6+和Firefox1.5+都有着很是良好的支持。功能齊全,界面美觀,就是文檔是英文的,對開發人員英文水平有必定要求。

  • 百度ueditor——UEditor是由百度web前端研發部開發所見即所得富文本web編輯器,具備輕量,功能齊全,可定製,注重用戶體驗等特色,開源基於MIT協議,容許自由使用和修改代碼,缺點是已經沒有更新了

  • kindeditor——界面經典。

  • Textbox——Textbox是一款極簡但功能強大的在線文本編輯器,支持桌面設備和移動設備。主要功能包含內置的圖像處理和存儲、文件拖放、拼寫檢查和自動更正。此外,該工具還實現了屏幕閱讀器等輔助技術,並符合WAI-ARIA可訪問性標準。

  • CKEditor——國外的,界面美觀。

  • quill——功能強大,還能夠編輯公式等

  • simditor——界面美觀,功能較全。

  • summernote——UI好看,精美

  • jodit——功能齊全

  • froala Editor——界面很是好看,功能很是強大,很是好用(非免費)

總之,目前可用的富文本編輯器有不少......這只是其中的一部分

16.二、Editor.md

我這裏使用的就是Editor.md,做爲一個資深碼農,Mardown必然是咱們程序猿最喜歡的格式,看下面,就愛上了!

咱們能夠在官網下載它:https://pandao.github.io/editor.md/ , 獲得它的壓縮包!

解壓之後,在examples目錄下面,能夠看到他的不少案例使用!學習,其實就是看人家怎麼寫的,而後進行模仿就行了!

咱們能夠將整個解壓的文件倒入咱們的項目,將一些無用的測試和案例刪掉便可!

16.三、基礎工程搭建

數據庫設計

article:文章表

字段   備註
id int 文章的惟一ID
author varchar 做者
title varchar 標題
content longtext 文章的內容

建表SQL:

CREATE TABLE `article` ( `id` int(10) NOT NULL AUTO_INCREMENT COMMENT 'int文章的惟一ID', `author` varchar(50) NOT NULL COMMENT '做者', `title` varchar(100) NOT NULL COMMENT '標題', `content` longtext NOT NULL COMMENT '文章的內容', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 

基礎項目搭建

一、建一個SpringBoot項目配置

spring: datasource: username: root password: 123456 #?serverTimezone=UTC解決時區的報錯 url: jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8 driver-class-name: com.mysql.cj.jdbc.Driver 
<resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> </resources> 

二、實體類:

//文章類 @Data @NoArgsConstructor @AllArgsConstructor public class Article implements Serializable { private int id; //文章的惟一ID private String author; //做者名 private String title; //標題 private String content; //文章的內容 } 

三、mapper接口:

@Mapper @Repository public interface ArticleMapper { //查詢全部的文章 List<Article> queryArticles(); //新增一個文章 int addArticle(Article article); //根據文章id查詢文章 Article getArticleById(int id); //根據文章id刪除文章 int deleteArticleById(int id); } 

既然已經提供了 myBatis 的映射配置文件,天然要告訴 spring boot 這些文件的位置

mybatis: mapper-locations: classpath:com/kuang/mapper/*.xml type-aliases-package: com.kuang.pojo 

編寫一個Controller測試下,是否ok;

16.四、文章編輯整合(重點)

一、導入 editor.md 資源 ,刪除多餘文件

二、編輯文章頁面 editor.html、須要引入 jQuery;

<!DOCTYPE html> <html class="x-admin-sm" lang="zh" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>秦疆'Blog</title> <meta name="renderer" content="webkit"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta name="viewport" content="width=device-width,user-scalable=yes, minimum-scale=0.4, initial-scale=0.8,target-densitydpi=low-dpi" /> <!--Editor.md--> <link rel="stylesheet" th:href="@{/editormd/css/editormd.css}"/> <link rel="shortcut icon" href="https://pandao.github.io/editor.md/favicon.ico" type="image/x-icon" /> </head> <body> <div class="layui-fluid"> <div class="layui-row layui-col-space15"> <div class="layui-col-md12"> <!--博客表單--> <form name="mdEditorForm"> <div> 標題:<input type="text" name="title"> </div> <div> 做者:<input type="text" name="author"> </div> <div id="article-content"> <textarea name="content" id="content" style="display:none;"> </textarea> </div> </form> </div> </div> </div> </body> <!--editormd--> <script th:src="@{/editormd/lib/jquery.min.js}"></script> <script th:src="@{/editormd/editormd.js}"></script> <script type="text/javascript"> var testEditor; //window.onload = function(){ } $(function() { testEditor = editormd("article-content", { width : "95%", height : 400, syncScrolling : "single", path : "../editormd/lib/", saveHTMLToTextarea : true, // 保存 HTML 到 Textarea emoji: true, theme: "dark",//工具欄主題 previewTheme: "dark",//預覽主題 editorTheme: "pastel-on-dark",//編輯主題 tex : true, // 開啓科學公式TeX語言支持,默認關閉 flowChart : true, // 開啓流程圖支持,默認關閉 sequenceDiagram : true, // 開啓時序/序列圖支持,默認關閉, //圖片上傳 imageUpload : true, imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"], imageUploadURL : "/article/file/upload", onload : function() { console.log('onload', this); }, /*指定須要顯示的功能按鈕*/ toolbarIcons : function() { return ["undo","redo","|", "bold","del","italic","quote","ucwords","uppercase","lowercase","|", "h1","h2","h3","h4","h5","h6","|", "list-ul","list-ol","hr","|", "link","reference-link","image","code","preformatted-text", "code-block","table","datetime","emoji","html-entities","pagebreak","|", "goto-line","watch","preview","fullscreen","clear","search","|", "help","info","releaseIcon", "index"] }, /*自定義功能按鈕,下面我自定義了2個,一個是發佈,一個是返回首頁*/ toolbarIconTexts : { releaseIcon : "<span bgcolor=\"gray\">發佈</span>", index : "<span bgcolor=\"red\">返回首頁</span>", }, /*給自定義按鈕指定回調函數*/ toolbarHandlers:{ releaseIcon : function(cm, icon, cursor, selection) { //表單提交 mdEditorForm.method = "post"; mdEditorForm.action = "/article/addArticle";//提交至服務器的路徑 mdEditorForm.submit(); }, index : function(){ window.location.href = '/'; }, } }); }); </script> </html> 

三、編寫Controller,進行跳轉,以及保存文章

@Controller @RequestMapping("/article") public class ArticleController { @GetMapping("/toEditor") public String toEditor(){ return "editor"; } @PostMapping("/addArticle") public String addArticle(Article article){ articleMapper.addArticle(article); return "editor"; } } 

圖片上傳問題

一、前端js中添加配置

//圖片上傳
imageUpload : true, imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"], imageUploadURL : "/article/file/upload", // //這個是上傳圖片時的訪問地址 

二、後端請求,接收保存這個圖片, 須要導入 FastJson 的依賴!

//博客圖片上傳問題 @RequestMapping("/file/upload") @ResponseBody public JSONObject fileUpload(@RequestParam(value = "editormd-image-file", required = true) MultipartFile file, HttpServletRequest request) throws IOException { //上傳路徑保存設置 //得到SpringBoot當前項目的路徑:System.getProperty("user.dir") String path = System.getProperty("user.dir")+"/upload/"; //按照月份進行分類: Calendar instance = Calendar.getInstance(); String month = (instance.get(Calendar.MONTH) + 1)+"月"; path = path+month; File realPath = new File(path); if (!realPath.exists()){ realPath.mkdir(); } //上傳文件地址 System.out.println("上傳文件保存地址:"+realPath); //解決文件名字問題:咱們使用uuid; String filename = "ks-"+UUID.randomUUID().toString().replaceAll("-", ""); //經過CommonsMultipartFile的方法直接寫文件(注意這個時候) file.transferTo(new File(realPath +"/"+ filename)); //給editormd進行回調 JSONObject res = new JSONObject(); res.put("url","/upload/"+month+"/"+ filename); res.put("success", 1); res.put("message", "upload success!"); return res; } 

三、解決文件回顯顯示的問題,設置虛擬目錄映射!在咱們本身拓展的MvcConfig中進行配置便可!

@Configuration public class MyMvcConfig implements WebMvcConfigurer { // 文件保存在真實目錄/upload/下, // 訪問的時候使用虛路徑/upload,好比文件名爲1.png,就直接/upload/1.png就ok了。 @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/upload/**") .addResourceLocations("file:"+System.getProperty("user.dir")+"/upload/"); } } 

表情包問題

本身手動下載,emoji 表情包,放到圖片路徑下:

修改editormd.js文件

// Emoji graphics files url path editormd.emoji = { path : "../editormd/plugins/emoji-dialog/emoji/", ext : ".png" }; 

16.五、文章展現

一、Controller 中增長方法

@GetMapping("/{id}") public String show(@PathVariable("id") int id,Model model){ Article article = articleMapper.getArticleById(id); model.addAttribute("article",article); return "article"; } 

二、編寫頁面 article.html

<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <title th:text="${article.title}"></title> </head> <body> <div> <!--文章頭部信息:標題,做者,最後更新日期,導航--> <h2 style="margin: auto 0" th:text="${article.title}"></h2> 做者:<span style="float: left" th:text="${article.author}"></span> <!--文章主體內容--> <div id="doc-content"> <textarea style="display:none;" placeholder="markdown" th:text="${article.content}"></textarea> </div> </div> <link rel="stylesheet" th:href="@{/editormd/css/editormd.preview.css}" /> <script th:src="@{/editormd/lib/jquery.min.js}"></script> <script th:src="@{/editormd/lib/marked.min.js}"></script> <script th:src="@{/editormd/lib/prettify.min.js}"></script> <script th:src="@{/editormd/lib/raphael.min.js}"></script> <script th:src="@{/editormd/lib/underscore.min.js}"></script> <script th:src="@{/editormd/lib/sequence-diagram.min.js}"></script> <script th:src="@{/editormd/lib/flowchart.min.js}"></script> <script th:src="@{/editormd/lib/jquery.flowchart.min.js}"></script> <script th:src="@{/editormd/editormd.js}"></script> <script type="text/javascript"> var testEditor; $(function () { testEditor = editormd.markdownToHTML("doc-content", {//注意:這裏是上面DIV的id htmlDecode: "style,script,iframe", emoji: true, taskList: true, tocm: true, tex: true, // 默認不解析 flowChart: true, // 默認不解析 sequenceDiagram: true, // 默認不解析 codeFold: true });}); </script> </body> </html> 

重啓項目,訪問進行測試!大功告成!

小結:

有了富文本編輯器,咱們網站的功能就會又多一項,你們到了這裏徹底能夠有時間寫一個屬於本身的博客網站了,根據所學的知識是徹底沒有任何問題的!

1七、SpringBoot:Dubbo和Zookeeper集成

分佈式理論

17.一、什麼是分佈式系統?

在《分佈式系統原理與範型》一書中有以下定義:「分佈式系統是若干獨立計算機的集合,這些計算機對於用戶來講就像單個相關係統」;

分佈式系統是由一組經過網絡進行通訊、爲了完成共同的任務而協調工做的計算機節點組成的系統。分佈式系統的出現是爲了用廉價的、普通的機器完成單個計算機沒法完成的計算、存儲任務。其目的是利用更多的機器,處理更多的數據

分佈式系統(distributed system)是創建在網絡之上的軟件系統。

首先須要明確的是,只有當單個節點的處理能力沒法知足日益增加的計算、存儲任務的時候,且硬件的提高(加內存、加磁盤、使用更好的CPU)高昂到得不償失的時候,應用程序也不能進一步優化的時候,咱們才須要考慮分佈式系統。由於,分佈式系統要解決的問題自己就是和單機系統同樣的,而因爲分佈式系統多節點、經過網絡通訊的拓撲結構,會引入不少單機系統沒有的問題,爲了解決這些問題又會引入更多的機制、協議,帶來更多的問題。。。

17.二、Dubbo文檔

隨着互聯網的發展,網站應用的規模不斷擴大,常規的垂直應用架構已沒法應對,分佈式服務架構以及流動計算架構勢在必行,急需一個治理系統確保架構有條不紊的演進。

在Dubbo的官網文檔有這樣一張圖

**單一應用架構****

當網站流量很小時,只需一個應用,將全部功能都部署在一塊兒,以減小部署節點和成本。此時,用於簡化增刪改查工做量的數據訪問框架(ORM)是關鍵。

適用於小型網站,小型管理系統,將全部功能都部署到一個功能裏,簡單易用。

缺點:

一、性能擴展比較難

二、協同開發問題

三、不利於升級維護

****垂直應用架構******

當訪問量逐漸增大,單一應用增長機器帶來的加速度愈來愈小,將應用拆成互不相干的幾個應用,以提高效率。此時,用於加速前端頁面開發的Web框架(MVC)是關鍵。

經過切分業務來實現各個模塊獨立部署,下降了維護和部署的難度,團隊各司其職更易管理,性能擴展也更方便,更有針對性。

缺點:公用模塊沒法重複利用,開發性的浪費

******分佈式服務架構********

當垂直應用愈來愈多,應用之間交互不可避免,將核心業務抽取出來,做爲獨立的服務,逐漸造成穩定的服務中心,使前端應用能更快速的響應多變的市場需求。此時,用於提升業務複用及整合的分佈式服務框架(RPC)是關鍵。

********流動計算架構**********

當服務愈來愈多,容量的評估,小服務資源的浪費等問題逐漸顯現,此時需增長一個調度中心基於訪問壓力實時管理集羣容量,提升集羣利用率。此時,用於提升機器利用率的資源調度和治理中心(SOA)[ Service Oriented Architecture]是關鍵。

什麼是RPC

RPC【Remote Procedure Call】是指遠程過程調用,是一種進程間通訊方式,他是一種技術的思想,而不是規範。它容許程序調用另外一個地址空間(一般是共享網絡的另外一臺機器上)的過程或函數,而不用程序員顯式編碼這個遠程調用的細節。即程序員不管是調用本地的仍是遠程的函數,本質上編寫的調用代碼基本相同。

也就是說兩臺服務器A,B,一個應用部署在A服務器上,想要調用B服務器上應用提供的函數/方法,因爲不在一個內存空間,不能直接調用,須要經過網絡來表達調用的語義和傳達調用的數據。爲何要用RPC呢?就是沒法在一個進程內,甚至一個計算機內經過本地調用的方式完成的需求,好比不一樣的系統間的通信,甚至不一樣的組織間的通信,因爲計算能力須要橫向擴展,須要在多臺機器組成的集羣上部署應用。RPC就是要像調用本地的函數同樣去調遠程函數;

推薦閱讀文章:https://www.jianshu.com/p/2accc2840a1b

RPC基本原理

步驟解析:

RPC兩個核心模塊:通信,序列化。

測試環境搭建

********Dubbo**********

Apache Dubbo |ˈdʌbəʊ| 是一款高性能、輕量級的開源Java RPC框架,它提供了三大核心能力:面向接口的遠程方法調用,智能容錯和負載均衡,以及服務自動註冊和發現。

dubbo官網 http://dubbo.apache.org/zh-cn/index.html

1.瞭解Dubbo的特性

2.查看官方文檔

dubbo基本概念

服務提供者(Provider):暴露服務的服務提供方,服務提供者在啓動時,向註冊中心註冊本身提供的服務。

服務消費者(Consumer):調用遠程服務的服務消費方,服務消費者在啓動時,向註冊中心訂閱本身所需的服務,服務消費者,從提供者地址列表中,基於軟負載均衡算法,選一臺提供者進行調用,若是調用失敗,再選另外一臺調用。

註冊中心(Registry):註冊中心返回服務提供者地址列表給消費者,若是有變動,註冊中心將基於長鏈接推送變動數據給消費者

監控中心(Monitor):服務消費者和提供者,在內存中累計調用次數和調用時間,定時每分鐘發送一次統計數據到監控中心

調用關係說明

l 服務容器負責啓動,加載,運行服務提供者。

l 服務提供者在啓動時,向註冊中心註冊本身提供的服務。

l 服務消費者在啓動時,向註冊中心訂閱本身所需的服務。

l 註冊中心返回服務提供者地址列表給消費者,若是有變動,註冊中心將基於長鏈接推送變動數據給消費者。

l 服務消費者,從提供者地址列表中,基於軟負載均衡算法,選一臺提供者進行調用,若是調用失敗,再選另外一臺調用。

l 服務消費者和提供者,在內存中累計調用次數和調用時間,定時每分鐘發送一次統計數據到監控中心。

********Dubbo環境搭建**********

點進dubbo官方文檔,推薦咱們使用Zookeeper 註冊中心

什麼是zookeeper呢?能夠查看官方文檔

********Window下安裝zookeeper**********

一、下載zookeeper :地址, 咱們下載3.4.14 , 最新版!解壓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節點的值

咱們再來查看一下節點

********window下安裝dubbo-admin**********

dubbo自己並非一個服務軟件。它其實就是一個jar包,可以幫你的java程序鏈接到zookeeper,並利用zookeeper消費、提供服務。

可是爲了讓用戶更好的管理監控衆多的dubbo服務,官方提供了一個可視化的監控程序dubbo-admin,不過這個監控即便不裝也不影響使用。

咱們這裏來安裝一下:

一、下載dubbo-admin

地址 :https://github.com/apache/dubbo-admin/tree/master

二、解壓進入目錄

修改 dubbo-admin\src\main\resources \application.properties 指定zookeeper地址

server.port=7001spring.velocity.cache=falsespring.velocity.charset=UTF-8spring.velocity.layout-url=/templates/default.vmspring.messages.fallback-to-system-locale=falsespring.messages.basename=i18n/messagespring.root.password=rootspring.guest.password=guestdubbo.registry.address=zookeeper://127.0.0.1:2181 

三、在項目目錄下打包dubbo-admin

mvn clean package -Dmaven.test.skip=true 

第一次打包的過程有點慢,須要耐心等待!直到成功!

四、執行 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;

登陸成功後,查看界面

安裝完成!

17.三、SpringBoot + Dubbo + zookeeper

********框架搭建**********

1. 啓動zookeeper !

2. IDEA建立一個空項目;

3.建立一個模塊,實現服務提供者:provider-server , 選擇web依賴便可

4.項目建立完畢,咱們寫一個服務,好比賣票的服務;

編寫接口

package com.kuang.provider.service; public interface TicketService { public String getTicket(); } 

編寫實現類

package com.kuang.provider.service; public class TicketServiceImpl implements TicketService { @Override public String getTicket() { return "《狂神說Java》"; } } 

5.建立一個模塊,實現服務消費者:consumer-server , 選擇web依賴便可

6.項目建立完畢,咱們寫一個服務,好比用戶的服務;

編寫service

package com.kuang.consumer.service; public class UserService { //咱們須要去拿去註冊中心的服務 } 

需求:如今咱們的用戶想使用買票的服務,這要怎麼弄呢 ?

********服務提供者**********

一、將服務提供者註冊到註冊中心,咱們須要整合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> 

二、在springboot配置文件中配置dubbo相關屬性!

#當前應用名字 dubbo.application.name=provider-server #註冊中心地址 dubbo.registry.address=zookeeper://127.0.0.1:2181 #掃描指定包下服務 dubbo.scan.base-packages=com.kuang.provider.service 

三、在service的實現類中配置服務註解,發佈服務!注意導包問題

import org.apache.dubbo.config.annotation.Service; import org.springframework.stereotype.Component; @Service //將服務發佈出去 @Component //放在容器中 public class TicketServiceImpl implements TicketService { @Override public String getTicket() { return "《狂神說Java》"; } } 

邏輯理解 :應用啓動起來,dubbo就會掃描指定的包下帶有@component註解的服務,將它發佈在指定的註冊中心中!

********服務消費者**********

一、導入依賴,和以前的依賴同樣;

<!--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> 

二、配置參數

#當前應用名字 dubbo.application.name=consumer-server #註冊中心地址 dubbo.registry.address=zookeeper://127.0.0.1:2181 

3. 原本正常步驟是須要將服務提供者的接口打包,而後用pom文件導入,咱們這裏使用簡單的方式,直接將服務的接口拿過來,路徑必須保證正確,即和服務提供者相同;

4. 完善消費者的服務類

package com.kuang.consumer.service; import com.kuang.provider.service.TicketService; import org.apache.dubbo.config.annotation.Reference; import org.springframework.stereotype.Service; @Service //注入到容器中 public class UserService { @Reference //遠程引用指定的服務,他會按照全類名進行匹配,看誰給註冊中心註冊了這個全類名 TicketService ticketService; public void bugTicket(){ String ticket = ticketService.getTicket(); System.out.println("在註冊中心買到"+ticket); } } 

5. 測試類編寫;

@RunWith(SpringRunner.class) @SpringBootTest public class ConsumerServerApplicationTests { @Autowired UserService userService; @Test public void contextLoads() { userService.bugTicket(); } } 

********啓動測試**********

1. 開啓zookeeper

2. 打開dubbo-admin實現監控【能夠不用作】

3. 開啓服務者

4. 消費者消費測試,結果:

監控中心 :

ok , 這就是SpingBoot + dubbo + zookeeper實現分佈式開發的應用,其實就是一個服務拆分的思想;

1八、SpringBoot:集成SpringSecurity

18.一、安全簡介

在 Web 開發中,安全一直是很是重要的一個方面。安全雖然屬於應用的非功能性需求,可是應該在應用開發的初期就考慮進來。若是在應用開發的後期才考慮安全的問題,就可能陷入一個兩難的境地:一方面,應用存在嚴重的安全漏洞,沒法知足用戶的要求,並可能形成用戶的隱私數據被攻擊者竊取;另外一方面,應用的基本架構已經肯定,要修復安全漏洞,可能須要對系統的架構作出比較重大的調整,於是須要更多的開發時間,影響應用的發佈進程。所以,從應用開發的第一天就應該把安全相關的因素考慮進來,並在整個應用的開發過程當中。

市面上存在比較有名的:Shiro,Spring Security !

這裏須要闡述一下的是,每個框架的出現都是爲了解決某一問題而產生了,那麼Spring Security框架的出現是爲了解決什麼問題呢?

首先咱們看下它的官網介紹:Spring Security官網地址

Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications.

Spring Security is a framework that focuses on providing both authentication and authorization to Java applications. Like all Spring projects, the real power of Spring Security is found in how easily it can be extended to meet custom requirements

Spring Security是一個功能強大且高度可定製的身份驗證和訪問控制框架。它其實是保護基於spring的應用程序的標準。

Spring Security是一個框架,側重於爲Java應用程序提供身份驗證和受權。與全部Spring項目同樣,Spring安全性的真正強大之處在於它能夠輕鬆地擴展以知足定製需求

從官網的介紹中能夠知道這是一個權限框架。想咱們以前作項目是沒有使用框架是怎麼控制權限的?對於權限 通常會細分爲功能權限,訪問權限,和菜單權限。代碼會寫的很是的繁瑣,冗餘。

怎麼解決以前寫權限代碼繁瑣,冗餘的問題,一些主流框架就應運而生而Spring Scecurity就是其中的一種。

Spring 是一個很是流行和成功的 Java 應用開發框架。Spring Security 基於 Spring 框架,提供了一套 Web 應用安全性的完整解決方案。通常來講,Web 應用的安全性包括用戶認證(Authentication)和用戶受權(Authorization)兩個部分。用戶認證指的是驗證某個用戶是否爲系統中的合法主體,也就是說用戶可否訪問該系統。用戶認證通常要求用戶提供用戶名和密碼。系統經過校驗用戶名和密碼來完成認證過程。用戶受權指的是驗證某個用戶是否有權限執行某個操做。在一個系統中,不一樣用戶所具備的權限是不一樣的。好比對一個文件來講,有的用戶只能進行讀取,而有的用戶能夠進行修改。通常來講,系統會爲不一樣的用戶分配不一樣的角色,而每一個角色則對應一系列的權限。

對於上面提到的兩種應用情景,Spring Security 框架都有很好的支持。在用戶認證方面,Spring Security 框架支持主流的認證方式,包括 HTTP 基本認證、HTTP 表單驗證、HTTP 摘要認證、OpenID 和 LDAP 等。在用戶受權方面,Spring Security 提供了基於角色的訪問控制和訪問控制列表(Access Control List,ACL),能夠對應用中的領域對象進行細粒度的控制。

18.二、實戰測試

18.2.一、實驗環境搭建

一、新建一個初始的springboot項目web模塊,thymeleaf模塊

二、導入靜態資源

welcome.html |views |level1 1.html 2.html 3.html |level2 1.html 2.html 3.html |level3 1.html 2.html 3.html Login.html 

三、controller跳轉!

package com.kuang.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class RouterController { @RequestMapping({"/","/index"}) public String index(){ return "index"; } @RequestMapping("/toLogin") public String toLogin(){ return "views/login"; } @RequestMapping("/level1/{id}") public String level1(@PathVariable("id") int id){ return "views/level1/"+id; } @RequestMapping("/level2/{id}") public String level2(@PathVariable("id") int id){ return "views/level2/"+id; } @RequestMapping("/level3/{id}") public String level3(@PathVariable("id") int id){ return "views/level3/"+id; } } 

四、測試實驗環境是否OK!

18.2.二、認識SpringSecurity

Spring Security 是針對Spring項目的安全框架,也是Spring Boot底層安全模塊默認的技術選型,他能夠實現強大的Web安全控制,對於安全控制,咱們僅須要引入spring-boot-starter-security 模塊,進行少許的配置,便可實現強大的安全管理!

記住幾個類:

  • WebSecurityConfigurerAdapter:自定義Security策略
  • AuthenticationManagerBuilder:自定義認證策略
  • @EnableWebSecurity:開啓WebSecurity模式

Spring Security的兩個主要目標是 「認證」 和 「受權」(訪問控制)。

「認證」(Authentication)

身份驗證是關於驗證您的憑據,如用戶名/用戶ID和密碼,以驗證您的身份。

身份驗證一般經過用戶名和密碼完成,有時與身份驗證因素結合使用。

「受權」 (Authorization)

受權發生在系統成功驗證您的身份後,最終會授予您訪問資源(如信息,文件,數據庫,資金,位置,幾乎任何內容)的徹底權限。

這個概念是通用的,而不是隻在Spring Security 中存在。

18.2.三、認證和受權

目前,咱們的測試環境,是誰均可以訪問的,咱們使用 Spring Security 增長上認證和受權的功能

一、引入 Spring Security 模塊

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-security</artifactId>
</dependency>

二、編寫 Spring Security 配置類

參考官網:https://spring.io/projects/spring-security

查看咱們本身項目中的版本,找到對應的幫助文檔:

https://docs.spring.io/spring-security/site/docs/5.3.0.RELEASE/reference/html5 #servlet-applications 8.16.4

三、編寫基礎配置類

package com.kuang.config; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @EnableWebSecurity // 開啓WebSecurity模式 public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { } } 

四、定製請求的受權規則

@Override protected void configure(HttpSecurity http) throws Exception { // 定製請求的受權規則 // 首頁全部人能夠訪問 http.authorizeRequests().antMatchers("/").permitAll() .antMatchers("/level1/**").hasRole("vip1") .antMatchers("/level2/**").hasRole("vip2") .antMatchers("/level3/**").hasRole("vip3"); } 

五、測試一下:發現除了首頁都進不去了!由於咱們目前沒有登陸的角色,由於請求須要登陸的角色擁有對應的權限才能夠!

六、在configure()方法中加入如下配置,開啓自動配置的登陸功能!

// 開啓自動配置的登陸功能 // /login 請求來到登陸頁 // /login?error 重定向到這裏表示登陸失敗 http.formLogin(); 

七、測試一下:發現,沒有權限的時候,會跳轉到登陸的頁面!

八、查看剛纔登陸頁的註釋信息;

咱們能夠定義認證規則,重寫configure(AuthenticationManagerBuilder auth)方法

//定義認證規則 @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //在內存中定義,也能夠在jdbc中去拿.... auth.inMemoryAuthentication() .withUser("kuangshen").password("123456").roles("vip2","vip3") .and() .withUser("root").password("123456").roles("vip1","vip2","vip3") .and() .withUser("guest").password("123456").roles("vip1","vip2"); } 

九、測試,咱們能夠使用這些帳號登陸進行測試!發現會報錯!

There is no PasswordEncoder mapped for the id 「null」

十、緣由,咱們要將前端傳過來的密碼進行某種方式加密,不然就沒法登陸,修改代碼

//定義認證規則 @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //在內存中定義,也能夠在jdbc中去拿.... //Spring security 5.0中新增了多種加密方式,也改變了密碼的格式。 //要想咱們的項目還可以正常登錄,須要修改一下configure中的代碼。咱們要將前端傳過來的密碼進行某種方式加密 //spring security 官方推薦的是使用bcrypt加密方式。 auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()) .withUser("kuangshen").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip3") .and() .withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3") .and() .withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2"); } 

十一、測試,發現,登陸成功,而且每一個角色只能訪問本身認證下的規則!搞定

18.2.四、權限控制和註銷

一、開啓自動配置的註銷的功能

//定製請求的受權規則 @Override protected void configure(HttpSecurity http) throws Exception { //.... //開啓自動配置的註銷的功能 // /logout 註銷請求 http.logout(); } 

二、咱們在前端,增長一個註銷的按鈕,index.html 導航欄中

<a class="item" th:href="@{/logout}"> <i class="address card icon"></i> 註銷 </a> 

三、咱們能夠去測試一下,登陸成功後點擊註銷,發現註銷完畢會跳轉到登陸頁面!

四、可是,咱們想讓他註銷成功後,依舊能夠跳轉到首頁,該怎麼處理呢?

// .logoutSuccessUrl("/"); 註銷成功來到首頁 http.logout().logoutSuccessUrl("/"); 

五、測試,註銷完畢後,發現跳轉到首頁OK

六、咱們如今又來一個需求:用戶沒有登陸的時候,導航欄上只顯示登陸按鈕,用戶登陸以後,導航欄能夠顯示登陸的用戶信息及註銷按鈕!還有就是,好比kuangshen這個用戶,它只有 vip2,vip3功能,那麼登陸則只顯示這兩個功能,而vip1的功能菜單不顯示!這個就是真實的網站狀況了!該如何作呢?

咱們須要結合thymeleaf中的一些功能

sec:authorize="isAuthenticated()":是否定證登陸!來顯示不一樣的頁面

Maven依賴:

<!-- https://mvnrepository.com/artifact/org.thymeleaf.extras/thymeleaf-extras-springsecurity4 --> <dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity5</artifactId> <version>3.0.4.RELEASE</version> </dependency> 

七、修改咱們的 前端頁面

  1. 導入命名空間

  2. xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"
  3. 修改導航欄,增長認證判斷

  4. <!--登陸註銷--> <div class="right menu"> <!--若是未登陸--> <div sec:authorize="!isAuthenticated()"> <a class="item" th:href="@{/login}"> <i class="address card icon"></i> 登陸 </a> </div> <!--若是已登陸--> <div sec:authorize="isAuthenticated()"> <a class="item"> <i class="address card icon"></i> 用戶名:<span sec:authentication="principal.username"></span> 角色:<span sec:authentication="principal.authorities"></span> </a> </div> <div sec:authorize="isAuthenticated()"> <a class="item" th:href="@{/logout}"> <i class="address card icon"></i> 註銷 </a> </div> </div> 

八、重啓測試,咱們能夠登陸試試看,登陸成功後確實,顯示了咱們想要的頁面;

九、若是註銷404了,就是由於它默認防止csrf跨站請求僞造,由於會產生安全問題,咱們能夠將請求改成post表單提交,或者在spring security中關閉csrf功能;咱們試試:在 配置中增長 http.csrf().disable();

http.csrf().disable();//關閉csrf功能:跨站請求僞造,默認只能經過post方式提交logout請求 http.logout().logoutSuccessUrl("/"); 

十、咱們繼續將下面的角色功能塊認證完成!

<!-- sec:authorize="hasRole('vip1')" --> <div class="column" sec:authorize="hasRole('vip1')"> <div class="ui raised segment"> <div class="ui"> <div class="content"> <h5 class="content">Level 1</h5> <hr> <div><a th:href="@{/level1/1}"><i class="bullhorn icon"></i> Level-1-1</a></div> <div><a th:href="@{/level1/2}"><i class="bullhorn icon"></i> Level-1-2</a></div> <div><a th:href="@{/level1/3}"><i class="bullhorn icon"></i> Level-1-3</a></div> </div> </div> </div> </div> <div class="column" sec:authorize="hasRole('vip2')"> <div class="ui raised segment"> <div class="ui"> <div class="content"> <h5 class="content">Level 2</h5> <hr> <div><a th:href="@{/level2/1}"><i class="bullhorn icon"></i> Level-2-1</a></div> <div><a th:href="@{/level2/2}"><i class="bullhorn icon"></i> Level-2-2</a></div> <div><a th:href="@{/level2/3}"><i class="bullhorn icon"></i> Level-2-3</a></div> </div> </div> </div> </div> <div class="column" sec:authorize="hasRole('vip3')"> <div class="ui raised segment"> <div class="ui"> <div class="content"> <h5 class="content">Level 3</h5> <hr> <div><a th:href="@{/level3/1}"><i class="bullhorn icon"></i> Level-3-1</a></div> <div><a th:href="@{/level3/2}"><i class="bullhorn icon"></i> Level-3-2</a></div> <div><a th:href="@{/level3/3}"><i class="bullhorn icon"></i> Level-3-3</a></div> </div> </div> </div> </div> 

十一、測試一下!

十二、權限控制和註銷搞定!

18.2.五、記住我

如今的狀況,咱們只要登陸以後,關閉瀏覽器,再登陸,就會讓咱們從新登陸,可是不少網站的狀況,就是有一個記住密碼的功能,這個該如何實現呢?很簡單

一、開啓記住我功能

//定製請求的受權規則 @Override protected void configure(HttpSecurity http) throws Exception { //。。。。。。。。。。。 //記住我 http.rememberMe(); } 

二、咱們再次啓動項目測試一下,發現登陸頁多了一個記住我功能,咱們登陸以後關閉 瀏覽器,而後從新打開瀏覽器訪問,發現用戶依舊存在!

思考:如何實現的呢?其實很是簡單

咱們能夠查看瀏覽器的cookie

三、咱們點擊註銷的時候,能夠發現,spring security 幫咱們自動刪除了這個 cookie


四、結論:登陸成功後,將cookie發送給瀏覽器保存,之後登陸帶上這個cookie,只要經過檢查就能夠免登陸了。若是點擊註銷,則會刪除這個cookie,具體的原理咱們在JavaWeb階段都講過了,這裏就不在多說了!

18.2.六、定製登陸頁

如今這個登陸頁面都是spring security 默認的,怎麼樣能夠使用咱們本身寫的Login界面呢?

一、在剛纔的登陸頁配置後面指定 loginpage

http.formLogin().loginPage("/toLogin"); 

二、而後前端也須要指向咱們本身定義的 login請求

<a class="item" th:href="@{/toLogin}"> <i class="address card icon"></i> 登陸 </a> 

三、咱們登陸,須要將這些信息發送到哪裏,咱們也須要配置,login.html 配置提交請求及方式,方式必須爲post:

在 loginPage()源碼中的註釋上有寫明:

<form th:action="@{/login}" method="post"> <div class="field"> <label>Username</label> <div class="ui left icon input"> <input type="text" placeholder="Username" name="username"> <i class="user icon"></i> </div> </div> <div class="field"> <label>Password</label> <div class="ui left icon input"> <input type="password" name="password"> <i class="lock icon"></i> </div> </div> <input type="submit" class="ui blue submit button"/> </form> 

四、這個請求提交上來,咱們還須要驗證處理,怎麼作呢?咱們能夠查看formLogin()方法的源碼!咱們配置接收登陸的用戶名和密碼的參數!

http.formLogin()
  .usernameParameter("username") .passwordParameter("password") .loginPage("/toLogin") .loginProcessingUrl("/login"); // 登錄表單提交請求 

五、在登陸頁增長記住個人多選框

<input type="checkbox" name="remember"> 記住我 

六、後端驗證處理!

//定製記住個人蔘數! http.rememberMe().rememberMeParameter("remember"); 

七、測試,OK

18.三、完整配置代碼

package com.kuang.config; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { //定製請求的受權規則 @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().antMatchers("/").permitAll() .antMatchers("/level1/**").hasRole("vip1") .antMatchers("/level2/**").hasRole("vip2") .antMatchers("/level3/**").hasRole("vip3"); //開啓自動配置的登陸功能:若是沒有權限,就會跳轉到登陸頁面! // /login 請求來到登陸頁 // /login?error 重定向到這裏表示登陸失敗 http.formLogin() .usernameParameter("username") .passwordParameter("password") .loginPage("/toLogin") .loginProcessingUrl("/login"); // 登錄表單提交請求 //開啓自動配置的註銷的功能 // /logout 註銷請求 // .logoutSuccessUrl("/"); 註銷成功來到首頁 http.csrf().disable();//關閉csrf功能:跨站請求僞造,默認只能經過post方式提交logout請求 http.logout().logoutSuccessUrl("/"); //記住我 http.rememberMe().rememberMeParameter("remember"); } //定義認證規則 @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //在內存中定義,也能夠在jdbc中去拿.... //Spring security 5.0中新增了多種加密方式,也改變了密碼的格式。 //要想咱們的項目還可以正常登錄,須要修改一下configure中的代碼。咱們要將前端傳過來的密碼進行某種方式加密 //spring security 官方推薦的是使用bcrypt加密方式。 auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()) .withUser("kuangshen").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip3") .and() .withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3") .and() .withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2"); } } 

去進行一波源碼分析!**

相關文章
相關標籤/搜索