SpringBoot學習筆記

NO IMAGE

positive:正確的,積極的

negative:否認的,消極的

navigate:導航

navicat:資料庫連線軟體

candidate:候選者

detect:檢測

indicate:表明

indentation:縮排

feature:特徵

1.前言

該筆記僅僅只是記錄自己在觀看SpringBoot視訊的學習過程中,我認為自己需要記憶的,自己記不住的一些知識點。所以筆記可能比較鬆散簡單,有關任何SpringBoot的知識,歡迎大家留貼討論。

2.SpringBoot專案的建立

通過idea的Spring Initialzer來快速選擇需要的模組功能,然後idea會自動生成SpringBoot專案


3.關於SpirngBoot的pom檔案繼承的兩種寫法

3.1 繼承方式
因pom檔案是單繼承的,所以侷限性比較大
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
3.2 匯入方式
這種方式就非常好了,這樣pom檔案還可以繼續繼承一個自己公司的
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.0.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

4.SpringBoot配置檔案的載入順序與優先順序

.properties的優先順序大於.yml

.properties的配置和.yml的配置全部都會載入,高優先順序會覆蓋低優先順序,並且形成互補配置

預設會先載入不帶profile標識的配置檔案,然後如果預設配置檔案中有設定spring.profile.active屬性,則載入對應的配置檔案,注意,此時的配置檔案優先順序會比預設的高!!例如在application.yml中配置埠為8081,配置spring.profile.active為dev,那麼如果在application-dev.yml中如果配置了8082,那麼啟動時埠為8082

意思就是制定spring哪個配置檔案是活躍的,既然是活躍的,那麼優先順序肯定最高了!

優先順序1最大,4最小,所有位置的配置檔案都會載入,高優先順序會覆蓋低優先順序的值

  1. 專案根/config
  2. 專案根
  3. classpath:/config
  4. classpath:

還有一種在啟動springboot專案的時候指定配置項spring.config.location,這種是最屌的,可以覆蓋專案專案啟動後已經有的值。簡單的說就是可以不用修改專案程式碼,只要重啟指定配置檔案的位置,就可以了。這個位置可以是任何位置。一般運維會用用。

5.yml配置檔案的編寫、切換、讀取

5.1 語法格式

k: v 一個k加冒號然後空格然後再寫值,空格很重要!!

如果有多級的,另起一行然後再打幾個空格,打幾個空格無所謂,只要同一級的屬性對其即可!!

5.2 屬性值編寫與讀取

實體物件

@Data
@Component
@ConfigurationProperties("person")
public class Person {
private Integer age;
private String name1;
private String name2;
private String name3;
private Boolean isStudent;
private Double score;
private Date birthDay;
private String[] arr;
private List<String> list;
private Map<String,Object> map;
private String foo;
private String random;
}

yml配置

字面量:字串,數字,布林,日期等

複合型別:list,map

佔位符,隨機數的寫法

#字面量
person:
age: 18
#  字串無需加單雙引號,雙引號會進行轉義,單引號不會進行轉義(是啥就是啥)
name1: zhangsan1 \n lisi
name2: 'zhangsan3 \n lisi'
name3: "zhangsan2 \n lisi"
isStudent: false
score: 99.5
#  日期格式應該支援繫結很多種格式
birthDay: 2018/6/10 11:15:20
arr:
- v1
- v2
#  也可以像下面這種一行的寫法
list: [v1,v2,v3]
#  map的寫法和物件是差不多的
#  map:
#    key1: v1
#    key2: v2
map: {key1: v1,key2: v2}
#  佔位符的方式,可以獲取系統中其他的配置量
foo: ${person.name1} 今年 ${person.age}
#  自帶的簡單隨機數獲取
#  random: ${random.long}
#  random: ${random.int}
#  random: ${random.value}
#  random: ${random.int(10,20)}
random: ${random.uuid}

測試輸出

5.3 配置檔案相關注解

@ConfigurationProperties:可以進行屬性的批量配置,只需指定配置檔案的字首即可!注意,需要配置屬性的類,必須是一個元件,即必須標有@Component,或者在某個元件(配置類)的地方宣告瞭@EnableConfigurationProperties(加入該bean的class),該註解會自動把標有@ConfigurationProperties的類加入到spring容器進行管理。總結的說就是,@EnableConfigurationProperties註解會把該註解指定的類(如果該類標記有@ConfigurationProperties,即使該類不標記@Component)註冊到spring容器中,如果@EnableConfigurationProperties不指定class類,那麼他僅僅只是開啟這個功能,他會在spring容器中查詢各個帶有@ConfigurationProperties註解的元件使其生效。

@Configuration 
public class Config {
private Foo foo;
//如果一個元件他有且僅有一個建構函式,那麼不管他這個建構函式有幾個引數
//他會自動在容器中查詢所需要的元件進行組裝。
public Config(Foo foo){
this.foo=foo;
}
}

“`java
@Configuration
public class Config {

 //或者這種注入,我比較喜歡這種
@Autowired
private Foo foo;

}
“`

該註解還可以新增@Validated,那麼下面的屬性就會進行JSR303標準的校驗。

@Data
@Component
@ConfigurationProperties("person")
//開啟資料校驗,將配置檔案中的資料繫結到該類時會進行資料的校驗工作
@Validated
public class Person {
private Integer age;
//name1屬性必須是一個合法的email地址,否則會報錯
@Email
private String email;
}

@PropertySource@Value

同樣的@PropertySource也要在元件上才能生效!

第一個註解可以載入配置檔案,任意位置,任意名稱的配置檔案。

第二個註解可以對類的屬性值進行寫入,可以是常量,可以讀取當前容器所在環境中已存在的配置量,可以是SpEL表示式

@Data
@Component
@PropertySource("classpath:student.properties")
public class Student {
@Value("zhangsan")
private String name;
@Value("${student.age}")
private Integer age;
@Value("#{56 24.4}")
private Double score;
}
5.4 profile切換

編寫格式:application-{profile}.properties/yml

啟用方式: 在預設的application.properties/yml中設定spring.profile.active={profile},來指定

其他啟用方式:命令列,jar指定執行引數,設定系統變數等等有需要可以查

6.自動配置原理分析

SpringBoot開啟自動配置的關鍵入口是註解@EnableAutoConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}

該註解又有兩個註解,分別是:

@AutoConfigurationPackage:這個註解的作用是掃描@EnableAutoConfiguration註解所在的類的包下的所有子孫包,將標有@Component註解的主鍵掃描進ioc容器中。其底層實現也是通過@Import註解來實現

@Import(AutoConfigurationImportSelector.class):這個註解的作用是,將後邊選擇器所返回的類註冊到ioc容器中。

AutoConfigurationImportSelector:自動配置匯入選擇器,這個類是實現自動配置的核心,裡面有一個重要的方法,selectImports(),該方法返回字串陣列,該陣列其實就是需要註冊到spirng容器中的各種configuration類

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//核心方法,getCandidateConfigurations,獲取候選的配置類。
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations);
}

繼續點getCandidateConfigurations方法進去看:

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
//這裡就不再點進去看了,大概意思是指定配置檔案的位置,key值,classLoader等一些資訊
//其中getSpringFactoriesLoaderFactoryClass()方法返回的就是EnableAutoConfiguration的全類目,以該全類目為key
//然後在當前專案所有jar包的類路徑下得META-INF/spring.factories去匹配該key對應的value,即能夠被返回的所有的配置類的全類名
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories. If you "
"are using a custom packaging, make sure that file is correct.");
return configurations;
}
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}

以下是autoConfiguration包下的spring.factories

雖然是載入所有的jar包下得META-INF/spring.factories,但是因為前面已經指定了key鍵,也就是getSpringFactoriesLoaderFactoryClass()這個方法,返回的值為:EnableAutoConfiguration.class,該class對應的全類名,也就是key鍵就是:org.springframework.boot.autoconfigure.EnableAutoConfiguration

至此,所有的配置類全部掃描出來,但是到目前為止,這麼多配置類並不是都加入了ioc容器。SpringBoot還要再做一層處理。這一層處理就是判斷每一個配置類載入的條件,然後來決定是否加入ioc容器。這邊我以簡單的HttpEncodingAutoConfiguration配置類,來做介紹,其他配置類的載入條件是差不多的。

//配置類的標記
@Configuration
//將HttpEncodingProperties這個類開啟自動屬性的繫結,並加入到ioc容器中
@EnableConfigurationProperties(HttpEncodingProperties.class)
//這三個都是條件判斷的註解,只有滿足條件,那麼當前配置類才會生效,即被spring容器所管理
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
private final HttpEncodingProperties properties;
//當只有一個有參構造時,引數會自動從容器中獲取
public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {
this.properties = properties;
}
@Bean
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
return filter;
}
......省略
}

可以看到,一開始返回的那麼多xxxxAutoConfiguration類並不是全部都進行註冊,而是會加一些判斷,比如是否是web環境?是否有什麼什麼類已經存在?等等。通過這些限制條件,spring會自動幫我們把我們當前專案環境所需要的配置自動載入!!也就是SpringBoot會自動幫我們檢測當前專案環境,都有哪些jar包被匯入,jdk版本等等,然後來決定要註冊哪些xxxAutoConfiguration,並註冊這些自動配置類裡面配置的元件,從而實現了自動配置。

一般來說每個xxxxAutoConfiguration會對應一個xxxxProperties的實體類(該實體類標有@ConfigurationProperties,並被xxxxAutoConfiguration類標有的@EnableConfigurationProperties所指定),即該xxxxProperties實體類被納入了容器進行管理。

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

作用:必須是@Conditional指定的條件成立,才給容器中新增元件,配置配裡面的所有內容才生效;

@Conditional擴充套件註解作用(判斷是否滿足當前指定條件)
@ConditionalOnJava系統的java版本是否符合要求
@ConditionalOnBean容器中存在指定Bean;
@ConditionalOnMissingBean容器中不存在指定Bean;
@ConditionalOnExpression滿足SpEL表示式指定
@ConditionalOnClass系統中有指定的類
@ConditionalOnMissingClass系統中沒有指定的類
@ConditionalOnSingleCandidate容器中只有一個指定的Bean,或者這個Bean是首選Bean
@ConditionalOnProperty系統中指定的屬性是否有指定的值
@ConditionalOnResource類路徑下是否存在指定資原始檔
@ConditionalOnWebApplication當前是web環境
@ConditionalOnNotWebApplication當前不是web環境
@ConditionalOnJndiJNDI存在指定項

可以在配置檔案中設定debug=true,那麼在springboot啟動的時候就會列印出哪些類被配置了!

7.日誌

7.1 市面上的日誌選擇

JCL、slf4j、Jboss-logging、JUL、log4j、logback、log4j2、….

日誌門面 (日誌的抽象層)日誌實現
JCL(Jakarta Commons Logging) SLF4j(Simple Logging Facade for Java) jboss-loggingLog4j JUL(java.util.logging) Log4j2 Logback

log4j,Logback,SLF4J都是同一個人寫的!!!

左邊選一個門面(抽象層)、右邊來選一個實現;

日誌門面: SLF4J;

日誌實現:Logback;

Spring框架預設是用JCL

SpringBoot框架預設是用SLF4J和Logback

開發的時候,日誌記錄方法的呼叫,不應該來直接呼叫日誌的實現類,而是呼叫日誌抽象層裡面的方法;

7.2 官方slf4j入門案列
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(HelloWorld.class);
logger.info("Hello World");
}
}
7.3 SLF4J官方介紹

每一個日誌的實現框架都有自己的配置檔案。使用slf4j以後,配置檔案還是做成日誌實現框架自己本身的配置檔案;

7.4 如何讓系統中所有的日誌都統一到slf4j

如何統一專案中各個框架使用不同日誌的問題,讓各個框架全部統一使用SLF4J,這個官網的介紹

  1. 將系統中其他日誌框架先排除出去;
  2. 用中間包來替換原有的日誌框架;
  3. 我們匯入slf4j其他的實現(xxxx-over-slf4j.jar)
7.5 SpringBoot日誌關係

主要起作用的是這個依賴裡面的logging依賴

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

底層依賴關係

總結:

​ 1)、SpringBoot底層也是使用slf4j logback的方式進行日誌記錄

​ 2)、SpringBoot也把其他的日誌都替換成了slf4j;

​ 3)、中間替換包(類名和包名和原來一模一樣!但是裡面的內容不一樣,類似tk.mapper)

​ 4)、如果我們要引入其他框架,一定要把這個框架的預設日誌依賴移除掉.

​ Spring框架用的是commons-logging;

        <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>

SpringBoot能自動適配所有的日誌,而且底層使用slf4j logback的方式記錄日誌,引入其他框架的時候,只需要把這個框架依賴的日誌框架排除掉即可

7.6 SpirngBoot日誌使用
logging.filelogging.pathExampleDescription
(none)(none)只在控制檯輸出
指定檔名(none)my.log輸出日誌到my.log檔案
(none)指定目錄/var/log輸出到指定目錄的 spring.log 檔案中
#logging.level是一個map,key為包名,value為日誌輸出的級別
#如果沒有指定預設級別,則用SpringBoot預設的,info級別
#file和path屬性指定其一就可以了,如果都指定中生效file
logging:
#map的寫法也可以這樣寫
#  level: {com: warn,com.yckj: error}
level:
com: error
com.yckj: trace
#   不指定路勁,"my.log",只指定檔名,則預設在專案的根目錄生成該日誌檔案
#   如果帶斜槓,"/my.log",則在專案所在的碟符根目錄生成
#   或者使用全路勁,"E:/springlog/my.log"
#  file: E:/springlog/my.log
#指定路勁,然後檔名為SpringBoot預設的"spring.log"
#如果不加/,那麼就是在當前專案的根目錄建立資料夾,並在該資料夾內建立spring.log檔案
#如果加了/,那麼就是以專案所在的磁碟為根目錄,建立資料夾
path: spring/log
#  注意,在用yml寫pattern的時候請使用雙引號或者單引號擴起來,不然會解析不了
pattern:
#  設定控制檯輸出的樣式
console: '%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n'
#    這是輸出到檔案中的樣式
file: '%d{yyyy-MM-dd} === [%thread] === %-5level === %logger{50} ==== %msg%n'
@SpringBootApplication
@Slf4j
public class SpringBootLogApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootLogApplication.class, args);
//日誌的級別  trace<debug<info<warn<error
log.trace("這是trace");
log.debug("這是debug");
//SpringBoot預設級別是info的,所以trace和debug資訊不輸出
//設定為什麼級別,就是該級別及更高階別的日誌才會被輸出
log.info("這是info");
log.warn("這是warn");
log.error("這是error");
}
}

日誌輸出格式:
%d:表示日期時間,
%thread:表示執行緒名,
%-5level:級別從左顯示5個字元寬度
%logger{50}: 表示logger名字最長50個字元,否則按照句點分割。
%msg:日誌訊息,
%n:換行符

以上的配置只是簡單的設定下日誌的輸出配置,若要定製,則需要自己加入相對應的日誌實現框架的配置檔案。SpringBoot下非常的簡單,這是官方文件的說明。

帶”-spring”標識的配置檔案可以設定日誌在不同環境下輸出的格式。

log4j2.xml:直接就被日誌框架識別了;

log4j2-spring.xml:日誌框架就不直接載入日誌的配置項,由SpringBoot解析日誌配置,可以使用SpringBoot的高階Profile功能

例如:

<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<!--
日誌輸出格式:
%d表示日期時間,
%thread表示執行緒名,
%-5level:級別從左顯示5個字元寬度
%logger{50} 表示logger名字最長50個字元,否則按照句點分割。 
%msg:日誌訊息,
%n是換行符
-->
<layout class="ch.qos.logback.classic.PatternLayout">
<springProfile name="dev">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ----> [%thread] ---> %-5level %logger{50} - %msg%n</pattern>
</springProfile>
<springProfile name="!dev">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ==== [%thread] ==== %-5level %logger{50} - %msg%n</pattern>
</springProfile>
</layout>
</appender>

總結:如果什麼都不配置,僅僅只需要在application.yml中簡單的設定下,比如設定下輸出到控制檯的格式啊,輸出檔案的格式啊,輸出的檔案去哪裡啊,日誌的級別啊。比如像下面這個圖中的配置

當然,這並不能滿足我們複雜的業務需求。這個時候我們就需要加入不同的日誌框架所對應的不同的配置檔名。這個在上面已經截圖了。(SpringBoot官方建議寫帶”-spring”字尾的,這樣可以開始高階profile的功能)。SpringBoot預設會從類路徑下載入,當然你也可以放到任何地方,然後配置一下logging.config然後指定下配置檔案的位置即可!比如像這樣

logging:
config: classpath:log/logback.xml

這樣就可以載入到了,然後再日誌的配置檔案中進行配置。這邊我司專案主要用log4j2,所有我這邊就貼下log4j2相關的配置了。

7.7 SpringBoot日誌框架的切換

將SpringBoot預設的slf4j logback切換到slf4j log4j2

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!--排除SpringBoot預設的slf4j和logback的實現-->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--匯入log4j2的starter啟動器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

然後在專案中新增log4j2-spring.xml的配置檔案即可!

IDE可以開啟pom的依賴關係,然後進行不需要的jar包的排除

7.8 log4j2配置檔案詳解

引用下其他人的配置檔案,我就不再說明了。有需要的可以慢慢看,或者自行谷歌百度。

8.Web開發

8.1 靜態資原始檔的對映

在WebMvcAutoConfiguration中有這樣一個方法:

@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/的資源對映,例如jquery可以以maven導包的形式匯入工程。
//專案啟動後訪問:http://localhost:8081/webjars/jquery/3.1.0/jquery.js
//springboot會把webjars後面的訪問路徑轉發到專案類路徑下或當前專案所有jar包的類路勁下的
//META-INF/resources/webjars/下來進行訪問。可以看下面我發的兩張圖
if (!registry.hasMappingForPattern("/webjars/**")) {
customizeResourceHandlerRegistration(registry
.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/")
.setCachePeriod(getSeconds(cachePeriod))
.setCacheControl(cacheControl));
}
}

我先在自己專案的類路徑下建立如下資料夾和資原始檔

然後我就可以通過瀏覽器進行訪問了

當然以上的方式還是比較麻煩的。SpringBoot預設為我們設定好了一些靜態資源的路徑。

//這個是WebMvcAutoConfiguration的addResourceHandlers方法的裡面一部分
//主要作用就是對訪問路勁"/**"做對映,對映的路徑就是下面那個路徑。
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(
registry.addResourceHandler(staticPathPattern)
.addResourceLocations(getResourceLocations(
this.resourceProperties.getStaticLocations()))
.setCachePeriod(getSeconds(cachePeriod))
.setCacheControl(cacheControl));
}
"classpath:/META-INF/resources/", 
"classpath:/resources/",
"classpath:/static/", 
"classpath:/public/" 
"/":當前專案的根路徑

綜上,我們可以在專案的類路勁下建立以上這些檔案,然後把靜態資源放到這裡面即可!比第一種要方便很多。

WebMvcAutoConfiguration還可以配置歡迎頁面,網站標題的圖示。在welcomePageHandlerMapping和FaviconConfiguration.faviconHandlerMapping方法中設定。

8.2 Thymeleaf

SpringBoot推薦使用的模板引擎,因我司都是前後端完全分離的,所以完全不用寫html css js。

這邊貼一個官網的文件。上面介紹的非常詳細,若日後有需要,可以看看。官方PDF文件

8.3 webMVC自動配置原理與定製

首先我們看看官方文件中介紹webMVC自動配置了哪些功能:官方Web開發模組