JavaWeb了解之SpringBoot篇1

目錄
  1. 第一個(gè)SpringBoot項(xiàng)目(HelloWorld)
  2. starter機(jī)制
  3. YAML標(biāo)記語(yǔ)言
  4. 配置文件
  5. 日志(記錄運(yùn)行情況在讶、定位問(wèn)題)
  6. 靜態(tài)資源映射
  7. Thymeleaf模板引擎
  8. 國(guó)際化

一款用于簡(jiǎn)化Spring項(xiàng)目搭建和開(kāi)發(fā)的開(kāi)源框架(使開(kāi)發(fā)者更專注于業(yè)務(wù)邏輯)煞抬。

SpringBoot的特點(diǎn)
  1. 以Spring為基礎(chǔ),使用更簡(jiǎn)單构哺、功能更豐富革答、性能更穩(wěn)定健壯。
  2. 提供了大量開(kāi)箱即用的依賴包(starter機(jī)制):自動(dòng)管理依賴包中的依賴(簡(jiǎn)化了復(fù)雜的依賴包管理);提供了大量默認(rèn)/自動(dòng)配置(省去了大量的XML配置內(nèi)容)蝗碎,不需要任何形式的配置即可實(shí)現(xiàn)Spring的所有配置(可以通過(guò)配置文件修改默認(rèn)配置)湖笨。
  3. 內(nèi)嵌了Servlet容器(如:Tomcat、Jetty蹦骑、Undertow等)慈省,應(yīng)用無(wú)需打成WAR包 。
  4. 可在終端執(zhí)行java–jar xxx.jar命令來(lái)獨(dú)立運(yùn)行SpringBoot項(xiàng)目眠菇。
  5. 可對(duì)正在運(yùn)行的項(xiàng)目提供監(jiān)控边败。

隨著微服務(wù)技術(shù)的流行,SpringBoot也成了時(shí)下炙手可熱的技術(shù)捎废。

1. 第一個(gè)SpringBoot項(xiàng)目(HelloWorld)

===》1. 創(chuàng)建項(xiàng)目
 方式1(創(chuàng)建Maven項(xiàng)目)
  1. 修改pom.xml文件(添加SpringBoot依賴包)
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.5</version>
    <relativePath/>
  </parent>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>
  2. 創(chuàng)建HelloWorldApplication.java文件(在com.sst.cx包下)
  package com.sst.cx;
  import org.springframework.boot.SpringApplication;
  import org.springframework.boot.autoconfigure.SpringBootApplication;
  @SpringBootApplication
  public class HelloWorldApplication {
    public static void main( String[] args ) {
        SpringApplication.run(HelloWorldApplication.class, args);
    }
  }
 方式2(創(chuàng)建Spring項(xiàng)目)推薦
  會(huì)自動(dòng)在pom.xml添加SpringBoot依賴笑窜,并自動(dòng)在com.sst.cx包下創(chuàng)建項(xiàng)目名+Application.java文件(內(nèi)容同上)。

===》2. 創(chuàng)建HelloController.java文件(在com.sst.cx.controller包下)
package com.sst.cx.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class HelloController {
    @ResponseBody  // 將方法的返回內(nèi)容作為頁(yè)面內(nèi)容登疗。不使用該注解時(shí)排截,會(huì)使用該方法的返回值對(duì)應(yīng)的同名html文件作為顯示頁(yè)面。
    @RequestMapping("/hello")  // 映射路徑為/hello辐益,在瀏覽器中使用http://localhost/hello可訪問(wèn)到断傲。
    public String hello() {
        return "Hello World!";
    }
}

===》3. 運(yùn)行項(xiàng)目,在瀏覽器中輸入http://127.0.0.1:8080/hello智政。
  內(nèi)置了Tomcat(不再需要部署到Tomcat)认罩,可以直接運(yùn)行。
創(chuàng)建Maven項(xiàng)目

創(chuàng)建Spring項(xiàng)目-步驟1

創(chuàng)建Spring項(xiàng)目-步驟2

SpringBoot項(xiàng)目結(jié)構(gòu)

運(yùn)行結(jié)果

2. starter機(jī)制

Spring項(xiàng)目在創(chuàng)建后想要運(yùn)行续捂,需要:導(dǎo)入各種依賴jar包垦垂、添加許多xml配置、部署到Tomcat服務(wù)器中牙瓢。
SpringBoot項(xiàng)目在創(chuàng)建后可以直接運(yùn)行(不用編寫(xiě)任何代碼劫拗、不用進(jìn)行任何配置),這都要?dú)w功于starter機(jī)制一罩。

SpringBoot將企業(yè)應(yīng)用研發(fā)中的各種場(chǎng)景都抽取出來(lái) 做成一個(gè)個(gè)的starter依賴包(整合了該場(chǎng)景下所有可能用到的依賴杨幼,并提供了大量的默認(rèn)/自動(dòng)配置),開(kāi)發(fā)者只需要在Maven的pom.xml中添加相應(yīng)的starter即可(SpringBoot就能自動(dòng)掃描到要加載的信息并啟動(dòng)相應(yīng)的默認(rèn)配置)聂渊。

1. SpringBoot官方提供的starter依賴包以spring-boot-starter-xxx方式命名差购。
    spring-boot-starter-parent
    spring-boot-starter-web
    spring-boot-starter-test
    spring-boot-starter-redis
    spring-boot-starter-data-mongodb
    spring-boot-starter-data-elasticsearch
2. 自定義的starter依賴包(第三方技術(shù)廠商提供 或 開(kāi)發(fā)員自己創(chuàng)建)以xxx-spring-boot-starter方式命名。
    druid-spring-boot-starter
    mybatis-spring-boot-starter
如果pom.xml中提示找不到該依賴時(shí)汉嗽,點(diǎn)擊Maven刷新按鈕
  1. spring-boot-starter-parent

所有SpringBoot項(xiàng)目的父級(jí)依賴(即所有SpringBoot項(xiàng)目都需要添加該父依賴):統(tǒng)一管理項(xiàng)目?jī)?nèi)的部分常用依賴欲逃;統(tǒng)一管理其他starter的版本(該依賴又被稱為SpringBoot的版本仲裁中心)。

該依賴包主要提供了以下特性:
  1. 默認(rèn)JDK版本(Java 8)
  2. 默認(rèn)字符集(UTF-8)
  3. 依賴管理功能
  4. 資源過(guò)濾
  5. 默認(rèn)插件配置
  6. 識(shí)別 application.properties 和 application.yml 類型的配置文件
===》查看源碼可知
其有一個(gè)父級(jí)依賴:spring-boot-dependencies饼暑。
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>2.4.5</version>
    </parent>

查看spring-boot-dependencies的pom.xml內(nèi)容:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.4.5</version>
    <packaging>pom</packaging>
    ....
    <!-- 負(fù)責(zé)定義依賴稳析、插件的版本號(hào) -->
    <properties>
        <activemq.version>5.16.1</activemq.version>
        <antlr2.version>2.7.7</antlr2.version>
        <appengine-sdk.version>1.9.88</appengine-sdk.version>
        <artemis.version>2.15.0</artemis.version>
        <aspectj.version>1.9.6</aspectj.version>
        <assertj.version>3.18.1</assertj.version>
        <atomikos.version>4.0.6</atomikos.version>
        ....
    </properties>
    <!-- 負(fù)責(zé)管理依賴 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.apache.activemq</groupId>
                <artifactId>activemq-amqp</artifactId>
                <version>${activemq.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.activemq</groupId>
                <artifactId>activemq-blueprint</artifactId>
                <version>${activemq.version}</version>
            </dependency>
            ...
        </dependencies>
    </dependencyManagement>
    <build>
        <!-- 負(fù)責(zé)管理插件 -->
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>build-helper-maven-plugin</artifactId>
                    <version>${build-helper-maven-plugin.version}</version>
                </plugin>
                <plugin>
                    <groupId>org.flywaydb</groupId>
                    <artifactId>flyway-maven-plugin</artifactId>
                    <version>${flyway.version}</version>
                </plugin>
                ...
            </plugins>
        </pluginManagement>
    </build>
</project>
  1. spring-boot-starter-web
為Web開(kāi)發(fā)提供了所有依賴:
  1. spring-boot-starter(核心啟動(dòng)器)
    spring-boot
    spring-boot-autoconfigure
    spring-boot-starter-logging(log日志)
      logback-classic
      log4j-to-slf4j
      jul-to-slf4j
    jakarta.annotation-api
    snakeyaml
  2. spring-boot-starter-tomcat(Tomcat服務(wù)器)
  3. spring-boot-starter-json(jackson)
  4. spring-web(SpringFramework)
    spring-beans
  5. spring-webmvc
    spring-aop
    spring-context
    spring-expression
    為SpringMVC提供了大量默認(rèn)配置
      1. 引入了ContentNegotiatingViewResolver和BeanNameViewResolver(視圖解析器)
      2. 對(duì)包括WebJars在內(nèi)的靜態(tài)資源的支持
      3. 自動(dòng)注冊(cè)Converter洗做、GenericConverter、Formatter(轉(zhuǎn)換器和格式化器)
      4. 對(duì)HttpMessageConverters的支持(Spring MVC中用于轉(zhuǎn)換HTTP請(qǐng)求和響應(yīng)的消息轉(zhuǎn)換器)
      5. 自動(dòng)注冊(cè) MessageCodesResolver(用于定義錯(cuò)誤代碼生成規(guī)則)
      6. 支持對(duì)靜態(tài)首頁(yè)(index.html)的訪問(wèn)
      7. 自動(dòng)使用 ConfigurableWebBindingInitializer

使用(只需在pom.xml中添加依賴:spring-boot-starter-web):
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.5</version>
        <relativePath/>
    </parent>
    <dependencies>
        <!--導(dǎo)入 spring-boot-starter-web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        ...
    </dependencies>

在終端執(zhí)行如下命令彰居,可查看項(xiàng)目的依賴樹(shù)
  mvn dependency:tree

自定義starter(命名規(guī)則:xxx-spring-boot-starter)

將獨(dú)立于業(yè)務(wù)代碼之外的功能模塊封裝成一個(gè)starter诚纸,便于復(fù)用。

步驟:
===》1. 創(chuàng)建一個(gè)SpringBoot項(xiàng)目陈惰,修改pom文件
  添加spring-boot-autoconfigure依賴畦徘,可根據(jù)功能添加其他依賴。
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
===》2. 創(chuàng)建HelloProperties.java配置類
@ConfigurationProperties(prefix = "hello")
public class HelloProperties {
    private String name;
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}
===》3. 創(chuàng)建HelloService.java
public class HelloService {
    private String name;
    private int age;
    public HelloService(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void hello() {
        System.out.println(name + "  age:" + age);
    }
}
===》4. 創(chuàng)建MyAutoConfiguration.java自動(dòng)配置類
@Configuration
@EnableConfigurationProperties(HelloProperties.class)
public class MyAutoConfiguration {
    @Autowired
    private HelloProperties helloProperties;
    @Bean
    public HelloService helloService() {
        return new HelloService(helloProperties.getName(), helloProperties.getAge());
    }
}
===》5. 創(chuàng)建spring.factories文件(在resources/META-INF/目錄下)
  在該文件中配置上面創(chuàng)建的自動(dòng)配置類抬闯。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.sst.cx.config.MyAutoConfiguration
===》6. 在application.properties配置文件中可進(jìn)行默認(rèn)配置井辆。 
===》7. 打包 
  放置到測(cè)試項(xiàng)目的resources/lib目錄下。

使用自定義的starter

===》1. 創(chuàng)建一個(gè)SpringBoot項(xiàng)目(勾選Web依賴)溶握,修改pom.xml文件
  添加自定義的starter依賴
<dependency>
   <groupId>com.sst.cx</groupId>
   <artifactId>hello-spring-boot-starter</artifactId>
   <version>1.0-SNAPSHOT</version>
   <scope>system</scope>
   <systemPath>${project.basedir}/src/main/resources/lib/hello-spring-boot-starter-0.0.1-SNAPSHOT.jar</systemPath>
</dependency>
===》2. 在appllication.properties/yml配置文件中杯缺,添加
hello.name=zhangsan
hello.age=12
===》3. 測(cè)試(TestController.java)
@Controller
public class TestController {
    @Autowired
    private HelloService helloService;
    @ResponseBody
    @RequestMapping("/hello")
    public String hello() {
        helloService.hello();
        return "success";
    }
}
在瀏覽器中訪問(wèn)http://127.0.0.1:8080/hello,控制臺(tái)會(huì)輸出:zhangsan  age:12

3. YAML標(biāo)記語(yǔ)言(以數(shù)據(jù)為中心,比xml睡榆、json更適合作為配置文件)

只需在SpringBoot項(xiàng)目中引入spring-boot-starter-web或spring-boot-starter(二者都集成了SnakeYAML庫(kù))就可以使用YAML(以 .yml 或 .yaml 結(jié)尾)作為配置文件萍肆。

語(yǔ)法規(guī)則
  1. 使用縮進(jìn)表示層級(jí)關(guān)系(不允許使用Tab鍵,只允許使用空格肉微;空格數(shù)不重要匾鸥,但同級(jí)元素必須左側(cè)對(duì)齊)。
  2. 大小寫(xiě)敏感碉纳。
  3. 使用"key:[空格]value"形式(空格不能省略)表示一個(gè)鍵值對(duì)。
    例: url: www.baidu.com
  4. 支持3種數(shù)據(jù)結(jié)構(gòu)(可任意組合嵌套):
    1. 對(duì)象(鍵值對(duì)的集合:對(duì)象的每個(gè)屬性對(duì)應(yīng)一個(gè)鍵值對(duì))
      寫(xiě)法1. 普通寫(xiě)法(使用縮進(jìn)表示:對(duì)象與屬性的層級(jí)關(guān)系) 
        website: 
          name: 張三
          url: www.baidu.com
      寫(xiě)法2. 行內(nèi)寫(xiě)法
        website: {name: 張三,url: www.baidu.com}
    2. 數(shù)組(一組按次序排列的值)
      寫(xiě)法1. 普通寫(xiě)法(使用-表示:數(shù)組中的元素)
        pets:
          -dog
          -cat
          -pig
      寫(xiě)法2. 行內(nèi)寫(xiě)法
        pets: [dog,cat,pig]
    3. 字面量(單個(gè)的不可拆分的值馏艾,如:數(shù)字劳曹、字符串、布爾值琅摩、日期)
      直接寫(xiě)在鍵值對(duì)的value中即可铁孵,默認(rèn)情況下字符串是不需要使用單引號(hào)或雙引號(hào)的。 
        若字符串使用了單引號(hào)房资,則會(huì)對(duì)字符串的特殊字符進(jìn)行轉(zhuǎn)義(如:"hello\nworld"則會(huì)輸出hello\nworld)蜕劝。
        若字符串使用了雙引號(hào),則不會(huì)轉(zhuǎn)義(如:"hello\nworld"則會(huì)進(jìn)行換行)轰异。
  5. 文件組織結(jié)構(gòu)
    一個(gè)YAML文件由一個(gè)或多個(gè)相互獨(dú)立的文檔組成岖沛,文檔之間使用“---”作為分隔符(只包含一個(gè)文檔時(shí)可省略)。

例(hello.yaml)

spring:
  profiles: dev
  datasource:
    url: jdbc:mysql://localhost:3306/Test
    username: root
    password: 12345678
    driver-class-name: com.mysql.cj.jdbc.Driver
---
website:
  name: bianchengbang
  url: www.biancheng.net
---
name: "張三 \n 李四"

4. 配置文件

  1. 默認(rèn)配置文件
SpringBoot項(xiàng)目啟動(dòng)時(shí)會(huì)將以下5個(gè)位置的application.properties或apllication.yml文件(文件名固定)作為默認(rèn)配置文件搭独,并讀取配置內(nèi)容婴削。
  1. file:./config/*/
  2. file:./config/
  3. file:./
  4. classpath:/config/
  5. classpath:/
說(shuō)明:
  1. 優(yōu)先級(jí)依次降低,相同位置的application.properties的優(yōu)先級(jí)高于application.yml牙肝。
  2. file: 指項(xiàng)目的根目錄唉俗;classpath: 指項(xiàng)目的類路徑(即resources目錄)嗤朴。
  3. 通常只使用第5種(在resources目錄下創(chuàng)建配置文件,并添加內(nèi)容來(lái)覆蓋默認(rèn)配置)虫溜。
  4. Maven項(xiàng)目打包時(shí)雹姊,位于項(xiàng)目根目錄下的配置文件無(wú)法被打包進(jìn)項(xiàng)目的JAR包(即在jar包中失效)。  
    解決(3方式):
      1. 在IDEA的運(yùn)行配置(Run/Debug Configuration)中衡楞,添加虛擬機(jī)參數(shù) -Dspring.config.additional-location=/my-application.yml吱雏,指定外部配置文件。
      2. 在IDEA的運(yùn)行配置(Run/Debug Configuration)中寺酪,添加程序運(yùn)行參數(shù) --spring.config.additional-location=/my-application.yml坎背,指定外部配置文件。
      3. 在主啟動(dòng)類中調(diào)用System.setProperty()方法添加系統(tǒng)屬性spring.config.additional-location寄雀,指定外部配置文件得滤。

===》創(chuàng)建項(xiàng)目,在項(xiàng)目根目錄下盒犹、類路徑的config目錄下懂更、類路徑下分別創(chuàng)建一個(gè)配置文件(application.yml文件)。

1. 項(xiàng)目根路徑下
#上下文路徑為 /abc
server:
  servlet:
    context-path: /abc

2. 類路徑的config目錄下
#端口號(hào)為8084
#上下文路徑為 /helloWorld
server:
  port: 8084
  servlet:
    context-path: /helloworld

3. 類路徑下
#默認(rèn)配置
server:
  port: 8080

===》MyController.java
    package com.sst.cx.controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.bind.annotation.RestController;
    @RestController
    public class MyController {
        @ResponseBody
        @RequestMapping("/test")
        public String hello() {
            return "hello Spring Boot!";
        }
    }

===》根據(jù)優(yōu)先級(jí)可知:服務(wù)器端口為8084急膀;上下文路徑為/abc
使用瀏覽器訪問(wèn)http://localhost:8084/abc/test
  1. 外部配置文件

除了默認(rèn)配置文件沮协,SpringBoot還可以加載項(xiàng)目外部的配置文件。

指定外部配置文件的路徑(2種方式)
  1. spring.config.location(將只加載外部配置文件卓嫂,默認(rèn)配置文件會(huì)失效)
    使用命令:java -jar {JAR}  --spring.config.location={外部配置文件全路徑}
  2. spring.config.additional-location(外部配置文件的優(yōu)先級(jí)最高) 
    使用命令:java -jar {JAR}  --spring.config.additional-location={外部配置文件全路徑}
  1. 配置加載順序

SpringBoot項(xiàng)目不僅可以通過(guò)配置文件進(jìn)行配置慷暂,還可以通過(guò)環(huán)境變量、命令行參數(shù)等形式進(jìn)行配置晨雳。

SpringBoot會(huì)加載以下所有形式的配置(優(yōu)先級(jí)由高到低):
  1. 命令行參數(shù)
    SpringBoot中的所有配置都可以通過(guò)命令行參數(shù)進(jìn)行指定行瑞。
    命令格式:java -jar {Jar文件名} --{參數(shù)1}={參數(shù)值1} --{參數(shù)2}={參數(shù)值2}
  2. 來(lái)自java:comp/env的JNDI 屬性
  3. Java系統(tǒng)屬性(System.getProperties())
  4. 操作系統(tǒng)的環(huán)境變量
  5. RandomValuePropertySource配置的random.*屬性值
  6. 配置文件(.yml/.properties文件)
    SpringBoot啟動(dòng)時(shí),會(huì)自動(dòng)加載JAR包內(nèi)部及JAR包所在目錄指定位置的配置文件(.yml/.properties文件)餐禁。
    下圖(配置文件的加載順序)的說(shuō)明:
      1. /myBoot:表示JAR包所在目錄
      2. /childDir:表示JAR包所在目錄下config目錄的子目錄
      3. JAR:表示SpringBoot項(xiàng)目打包生成的JAR包血久;
      4. 數(shù)字:表示該配置文件的優(yōu)先級(jí),數(shù)字越小 優(yōu)先級(jí)越高 越先被加載(后加載的相同屬性會(huì)被忽略)帮非。
      5. 同一位置下氧吐,Properties文件優(yōu)先級(jí)高于YAML文件。
  7. @Configuration注解修飾的配置類上的@PropertySource指定的配置文件
  8. 通過(guò)SpringApplication.setDefaultProperties指定的默認(rèn)屬性
配置文件的加載順序

例(命令行參數(shù))

java -jar hello-0.0.1-SNAPSHOT.jar --server.port=8081 --server.servlet.context-path=/hello
說(shuō)明:
  1. server.port:指定服務(wù)器端口
  2. server.servlet.context:上下文路徑(項(xiàng)目的訪問(wèn)路徑)
  1. 配置綁定(把配置文件中的值綁定到JavaBean對(duì)象的屬性中)
步驟:
1. 將配置信息存放在配置文件中末盔。
  如果將所有的配置都集中在application.properties/yml配置文件中筑舅,會(huì)十分臃腫、難以維護(hù)庄岖。通常會(huì)將與SpringBoot無(wú)關(guān)的自定義配置提取到一個(gè)單獨(dú)的配置文件(如:resources/person.properties)中豁翎,然后給類添加@PropertySource注解指向該配置文件。
/*
  例:
    @Component  
    @ConfigurationProperties(prefix="person")  // 將JavaBean屬性和配置文件的值進(jìn)行綁定
    @PropertySource(value = "classpath:person.properties")  // 指定配置文件
    public class Person{}
*/

2. 在代碼中給類添加注解進(jìn)行綁定隅忿。
  1. @ConfigurationProperties注解(修飾類:用于將配置文件的多個(gè)配置綁定到類的屬性中)
    支持松散綁定/松散語(yǔ)法(如:配置文件中的person.firstName心剥、person.first-name邦尊、person.first_name、PERSON_FIRST_NAME都可以綁定到Person類的firstName屬性)优烧。
    不支持SpEL表達(dá)式蝉揍。
    支持所有類型數(shù)據(jù)的封裝(如: 基礎(chǔ)數(shù)據(jù)類型、類畦娄、Map又沾、List、Set)熙卡。
    例:
      @Component  
      @ConfigurationProperties(prefix="person")
      public class Person{}
  2. @Value注解(修飾屬性杖刷,用于將配置文件中的某一個(gè)配置綁定到屬性中)
    不支持松散綁定。
    支持SpEL表達(dá)式驳癌。
    只支持基本數(shù)據(jù)類型(字符串滑燃、布爾值、整數(shù))颓鲜。
    例:
      @Component
      public class Person {
        @Value("${person.name}")
        private String name;
      }

===》在application.yml配置文件中(若為.properties文件則為:person.age=10形式)
  person:
    name: 張三
    age: 10
    boss: false
    birth: 1949/10/1
    maps: { k1: v1,k2: 12 }
    lists:
      ‐ 張三
      ‐ 李四
    dog:
      name: 初一
      age: 2 
===》Person類
@Component  // 類必須在Ioc容器中表窘。
@ConfigurationProperties(prefix="person")  // 將配置文件中以person前綴開(kāi)頭的配置綁定到類的屬性中。
public class Person{
    private String name;
    private Integer age;
    private Boolean boss;
    private Date birth;
    private Map<String, Object> maps;
    private List<Object> lists;
    private Dog dog;
        ...省略set甜滨、get方法
}
===》Dog類
public class Dog {
    private String name;
    private String age;
        ...省略set乐严、get方法
}
===》HelloController控制器類
@Controller
public class HelloController {
    @Autowired
    private Person person;
    @ResponseBody
    @RequestMapping("/hello")
    public Person hello() {
        return person;
    }
}
在瀏覽器中輸入:http://localhost:8081/hello
  1. 導(dǎo)入Spring配置(默認(rèn)情況下,Spring的.xml配置文件不會(huì)被SpringBoot識(shí)別)
2種方式:
  1. 使用@ImportResource注解加載Spring的xml配置文件衣摩。
    首先在類路徑(resources目錄)下創(chuàng)建beans.xml昂验,然后在主啟動(dòng)類中添加@ImportResource(locations = {"classpath:/beans.xml"})
  2. 使用全注解方式加載Spring配置。
    在使用@Configuration注解修飾的配置類中艾扮,使用@Bean注解向容器中注入JavaBean凛篙。
    例:
      @Configuration  // 定義一個(gè)配置類(替代.xml配置文件)
      public class MyAppConfig {
          // @Bean修飾的方法會(huì)被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext類掃描,構(gòu)建Bean并注入到容器中栏渺。
          // 等價(jià)于 <bean id="personService" class="PersonServiceImpl"></bean>
          @Bean  // 等價(jià)于xml文件的Bean元素,id為方法名锐涯,class為返回值類型
          public PersonService personService() {
              return new PersonServiceImpl();
          }
      }

例(@ImportResource注解 方式加載)

===》beans.xml(resources目錄下)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="personService" class="com.sst.cx.service.impl.PersonServiceImpl"></bean>
</beans>

===》PersonService.java
public interface PersonService {
    public Person getPersonInfo();
}
===》PersonServiceImpl.java
public class PersonServiceImpl implements PersonService {
    @Autowired
    private Person person;
    @Override
    public Person getPersonInfo() {
        return person;
    }
}
===》HelloworldApplicationTests.java(測(cè)試文件)
@SpringBootTest
class HelloworldApplicationTests {
    @Autowired
    Person person;
    // IOC 容器
    @Autowired
    ApplicationContext ioc;
    @Test
    public void testHelloService() {
        // 校驗(yàn) IOC 容器中是否包含組件 personService
        boolean b = ioc.containsBean("personService");
        if (b) {
            System.out.println("personService 已經(jīng)添加到 IOC 容器中");
        } else {
            System.out.println("personService 沒(méi)添加到 IOC 容器中");
        }
    }
    @Test
    void contextLoads() {
        System.out.println(person);
    }
}
===》HelloworldApplication.java(在主啟動(dòng)類中添加@ImportResource注解)
@ImportResource(locations = {"classpath:/beans.xml"})  // 將beans.xml加載到項(xiàng)目中
@SpringBootApplication
public class HelloworldApplication {
    public static void main(String[] args) {
        SpringApplication.run(HelloworldApplication.class, args);
    }
}
  1. 配置Profile(多環(huán)境)
通常程序會(huì)運(yùn)行在多套環(huán)境(開(kāi)發(fā)磕诊、測(cè)試、生產(chǎn))下纹腌,每套環(huán)境的配置不同(如:數(shù)據(jù)庫(kù)地址霎终、服務(wù)器端口、日志級(jí)別 等)缸夹。如果每次打包時(shí)靠手動(dòng)修改代碼來(lái)實(shí)現(xiàn)变隔,繁瑣且易錯(cuò)肚吏。可使用Profile功能來(lái)實(shí)現(xiàn)動(dòng)態(tài)切換環(huán)境(2種方式):
  方式1. yml多文檔方式
    在一個(gè)yml文件中每個(gè)分隔符隔開(kāi)的文檔對(duì)應(yīng)一套環(huán)境广凸。
  方式2. 多profile文件方式
    創(chuàng)建多個(gè)環(huán)境配置文件(每個(gè)環(huán)境配置文件對(duì)應(yīng)一套環(huán)境)阅茶。

切換環(huán)境的方法:
  1. 配置文件中手動(dòng)切換
  2. 虛擬機(jī)參數(shù): 在VM options指定: -Dspring.profiles.active=dev
  3. 命令行參數(shù): java-jar xxx.jar --spring.profiles.active=dev

例(yml多文檔方式)

在application.yml配置文件中,添加:

#默認(rèn)配置
server:
  port: 8080
#切換配置
spring:
  profiles:
    active: test  #設(shè)置當(dāng)前環(huán)境
---
#開(kāi)發(fā)環(huán)境
server:
  port: 8081
spring:
  config:
    activate:
      on-profile: dev
---
#測(cè)試環(huán)境
server:
  port: 8082
spring:
  config:
    activate:
      on-profile: test
---
#生產(chǎn)環(huán)境
server:
  port: 8083
spring:
  config:
    activate:
      on-profile: prod

例(多profile文件方式)

===》創(chuàng)建application-dev.properties
server.port = 8080
===》創(chuàng)建application-test.properties
server.port = 8081
===》創(chuàng)建application-pro.properties
server.port = 8082
===》在application.properties配置文件中
spring.profiles.active = dev #設(shè)置當(dāng)前環(huán)境谅海,各環(huán)境配置文件的后綴脸哀。
  1. 自動(dòng)配置原理

SpringBoot通過(guò)注解(而不是xml文件的方式)來(lái)實(shí)現(xiàn)自動(dòng)/默認(rèn)配置。SpringBoot在啟動(dòng)時(shí)從類路徑下的META-INF/spring.factories中獲取EnableAutoConfiguration指定的所有自動(dòng)配置類扭吁,導(dǎo)入到容器中生效撞蜂。
在SpringBoot項(xiàng)目的啟動(dòng)類中有一個(gè)@SpringBootApplication注解(核心注解,一個(gè)組合注解)侥袜。

===》@SpringBootApplication注解蝌诡,定義如下
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration 
@EnableAutoConfiguration  //  
@ComponentScan(  // 
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
...
}

===》@EnableAutoConfiguration注解,定義如下
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
...
}
SpringBoot通過(guò)@EnableAutoConfiguration注解開(kāi)啟自動(dòng)配置(掃描jar包下的spring.factories文件枫吧,文件中包含了自動(dòng)配置類)浦旱。
該注解導(dǎo)入了EnableAutoConfigurationImportSelector類的selectimports方法
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }
    protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            configurations = this.removeDuplicates(configurations);
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.getConfigurationClassFilter().filter(configurations);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationEntry(configurations, exclusions);
        }
    }
    通過(guò)getCandidateConfiguration方法,獲取配置文件列表由蘑。
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = new ArrayList(SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()));
        ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).forEach(configurations::add);
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you are using a custom packaging, make sure that file is correct.");
        return configurations;
    }
    loadFactoryNames方法會(huì)加載所有META-INF下有spring.factories文件的jar包闽寡,并根據(jù)spring.factories文件中的配置,去加載相應(yīng)的類尼酿。
    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        ClassLoader classLoaderToUse = classLoader;
        if (classLoader == null) {
            classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
        }
        String factoryTypeName = factoryType.getName();
        return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
    }
    private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
        Map<String, List<String>> result = (Map)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            Map<String, List<String>> result = new HashMap();
            try {
                Enumeration<URL> urls = classLoader.getResources("META-INF/spring.factories");
                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()) {
                        Map.Entry<?, ?> entry = (Map.Entry)var6.next();
                        String factoryTypeName = ((String)entry.getKey()).trim();
                        String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        String[] var10 = factoryImplementationNames;
                        int var11 = factoryImplementationNames.length;

                        for(int var12 = 0; var12 < var11; ++var12) {
                            String factoryImplementationName = var10[var12];
                            ((List)result.computeIfAbsent(factoryTypeName, (key) -> {
                                return new ArrayList();
                            })).add(factoryImplementationName.trim());
                        }
                    }
                }
                result.replaceAll((factoryType, implementations) -> {
                    return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
                });
                cache.put(classLoader, result);
                return result;
            } catch (IOException var14) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
            }
        }
    }

===》@import
該注解導(dǎo)入了AutoConfigurationImportSelector類爷狈,這個(gè)類中有一個(gè)很重要的方法:selectImports(),它幾乎涵蓋了組件自動(dòng)裝配的所有處理邏輯裳擎,包括獲得候選配置類涎永、配置類去重、排除不需要的配置類鹿响、過(guò)濾等羡微,最終返回符合條件的自動(dòng)配置類的全限定名數(shù)組。

例(spring.factories文件)
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.sst.cx.config.MyAutoConfiguration,\
com.sst.cx.config.MyAutoConfiguration2

例:
當(dāng)項(xiàng)目中需要使用依賴jar包中類的實(shí)例惶我,只需創(chuàng)建一個(gè)該類類型的屬性妈倔,并使用@Autowired或@Resource注解標(biāo)注(會(huì)注入到容器中)。

5. 日志(記錄運(yùn)行情況绸贡、定位問(wèn)題)

日志框架分為兩類:
  1. 日志抽象層(為日志功能提供了一套標(biāo)準(zhǔn)規(guī)范的JavaAPI)
    1. JCL(Jakarta Commons Logging)
    2. SLF4j(Simple Logging Facade for Java)目前最流行
      可靈活使用占位符進(jìn)行參數(shù)占位:簡(jiǎn)化代碼盯蝴、可讀性更好。
    3. jboss-logging
  2. 日志實(shí)現(xiàn)
    1. Log4j
    2. JUL(java.util.logging)
    3. Log4j2
    4. Logback(SLF4j的原生實(shí)現(xiàn)框架)
      和Log4j同一個(gè)作者听怕,用來(lái)代替Log4j捧挺,擁有比Log4j更多的優(yōu)點(diǎn)特性、更強(qiáng)的性能尿瞭。

通常情況下闽烙,日志功能由一個(gè)日志抽象層和一個(gè)日志實(shí)現(xiàn)組合而成(SpringBoot選用的是:SLF4J+Logback)声搁。
  1. 使用SLF4J
使用步驟:
  1. 在項(xiàng)目中導(dǎo)入SLF4J框架和一個(gè)日志實(shí)現(xiàn)框架(如:Logback)。
  2. 調(diào)用日志時(shí),應(yīng)調(diào)用日志抽象層的方法研儒,而不是直接調(diào)用日志實(shí)現(xiàn)層的方法。

例:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld {
    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger(HelloWorld.class);  
        // 調(diào)用SLF4J的info()方法独令,而非直接調(diào)用logback的方法端朵。
        logger.info("Hello World");
    }
}

從下圖的SLF4J官方方案可以看出:
  1. Logback作為Slf4j的原生實(shí)現(xiàn)框架。當(dāng)項(xiàng)目使用SLF4J+Logback組合記錄日志時(shí)燃箭,只需要引入SLF4J冲呢、Logback的Jar包即可;
  2. Log4j雖然和Logback屬于同一個(gè)作者招狸,但Log4j的出現(xiàn)要早于SLF4J碗硬,因而Log4j沒(méi)有直接實(shí)現(xiàn)SLF4J。當(dāng)項(xiàng)目使用SLF4J+Log4j組合記錄日志時(shí)瓢颅,不但需要引入SLF4J、Log4j 的Jar包弛说,還必須引入它們之間的適配層(承上啟下:既要實(shí)現(xiàn)SLF4J的方法挽懦,還要有調(diào)用Log4j的方法):slf4j-log4j12.jar。
  3. 當(dāng)項(xiàng)目使用SLF4J+JUL組合記錄日志時(shí)木人,與SLF4J+Log4j一樣信柿,不但需要引入SLF4J冀偶、JUL 的對(duì)應(yīng)的Jar包,還要引入適配層:slf4j-jdk14.jar渔嚷。

每一個(gè)日志實(shí)現(xiàn)框架都有自己的配置文件进鸠。使用SLF4J記錄日志時(shí),應(yīng)該使用日志實(shí)現(xiàn)框架的配置文件形病。
SLF4J可以和各種日志實(shí)現(xiàn)框架組合使用
  1. 統(tǒng)一各依賴包中的日志框架
通常客年,一個(gè)項(xiàng)目會(huì)依賴于各種框架,每個(gè)框架記錄日志所使用的日志框架各不相同漠吻。如:Spring Boot(slf4j+logback)量瓜、Spring(commons-logging)、Hibernate(jboss-logging)途乃。
因此绍傲,需要統(tǒng)一日志框架的使用:
  1. 使用 各替換包 替換 項(xiàng)目中原來(lái)的全部日志實(shí)現(xiàn)框架(分為2步:去除原框架;引入相應(yīng)的替換包)耍共。
    替換包包含了 被替換的日志框架中的所有類(保證應(yīng)用不會(huì)報(bào)錯(cuò))烫饼,但使用的是SLF4J的API(達(dá)到統(tǒng)一日主框架的目的)。
    如:log4j-over-slf4j替換Log4j试读、jul-to-slf4j.jar替換JUL
  2. 導(dǎo)入SLF4J實(shí)現(xiàn)杠纵。

SpringBoot項(xiàng)目
  spring-boot-starter(核心啟動(dòng)器)引入了spring-boot-starter-logging。spring-boot-starter-logging引入了 logback-classic(SLF4J的實(shí)現(xiàn))鹏往、 log4j-to-slf4j(log4j的替換包)淡诗、jul-to-slf4j(JUL的替換包)。
  所以引入spring-boot-starter就完成了:引入替換包和導(dǎo)入SLF4J實(shí)現(xiàn) 這2步伊履。引入其他三方框架依賴時(shí)韩容,只需刪除其所依賴的日志框架,即可實(shí)現(xiàn)日志框架的統(tǒng)一唐瀑。例:
<dependency>
    <groupId>org.apache.activemq</groupId>
    <artifactId>activemq-console</artifactId>
    <version>${activemq.version}</version>
    <exclusions>
        <exclusion>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>
SLF4J 官方給出的統(tǒng)一日志框架使用的方案
  1. 日志的配置(日志的級(jí)別群凶、日志的輸出格式)
  1. 默認(rèn)配置
SpringBoot項(xiàng)目中引入spring-boot-starter(引入了SLF4J+Logback,提供了大量默認(rèn)配置)就可以直接使用日志功能哄辣。

日志級(jí)別
  日志的輸出都是分級(jí)別的请梢,當(dāng)一條日志信息的級(jí)別大于或等于配置文件的級(jí)別時(shí),才會(huì)對(duì)這條日志進(jìn)行記錄力穗。

輸出格式
  可以通過(guò)日志參數(shù)對(duì)日志的輸出格式進(jìn)行修改毅弧。
序號(hào) 常見(jiàn)的日志級(jí)別(優(yōu)先級(jí)依次升高) 描述
1 trace 追蹤,指明程序運(yùn)行軌跡当窗。(很少使用)
2 debug 調(diào)試够坐。(實(shí)際項(xiàng)目中一般將其作為最低級(jí)別)
3 info 輸出重要的信息。(使用較多)
4 warn 警告。(使用較多)
5 error 錯(cuò)誤信息元咙。(使用較多)
序號(hào) 常用的輸出格式 描述
1 %d{yyyy-MM-dd HH:mm:ss, SSS} 日志創(chuàng)建時(shí)間(年月日 時(shí)分秒 毫秒)
2 %-5level 日志級(jí)別(-5表示:左對(duì)齊且固定輸出5個(gè)字符梯影,不足則右邊補(bǔ)0)
3 %logger 或 %c logger的名稱
4 %thread 或 %t 當(dāng)前線程的名稱
5 %p 日志輸出格式
6 %message 或 %msg 或 %m 日志內(nèi)容。logger.info("message")中的message
7 %n 換行符
8 %class 或 %C Java類名
9 %file 或 %F 文件名
10 %L 出錯(cuò)的行號(hào)
11 %method 或 %M 方法名
12 %l 語(yǔ)句所在的行數(shù)(包括類名庶香、方法名甲棍、文件名、行數(shù))
13 hostName 本地機(jī)器名
14 hostAddress 本地ip地址
例(測(cè)試SpringBoot項(xiàng)目的日志默認(rèn)級(jí)別)
package com.sst.cx;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class HelloApplication {
    public static void main(String[] args) {
        SpringApplication.run(HelloApplication.class, args);
        Logger logger = LoggerFactory.getLogger(SpringBootMaven2Application.class);
        logger.trace("trace 級(jí)別日志");
        logger.debug("debug 級(jí)別日志");
        logger.info("info 級(jí)別日志");
        logger.warn("warn 級(jí)別日志");
        logger.error("error 級(jí)別日志");
    }
}

通過(guò)控制臺(tái)輸出結(jié)果可知:
  1. SpringBoot項(xiàng)目的日志默認(rèn)級(jí)別為:info赶掖。
  2. 日志輸出內(nèi)容默認(rèn)包含(占用一行感猛,依次包含):
    創(chuàng)建時(shí)間(yyyy-MM-dd HH:mm:ss.SSS)
    日志級(jí)別(如:WARN)
    進(jìn)程 ID(如:945)
    分隔符:---
    線程名:方括號(hào)括起來(lái)(如:[     main])
    logger的名稱(如:com.sst.cx.HelloApplication)
    日志內(nèi)容(如:warn 級(jí)別日志)
例:
2015-10-29 11:37:21.940  INFO 945 --- [           main] com.sst.cx.HelloApplication   : info 級(jí)別日志
  1. 修改默認(rèn)配置
在resources目錄下創(chuàng)建application.properties/yml,按需求添加如下內(nèi)容來(lái)覆蓋默認(rèn)配置:

#日志級(jí)別
logging.level.net.biancheng.www=trace
#設(shè)置日志輸出的位置-相對(duì)路徑(會(huì)在項(xiàng)目根目錄/my-log/myLog下自動(dòng)生成spring.log文件)
#logging.file.path=my-log/myLog
#設(shè)置日志輸出的位置-絕對(duì)路徑(會(huì)在項(xiàng)目所在磁盤(pán)根目錄/Users/cx/spring-boot/logging下自動(dòng)生成spring.log文件)
logging.file.path=/Users/cx/spring-boot/logging
#控制臺(tái)的日志輸出格式
logging.pattern.console=%d{yyyy-MM-dd hh:mm:ss} [%thread] %-5level %logger{50} - %msg%n
#spring.log日志文件輸出格式
logging.pattern.file=%d{yyyy-MM-dd} === [%thread] === %-5level === %logger{50} === - %msg%n
  1. 自定義日志配置
修改application.porperties/yml文件只能修改個(gè)別日志配置倘零,若修改更多的配置或使用更高級(jí)的功能唱遭,則需要通過(guò)日志實(shí)現(xiàn)框架的配置文件(Spring官方提供,開(kāi)發(fā)者只需將指定的配置文件放置到項(xiàng)目的類路徑即resourecs目錄下即可)來(lái)進(jìn)行配置(按需修改)呈驶。
日志框架 配置文件
Logback logback-spring.xml拷泽、logback-spring.groovy、logback.xml袖瞻、logback.groovy
Log4j2 log4j2-spring.xml司致、log4j2.xml
JUL (Java Util Logging) logging.properties
日志框架的配置文件分為2類(在使用時(shí)大不相同):
  1. 普通日志配置文件(即不帶srping標(biāo)識(shí)),如:logback.xml聋迎。
    放置在項(xiàng)目的類路徑下后脂矫,配置文件會(huì)跳過(guò)SpringBoot直接被日志框架加載。
  2. 帶有spring標(biāo)識(shí)的日志配置文件(SpringBoot推薦該方式)霉晕,如:logback-spring.xml庭再、log4j2-spring.xml。
    放置在項(xiàng)目的類路徑下后牺堰,配置文件不會(huì)直接被日志框架加載拄轻,而是由SpringBoot對(duì)它們進(jìn)行解析,這樣就能使用SpringBoot的高級(jí)功能Profile(實(shí)現(xiàn)在不同的環(huán)境中使用不同的日志配置)伟葫。 
例(logback.xml)普通日志配置文件
  將logback.xml復(fù)制到SpringBoot項(xiàng)目的類路徑下(resources目錄下)恨搓,該配置文件配置內(nèi)容如下

<?xml version="1.0" encoding="UTF-8"?>
<!--
scan:設(shè)置為true(默認(rèn)),則當(dāng)配置文件發(fā)生改變后會(huì)被重新加載筏养。
scanPeriod:scan為true時(shí)此屬性生效斧抱。監(jiān)測(cè)配置文件是否發(fā)生修改的時(shí)間間隔(默認(rèn)1min)。如果沒(méi)有給出時(shí)間單位渐溶,默認(rèn)單位是毫秒辉浦。
debug:設(shè)置為true(默認(rèn)值為false)時(shí)會(huì)打印出logback內(nèi)部日志信息(實(shí)時(shí)查看logback運(yùn)行狀態(tài))。
-->
<configuration scan="false" scanPeriod="60 seconds" debug="false">
    <!-- 定義日志的根目錄 -->
    <property name="LOG_HOME" value="/Users/cx/spring-app/log"/>
    <!-- 定義日志文件名稱 -->
    <property name="appName" value="cx-spring-boot-logging"></property>
    <!-- 定義控制臺(tái)輸出 -->
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <!--
        日志輸出格式:
           %d:日期時(shí)間茎辐。
           %thread:線程名盏浙。
           %-5level:級(jí)別眉睹,從左顯示且5個(gè)字符寬度。
           %logger{50}:logger名字最長(zhǎng)50個(gè)字符废膘,否則按照句點(diǎn)分割。
           %msg:日志消息慕蔚。
           %n:換行符丐黄。
        -->
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread]**************** %-5level %logger{50} - %msg%n</pattern>
        </layout>
    </appender>

    <!-- 滾動(dòng)記錄文件,先將日志記錄到指定文件孔飒,當(dāng)符合某個(gè)條件時(shí)灌闺,將日志記錄到其他文件 -->
    <appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 指定日志文件的名稱 -->
        <file>${LOG_HOME}/${appName}.log</file>
        <!--
        當(dāng)發(fā)生滾動(dòng)時(shí),決定RollingFileAppender的行為坏瞄,涉及文件移動(dòng)和重命名桂对。
        TimeBasedRollingPolicy: 最常用的滾動(dòng)策略,它根據(jù)時(shí)間來(lái)制定滾動(dòng)策略鸠匀,既負(fù)責(zé)滾動(dòng)也負(fù)責(zé)發(fā)出滾動(dòng)蕉斜。
        -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--
            滾動(dòng)時(shí)產(chǎn)生的文件的存放位置及文件名稱 %d{yyyy-MM-dd}:按天進(jìn)行日志滾動(dòng)
            %i:當(dāng)文件大小超過(guò)maxFileSize時(shí),按照i進(jìn)行文件滾動(dòng)
            -->
            <fileNamePattern>${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
            <!--
            可選節(jié)點(diǎn)缀棍,控制保留的歸檔文件的最大數(shù)量宅此,超出數(shù)量就刪除舊文件。假設(shè)設(shè)置每天滾動(dòng)爬范,且maxHistory是365父腕,則只保存最近365天的文件,刪除之前的舊文件青瀑。注意璧亮,刪除舊文件時(shí),那些為了歸檔而創(chuàng)建的目錄也會(huì)被刪除斥难。
            -->
            <MaxHistory>365</MaxHistory>
            <!--
            當(dāng)日志文件超過(guò)maxFileSize指定的大小時(shí)枝嘶,根據(jù)上面提到的%i進(jìn)行日志文件滾動(dòng) 注意此處配置SizeBasedTriggeringPolicy是無(wú)法實(shí)現(xiàn)按文件大小進(jìn)行滾動(dòng)的,必須配置timeBasedFileNamingAndTriggeringPolicy
            -->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <!-- 日志輸出格式: -->
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [ %thread ] ------------------ [ %-5level ] [ %logger{50} : %line ] -
                %msg%n
            </pattern>
        </layout>
    </appender>

    <!--
    logger主要用于存放日志對(duì)象蘸炸,也可以定義日志類型躬络、級(jí)別。
      name:表示匹配的logger類型前綴(包路徑的前半部分)
      level:要記錄的日志級(jí)別搭儒,包括:TRACE < DEBUG < INFO < WARN < ERROR
      additivity:作用在于children-logger是否使用 rootLogger配置的appender進(jìn)行輸出穷当,false:表示只用當(dāng)前l(fā)ogger的appender-ref,true:表示當(dāng)前l(fā)ogger的appender-ref和rootLogger的appender-ref都有效淹禾。
    -->
    <!-- hibernate logger -->
    <logger name="com.sst.cx" level="debug"/>
    <!-- Spring framework logger -->
    <logger name="org.springframework" level="debug" additivity="false"></logger>

    <!--
    root與logger是父子關(guān)系馁菜。沒(méi)有特別定義則默認(rèn)為root,任何一個(gè)類只會(huì)和一個(gè)logger對(duì)應(yīng)铃岔,要么是定義的logger汪疮,要么是root峭火。判斷的關(guān)鍵在于找到這個(gè)logger,然后判斷這個(gè)logger的appender和level智嚷。
    -->
    <root level="info">
        <appender-ref ref="stdout"/>
        <appender-ref ref="appLogAppender"/>
    </root>
</configuration> 
例(logback-spring.xml)帶有spring標(biāo)識(shí)的日志配置文件

1. 將上例中的logback.xml文件名修改為:logback-spring.xml卖丸。修改控制臺(tái)日志輸出,通過(guò)Profile實(shí)現(xiàn)不同的環(huán)境使用不同的日志輸出格式:
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
       <layout class="ch.qos.logback.classic.PatternLayout">
            <!--開(kāi)發(fā)環(huán)境 日志輸出格式-->
            <springProfile name="dev">
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ----> [%thread] ---> %-5level %logger{50} - %msg%n</pattern>
            </springProfile>
            <!--非開(kāi)發(fā)環(huán)境 日志輸出格式-->
            <springProfile name="!dev">
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ==== [%thread] ==== %-5level %logger{50} - %msg%n</pattern>
            </springProfile>
        </layout>
    </appender>
2. 在SpringBoot項(xiàng)目的application.yml中盏道,激活開(kāi)發(fā)環(huán)境(dev)的 Profile稍浆,配置內(nèi)容如下:
#默認(rèn)配置
server:
  port: 8080
#在這里切換環(huán)境,dev猜嘱、test衅枫、prod
spring:
  profiles:
    active: dev
---
#開(kāi)發(fā)環(huán)境
server:
  port: 8081
spring:
  config:
    activate:
      on-profile: dev
---
#測(cè)試環(huán)境
server:
  port: 8082
spring:
  config:
    activate:
      on-profile: test
---
#生產(chǎn)環(huán)境
server:
  port: 8083
spring:
  config:
    activate:
      on-profile: prod

6. 靜態(tài)資源映射

在Web項(xiàng)目中,為了讓頁(yè)面更美觀朗伶、更好的用戶體驗(yàn)弦撩,會(huì)使用到大量的靜態(tài)資源(如: JS、CSS论皆、HTML益楼、jQuery、Bootstrap等)纯丸。
SpringMVC項(xiàng)目導(dǎo)入靜態(tài)資源(將靜態(tài)資源文件復(fù)制到webapp目錄下)后需要配置靜態(tài)資源的映射偏形。
而SpringBoot項(xiàng)目則不需要,SpringBoot默認(rèn)提供了3種靜態(tài)資源映射規(guī)則:
  1. WebJars映射(以Jar形式為Web項(xiàng)目提供資源文件)
    SpringBoot項(xiàng)目是以Jar包的形式進(jìn)行部署的觉鼻,因此不存在webapp目錄俊扭。
    WebJars可以將Web前端資源(JS,CSS 等)打成一個(gè)個(gè)的Jar包坠陈,然后將這些Jar包部署到Maven中央倉(cāng)庫(kù)中進(jìn)行統(tǒng)一管理萨惑。
    只需在SpringBoot項(xiàng)目的pom.xml中引入依賴(訪問(wèn)WebJars官網(wǎng),找到所需Web前端資源的pom依賴)仇矾。
    例(在SpringBoot項(xiàng)目中引入jquery庸蔼,只需在pom.xml中引入jquery依賴)
        <dependency>
          <groupId>org.webjars</groupId>
          <artifactId>jquery</artifactId>
          <version>3.6.0</version>
        </dependency>
        啟動(dòng)SpringBoot,在瀏覽器中訪問(wèn)“http://localhost:8080/webjars/jquery/3.6.0/jquery.js”訪問(wèn) jquery.js
    所有通過(guò)WebJars引入的前端資源都存放在當(dāng)前項(xiàng)目類路徑下的/META-INF/resources/webjars/目錄中贮匕。
    SpringBoot通過(guò)MVC的自動(dòng)配置類WebMvcAutoConfiguration為這些WebJars前端資源提供了默認(rèn)映射規(guī)則姐仅,部分源碼如下:
      public void addResourceHandlers(ResourceHandlerRegistry registry) {
        if (!this.resourceProperties.isAddMappings()) {
            logger.debug("Default resource handling disabled");
        } else {
            // WebJars 映射規(guī)則
            this.addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
            this.addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
                registration.addResourceLocations(this.resourceProperties.getStaticLocations());
                if (this.servletContext != null) {
                    ServletContextResource resource = new ServletContextResource(this.servletContext, "/");
                    registration.addResourceLocations(new Resource[]{resource});
                }
            });
        }
      }
    通過(guò)源碼可知,WebJars的映射路徑為"/webjars/**"(即所有訪問(wèn)"/webjars/**"的請(qǐng)求刻盐,都會(huì)去“classpath:/META-INF/resources/webjars/”下查找WebJars前端資源)掏膏。
  2. 默認(rèn)資源映射
    當(dāng)訪問(wèn)項(xiàng)目中的任意資源(即"/**")時(shí),SpringBoot會(huì)默認(rèn)從以下路徑(靜態(tài)資源目錄)中查找資源文件(優(yōu)先級(jí)依次降低)classpath表示resources目錄:
      1. classpath:/META-INF/resources/
      2. classpath:/resources/
      3. classpath:/static/
      4. classpath:/public/
  3. 靜態(tài)首頁(yè)(歡迎頁(yè))映射
    靜態(tài)資源目錄下的所有index.html被稱為靜態(tài)首頁(yè)或歡迎頁(yè)敦锌,會(huì)被"/**"映射(訪問(wèn)“/”或“/index.html”時(shí)會(huì)按優(yōu)先級(jí)查找 并跳轉(zhuǎn)到靜態(tài)首頁(yè))馒疹。
WebJars官網(wǎng)查找JQuery的pom依賴

7. Thymeleaf模板引擎(用于渲染xml/xhtml/html5內(nèi)容的模板引擎,取代JSP)

Thymeleaf作為新一代Java模板引擎乙墙,相比傳統(tǒng)Java模板引擎(JSP颖变、Velocity生均、FreeMaker):
  1. 支持HTML原型,其文件后綴為“.html”(因此可以直接被瀏覽器打開(kāi))腥刹。
  2. 通過(guò)在html標(biāo)簽中增加額外的屬性來(lái)達(dá)到“模板+數(shù)據(jù)”的展示方式马胧。

特點(diǎn):
  1. 動(dòng)靜結(jié)合(最大的特點(diǎn)):未啟動(dòng)Web應(yīng)用時(shí),也能在瀏覽器中顯示模板頁(yè)面(此時(shí)展示的是靜態(tài)內(nèi)容衔峰;通過(guò)Web應(yīng)用訪問(wèn)時(shí)會(huì)動(dòng)態(tài)替換掉靜態(tài)內(nèi)容)漓雅。
  2. 開(kāi)箱即用:Thymeleaf 提供了 Spring 標(biāo)準(zhǔn)方言以及一個(gè)與 SpringMVC 完美集成的可選模塊,可以快速的實(shí)現(xiàn)表單綁定朽色、屬性編輯器、國(guó)際化等功能组题。
  3. 多方言支持:它提供了 Thymeleaf 標(biāo)準(zhǔn)和 Spring 標(biāo)準(zhǔn)兩種方言葫男,可以直接套用模板實(shí)現(xiàn) JSTL、 OGNL 表達(dá)式崔列;必要時(shí)梢褐,開(kāi)發(fā)人員也可以擴(kuò)展和創(chuàng)建自定義的方言。
  4. 與SpringBoot完美整合:SpringBoot為T(mén)hymeleaf提供了默認(rèn)配置赵讯,并且還為T(mén)hymeleaf設(shè)置了視圖解析器盈咳。

<!DOCTYPE html>
<!-- 聲明thymeleaf命名空間 -->
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--th:text為T(mén)hymeleaf屬性,用于展示文本內(nèi)容-->
<h1 th:text="迎您來(lái)到Thymeleaf">歡迎您訪問(wèn)靜態(tài)頁(yè)面 HTML</h1>
</body>
</html>
未啟動(dòng)Web應(yīng)用時(shí)边翼,直接使用瀏覽器打開(kāi)顯示:歡迎您訪問(wèn)靜態(tài)頁(yè)面HTML鱼响。
使用Web應(yīng)用訪問(wèn)時(shí),顯示:迎您來(lái)到Thymeleaf组底。

使用(語(yǔ)法規(guī)則)

首先在html標(biāo)簽中聲明thymeleaf空間(避免編輯器出現(xiàn)html驗(yàn)證錯(cuò)誤丈积,非必須):
  <html xmlns:th="http://www.thymeleaf.org">

語(yǔ)法規(guī)則:
  1. 標(biāo)準(zhǔn)表達(dá)式語(yǔ)法
    1. 變量表達(dá)式(使用${}包裹的表達(dá)式)
      1. 獲取對(duì)象的屬性、方法
        ${person.lastName}
      2. 獲取內(nèi)置的基本對(duì)象及其屬性债鸡、方法
        ${#session.getAttribute('map')}  或  ${session.map}
        內(nèi)置的基本對(duì)象:
          1. #ctx :上下文對(duì)象江滨;
          2. #vars :上下文變量;
          3. #locale:上下文的語(yǔ)言環(huán)境厌均;
          4. #request:HttpServletRequest 對(duì)象(僅在Web應(yīng)用中可用)唬滑。
          5. #response:HttpServletResponse 對(duì)象(僅在Web應(yīng)用中可用)。
          6. #session:HttpSession對(duì)象(僅在Web應(yīng)用中可用)棺弊。
          7. #servletContext:ServletContext 對(duì)象(僅在Web應(yīng)用中可用)晶密。
      3. 獲取內(nèi)置的工具對(duì)象
        ${#strings.equals('張三',name)}
        內(nèi)置的工具對(duì)象:
          1. strings(字符串工具對(duì)象)
            常用方法:equals、equalsIgnoreCase镊屎、length惹挟、trim、toUpperCase缝驳、toLowerCase连锯、indexOf归苍、substring、replace运怖、startsWith拼弃、endsWith、contains摇展、containsIgnoreCase 等吻氧;
          2. numbers(數(shù)字工具對(duì)象)
            常用方法:formatDecimal 等;
          3. bools(布爾工具對(duì)象)
            常用方法:isTrue咏连、isFalse 等盯孙;
          4. arrays(數(shù)組工具對(duì)象)
            常用方法:toArray、length祟滴、isEmpty振惰、contains、containsAll 等垄懂;
          5. lists/sets(List/Set集合工具對(duì)象)
            常用方法:toList骑晶、size、isEmpty草慧、contains桶蛔、containsAll、sort 等漫谷;
          6. maps(Map 集合工具對(duì)象)
            常用方法:size仔雷、isEmpty、containsKey抖剿、containsValue 等朽寞;
          7. dates(日期工具對(duì)象)
            常用方法:format、year斩郎、month脑融、hour、createNow 等缩宜。
    2. 選擇變量表達(dá)式(使用*{}包裹的表達(dá)式)
      在變量表達(dá)式的基礎(chǔ)上增加了與th:object(用于存儲(chǔ)一個(gè)臨時(shí)變量肘迎,該變量只在該元素及其子元素中有效)的配合使用。
      使用th:object存儲(chǔ)一個(gè)對(duì)象后锻煌,可以在其子元素中使用選擇變量表達(dá)式*{name}獲取該對(duì)象(*代表該對(duì)象)中的屬性妓布。子元素中仍可使用${}變量表達(dá)式。
      例:
        <div th:object="${session.user}" >
          <p th:text="*{fisrtName}">firstname</p>
        </div>
    3. 鏈接表達(dá)式(使用@{}包裹的表達(dá)式)
      用于:靜態(tài)資源引用宋梧、form表單請(qǐng)求(凡是鏈接都可以用鏈接表達(dá)式)匣沼。
      1. 無(wú)參請(qǐng)求:@{/xxx}
      2. 有參請(qǐng)求:@{/xxx(k1=v1,k2=v2)}
      例:
        <link href="asserts/css/signin.css" th:href="@{/asserts/css/signin.css}" rel="stylesheet">
    4. 國(guó)際化表達(dá)式(使用#{}包裹的表達(dá)式)
      用于國(guó)際化的場(chǎng)景。
      th:text="#{msg}"
    5. 片段引用表達(dá)式(使用~{}包裹的表達(dá)式)
      用于在模板頁(yè)面中引用其他的模板片段捂龄。
      方式1.  ~{templatename::fragmentname}  推薦
      方式2.  ~{templatename::#id}
      說(shuō)明:
        1. templatename:模版名释涛,Thymeleaf會(huì)根據(jù)模版名解析完整路徑:/resources/templates/templatename.html加叁,要注意文件的路徑。
        2. fragmentname:片段名唇撬,Thymeleaf通過(guò)th:fragment聲明定義代碼塊(即:th:fragment="fragmentname")
        3. id:html的id選擇器它匕,使用時(shí)要在前面加上#號(hào),不支持class選擇器窖认。
  2. th屬性
    可以直接在HTML標(biāo)簽中使用豫柬。
常用的th屬性 描述 示例
th:id 替換id屬性 <input id="html-id" th:id="thymeleaf-id" />
th:text 文本替換,會(huì)轉(zhuǎn)義特殊字符 <h1 th:text="hello world" >hello</h1>
th:utext 文本替換扑浸,不會(huì)轉(zhuǎn)義特殊字符 <div th:utext="'<h1>hello world</h1>'" >世界你好</div>
th:object 在父標(biāo)簽中存儲(chǔ)一個(gè)對(duì)象烧给,子標(biāo)簽使用選擇變量表達(dá)式獲取該對(duì)象的屬性。 <div th:object="${session.user}" ><p th:text="*{fisrtName}">firstname</p></div>
th:value 替換value屬性 <input th:value = "${user.name}" />
th:with 局部變量賦值運(yùn)算 <div th:with="isEvens=${prodStat.count}%2==0" th:text="${isEvens}"></div>
th:style 設(shè)置樣式 <div th:style="'color:#F00; font-weight:bold'">hello</div>
th:onclick 點(diǎn)擊事件 <td th:onclick = "'getInfo()'"></td>
th:each 遍歷(支持Iterable喝噪、Map创夜、數(shù)組等)。 <table><tr th:each="m:${session.map}"><td th:text="${m.getKey()}"></td><td th:text="${m.getValue()}"></td></tr></table>
th:if 根據(jù)條件判斷是否需要展示此標(biāo)簽 <a th:if ="${userId == collect.userId}">
th:unless 和 th:if 判斷相反仙逻,滿足條件時(shí)不顯示 <div th:unless="${m.getKey()=='name'}" ></div>
th:switch 類似switch-case語(yǔ)句,與th:case配合使用涧尿,根據(jù)不同的條件展示不同的內(nèi)容系奉。 <div th:switch="${name}"><span th:case="a">hello</span><span th:case="b">world</span></div>
th:fragment 類似jsp的tag,用來(lái)定義一段被引用或包含的模板片段姑廉。 <footer th:fragment="footer">插入的內(nèi)容</footer>
th:insert 將使用th:fragment屬性指定的模板片段(包含標(biāo)簽)插入到當(dāng)前標(biāo)簽中缺亮。 <div th:insert="commons/bar::footer"></div>
th:replace 將使用th:fragment屬性指定的模板片段(包含標(biāo)簽)替換當(dāng)前整個(gè)標(biāo)簽。 <div th:replace="commons/bar::footer"></div>
th:selected select選擇框選中 <select><option th:selected="${name=='a'}">hello</option><option th:selected="${name=='b'}">world</option></select>
th:src 替換html中的src屬性 <img th:src="@{/asserts/img/bootstrap-solid.svg}" src="asserts/img/bootstrap-solid.svg" />
th:inline 內(nèi)聯(lián)屬性,有text桥言、none萌踱、javascript三種取值,在<script>標(biāo)簽中使用時(shí)号阿,js代碼中可以獲取到后臺(tái)傳遞頁(yè)面的對(duì)象。 <script type="text/javascript" th:inline="javascript">var name = 'hello world';alert(name)</script>
th:action 替換表單提交的地址 <form th:action="@{/user/login}" th:method="post"></form>

公共頁(yè)面的抽取和引用

Web項(xiàng)目的頁(yè)面中通常會(huì)存在一些重復(fù)代碼(如:頭部導(dǎo)航欄、側(cè)邊菜單欄审姓、公共的js/css等)近弟。可以把這些公共頁(yè)面片段抽取出來(lái)存放在一個(gè)獨(dú)立的頁(yè)面中枯夜,然后再由其他頁(yè)面根據(jù)需要進(jìn)行引用(消除代碼重復(fù)弯汰、使頁(yè)面更簡(jiǎn)潔)。

1. 抽取公共頁(yè)面
  將公共頁(yè)面片段抽取出來(lái)存放到一個(gè)獨(dú)立的頁(yè)面中湖雹,并使用th:fragment屬性命名咏闪。
    <div th:fragment="fragment-name" id="fragment-id">
        <span>公共頁(yè)面片段</span>
    </div>

2. 引用公共頁(yè)面
  可以通過(guò)以下3個(gè)屬性,將公共頁(yè)面片段引入到當(dāng)前頁(yè)面中摔吏。
    1. th:insert:將代碼塊片段整個(gè)插入到使用了th:insert屬性的html標(biāo)簽中鸽嫂。
    2. th:replace:將代碼塊片段整個(gè)替換使用了th:replace屬性的html標(biāo)簽中纵装。
    3. th:include:將代碼塊片段包含的內(nèi)容插入到使用了th:include屬性的html標(biāo)簽中。
  屬性值使用片段引用表達(dá)式引入引入頁(yè)面片段(通常溪胶,~{}可以省略)
    方式1. ~{templatename::#id}:模板名::選擇器
    方式2. ~{templatename::fragmentname}:模板名::片段名
  行內(nèi)寫(xiě)法為: [[~{...}]] 會(huì)轉(zhuǎn)義特殊字符搂擦、[(~{...})] 不會(huì)轉(zhuǎn)義特殊字符。
  例:
    <div th:insert="commons::fragment-name"></div>
  
  3. 傳遞參數(shù)
    引用公共頁(yè)面片段時(shí)哗脖,通過(guò)以下2種方式將參數(shù)傳入到被引用的頁(yè)面片段中:
      方式1(參數(shù)較多時(shí)使用該方式)明確指定參數(shù)名和參數(shù)值
        模板名::選擇器名或片段名(參數(shù)1=參數(shù)值1,參數(shù)2=參數(shù)值2)
      方式2(參數(shù)較少時(shí)使用該方式)
        模板名::選擇器名或片段名(參數(shù)值1,參數(shù)值2)

例(查看th:insert瀑踢、th:replace、th:include3者的區(qū)別)

1. 在頁(yè)面 fragment.html 中引入 commons.html 中聲明的頁(yè)面片段
<!--th:insert 片段名引入-->
<div th:insert="commons::fragment-name"></div>
<!--th:insert id 選擇器引入-->
<div th:insert="commons::#fragment-id"></div>
------------------------------------------------
<!--th:replace 片段名引入-->
<div th:replace="commons::fragment-name"></div>
<!--th:replace id 選擇器引入-->
<div th:replace="commons::#fragment-id"></div>
------------------------------------------------
<!--th:include 片段名引入-->
<div th:include="commons::fragment-name"></div>
<!--th:include id 選擇器引入-->
<div th:include="commons::#fragment-id"></div>

2. 啟動(dòng) Spring Boot才避,使用瀏覽器訪問(wèn)fragment.html橱夭,右鍵查看頁(yè)面源碼:
<!--th:insert 片段名引入-->
<div>
    <div id="fragment-id">
        <span>公共頁(yè)面片段</span>
    </div>
</div>
<!--th:insert id 選擇器引入-->
<div>
    <div id="fragment-id">
        <span>公共頁(yè)面片段</span>
    </div>
</div>
------------------------------------------------
<!--th:replace 片段名引入-->
<div id="fragment-id">
    <span>公共頁(yè)面片段</span>
</div>
<!--th:replace id 選擇器引入-->
<div id="fragment-id">
    <span>公共頁(yè)面片段</span>
</div>
------------------------------------------------
<!--th:include 片段名引入-->
<div>
    <span>公共頁(yè)面片段</span>
</div>
<!--th:include id 選擇器引入-->
<div>
    <span>公共頁(yè)面片段</span>
</div>

例(傳遞參數(shù))

<!--th:insert 片段名引入-->
<div th:insert="commons::fragment-name(var1='insert-name',var2='insert-name2')"></div>
<!--th:insert id 選擇器引入-->
<div th:insert="commons::#fragment-id(var1='insert-id',var2='insert-id2')"></div>
------------------------------------------------
<!--th:replace 片段名引入-->
<div th:replace="commons::fragment-name(var1='replace-name',var2='replace-name2')"></div>
<!--th:replace id 選擇器引入-->
<div th:replace="commons::#fragment-id(var1='replace-id',var2='replace-id2')"></div>
------------------------------------------------
<!--th:include 片段名引入-->
<div th:include="commons::fragment-name(var1='include-name',var2='include-name2')"></div>
<!--th:include id 選擇器引入-->
<div th:include="commons::#fragment-id(var1='include-id',var2='include-id2')"></div>

在公共頁(yè)面片段中使用:
<div th:fragment="fragment-name(var1,var2)" id="fragment-id">
    <p th:text="'參數(shù)1:'+${var1} + '-------------------參數(shù)2:' + ${var2}">...</p>
</div>

SpringBoot項(xiàng)目中使用Thymeleaf

SpringBoot推薦使用Thymeleaf作為模板引擎(為其提供了大量默認(rèn)配置)。

使用步驟
  1. 引入Thymeleaf依賴(在項(xiàng)目的pom.xml中添加spring-boot-starter-thymeleaf依賴)
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
  2. 創(chuàng)建.html模板文件桑逝,并放置在項(xiàng)目類路徑(resources目錄)的templates目錄下棘劣。
    例(hello.html):
      <!DOCTYPE html>
      <!--導(dǎo)入thymeleaf命名空間-->
      <html lang="en" xmlns:th="http://www.thymeleaf.org">
      <head>
          <meta charset="UTF-8">
          <title>Title</title>
      </head>
      <body>
            <h1 th:text="'歡迎來(lái)到'+${name}"></h1>
      </body>
      </html>
  3. 使用
    創(chuàng)建HelloController.java,通過(guò)參數(shù)map傳遞數(shù)據(jù)到頁(yè)面中:
      package com.sst.controller;
      import org.springframework.stereotype.Controller;
      import org.springframework.web.bind.annotation.RequestMapping;
      import java.util.Map;
      @Controller
      public class HelloController {
          @RequestMapping("/hello")
          public String hello(Map<String, Object> map) {
              map.put("name", "hello world");
              return "hello";
          }
      }
    啟動(dòng)SpringBoot楞遏,在瀏覽器中訪問(wèn):http://localhost:8080/hello
SpringBoot通過(guò)ThymeleafAutoConfiguration自動(dòng)配置類為T(mén)hymeleaf提供了一整套的自動(dòng)化配置方案茬暇。部分源碼如下:
    @Configuration(
        proxyBeanMethods = false
    )
    @EnableConfigurationProperties({ThymeleafProperties.class})
    @ConditionalOnClass({TemplateMode.class, SpringTemplateEngine.class})
    @AutoConfigureAfter({WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class})
    public class ThymeleafAutoConfiguration {
    }
使用@EnableConfigurationProperties注解導(dǎo)入了ThymeleafProperties類(包含了和Thymeleaf相關(guān)的自動(dòng)配置屬性)。其部分源碼如下:
    @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;
        private boolean cache;
        ...
    }
ThymeleafProperties通過(guò)@ConfigurationProperties注解將配置文件(application.properties/yml) 中前綴為spring.thymeleaf的配置和這個(gè)類中的屬性綁定寡喝。
ThymeleafProperties還提供了以下靜態(tài)變量:
  1. DEFAULT_ENCODING:默認(rèn)編碼格式
  2. DEFAULT_PREFIX:視圖解析器的前綴
  3. DEFAULT_SUFFIX:視圖解析器的后綴
根據(jù)以上配置屬性可知:
  1. Thymeleaf模板的默認(rèn)位置在resources/templates目錄下糙俗,默認(rèn)的后綴是html。
  2. 只要將html頁(yè)面放在該目錄下预鬓,Thymeleaf就能自動(dòng)進(jìn)行渲染巧骚。

8. 國(guó)際化(為不同的國(guó)家/語(yǔ)言提供相應(yīng)的頁(yè)面和數(shù)據(jù))

步驟:
  1. 創(chuàng)建國(guó)際化資源文件(在resources目錄的i18n目錄下)
    文件名格式:基本名_語(yǔ)言_國(guó)家.properties
    例(IDEA會(huì)自動(dòng)識(shí)別國(guó)際化資源文件并自動(dòng)添加Resouce Bundle目錄):
      hello.properties:默認(rèn)
      hello_zh_CN.properties:中文時(shí)生效
      hello_en_US.properties:英語(yǔ)時(shí)生效
    打開(kāi)任意一個(gè)國(guó)際化資源文件,切換為ResourceBundle模式(需要安裝ResourceBundle插件)格二,然后點(diǎn)擊“+”號(hào)創(chuàng)建所需的國(guó)際化屬性(需要進(jìn)行國(guó)際化的字段)劈彪。
  2. 使用ResourceBundleMessageSource管理國(guó)際化資源文件。
    在application.porperties/yml配置文件中添加spring.messages.basename來(lái)覆蓋默認(rèn)值(當(dāng)指定多個(gè)資源文件時(shí)顶猜,用逗號(hào)分隔)沧奴。
    例:
      spring.messages.basename=i18n.hello
  3. 在頁(yè)面中使用國(guó)際化表達(dá)式#{}來(lái)獲取國(guó)際化內(nèi)容。
切換為ResourceBundle模式

添加國(guó)際化資源
SpringBoot通過(guò)MessageSourceAutoConfiguration類對(duì)ResourceBundleMessageSource提供了默認(rèn)配置长窄。

===》MessageSourceAutoConfiguration類的部分源碼如下:
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(name = AbstractApplicationContext.MESSAGE_SOURCE_BEAN_NAME, search = SearchStrategy.CURRENT)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Conditional(org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration.ResourceBundleCondition.class)
@EnableConfigurationProperties
public class MessageSourceAutoConfiguration {
    private static final Resource[] NO_RESOURCES = {};
    // 將MessageSourceProperties以組件的形式添加到容器中扼仲,MessageSourceProperties下的每個(gè)屬性都與以spring.messages開(kāi)頭的屬性對(duì)應(yīng)。
    @Bean
    @ConfigurationProperties(prefix = "spring.messages")
    public MessageSourceProperties messageSourceProperties() {
        return new MessageSourceProperties();
    }
    // Spring Boot會(huì)從容器中獲取MessageSourceProperties抄淑,讀取國(guó)際化資源文件的basename(基本名)屠凶、encoding(編碼)等信息并封裝到 ResourceBundleMessageSource中。
    @Bean
    public MessageSource messageSource(MessageSourceProperties properties) {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        // 讀取國(guó)際化資源文件的basename (基本名),并封裝到ResourceBundleMessageSource中
        if (StringUtils.hasText(properties.getBasename())) {
            messageSource.setBasenames(StringUtils
                    .commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(properties.getBasename())));
        }
        // 讀取國(guó)際化資源文件的encoding (編碼),并封裝到ResourceBundleMessageSource中
        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;
    }
    ...
}
從源碼可知:
  1. SpringBoot將MessageSourceProperties以組件的形式添加到容器中肆资。
  2. MessageSourceProperties的屬性與配置文件中以“spring.messages”開(kāi)頭的配置進(jìn)行了綁定矗愧。
  3. SpringBoot從容器中獲取MessageSourceProperties組件,并從中讀取國(guó)際化資源文件的basename(文件基本名)、encoding(編碼)等信息唉韭,將它們封裝到 ResourceBundleMessageSource中夜涕。
  4. SpringBoot將ResourceBundleMessageSource以組件的形式添加到容器中,進(jìn)而實(shí)現(xiàn)對(duì)國(guó)際化資源文件的管理属愤。

===》MessageSourceProperties類的源碼如下:
public class MessageSourceProperties {
    private String basename = "messages";
    private Charset encoding;
    @DurationUnit(ChronoUnit.SECONDS)
    private Duration cacheDuration;
    private boolean fallbackToSystemLocale;
    private boolean alwaysUseMessageFormat;
    private boolean useCodeAsDefaultMessage;
    public MessageSourceProperties() {
        this.encoding = StandardCharsets.UTF_8;
        this.fallbackToSystemLocale = true;
        this.alwaysUseMessageFormat = false;
        this.useCodeAsDefaultMessage = false;
    }
    ...
}
從源碼可知:
  1. MessageSourceProperties為basename女器、encoding等屬性提供了默認(rèn)值。
  2. basename表示國(guó)際化資源文件的基本名住诸,其默認(rèn)值為“message”(即SpringBoot默認(rèn)會(huì)獲取類路徑下的message.properties以及message_XXX.properties作為國(guó)際化資源文件)驾胆。
  3. 在application.porperties/yml配置文件中,使用配置參數(shù)“spring.messages.basename”即可重新指定國(guó)際化資源文件的基本名贱呐。

1. 在resources目錄的i18n目錄下創(chuàng)建
  login.properties
  login_en_US.properties
  login_zh_CN.properties

2. 在application.porperties/yml配置文件中添加
  application.porperties
    spring.messages.basename=i18n.login
  application.yml
    spring:
      messages:
        basename: i18n/login
        encoding: utf-8

3. 獲取國(guó)際化內(nèi)容
  創(chuàng)建login.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.0, maximum-scale=1.0">
    <meta name="description" content="">
    <meta name="author" content="ThemeBucket">
    <link rel="shortcut icon" href="#" type="image/png">
    <title>Login</title>
    <!--將js css 等靜態(tài)資源的引用修改為 絕對(duì)路徑-->
    <link href="css/style.css" th:href="@{/css/style.css}" rel="stylesheet">
    <link href="css/style-responsive.css" th:href="@{/css/style-responsive.css}" rel="stylesheet">
    <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!--[if lt IE 9]>
    <script src="js/html5shiv.js" th:src="@{/js/html5shiv.js}"></script>
    <script src="js/respond.min.js" th:src="@{/js/respond.min.js}"></script>
    <![endif]-->
</head>
<body class="login-body">
<div class="container">
    <form class="form-signin" th:action="@{/user/login}" method="post">
        <div class="form-signin-heading text-center">
            <h1 class="sign-title" th:text="#{login.btn}">Sign In</h1>
            <img src="/images/login-logo.png" th:src="@{/images/login-logo.png}" alt=""/>
        </div>
        <div class="login-wrap">
            <p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>
            <input type="text" class="form-control" name="username" placeholder="User ID" autofocus
                   th:placeholder="#{login.username}"/>
            <input type="password" class="form-control" name="password" placeholder="Password"
                   th:placeholder="#{login.password}"/>
            <label class="checkbox">
                <input type="checkbox" value="remember-me" th:text="#{login.remember}">
                <span class="pull-right">
                    <a data-toggle="modal" href="#myModal" th:text="#{login.forgot}"> </a>
                </span>
            </label>
            <button class="btn btn-lg btn-login btn-block" type="submit">
                <i class="fa fa-check"></i>
            </button>

            <div class="registration">
                <!--Thymeleaf 行內(nèi)寫(xiě)法-->
                [[#{login.not-a-member}]]
                <a class="" href="/registration.html" th:href="@{/registration.html}">
                    [[#{login.signup}]]
                </a>
                <!--thymeleaf 模板引擎的參數(shù)用()代替 ?-->
                <br/>
                <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>
            </div>
        </div>
        <!-- Modal -->
        <div aria-hidden="true" aria-labelledby="myModalLabel" role="dialog" tabindex="-1" id="myModal"
             class="modal fade">
            <div class="modal-dialog">
                <div class="modal-content">
                    <div class="modal-header">
                        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
                        <h4 class="modal-title">Forgot Password ?</h4>
                    </div>
                    <div class="modal-body">
                        <p>Enter your e-mail address below to reset your password.</p>
                        <input type="text" name="email" placeholder="Email" autocomplete="off"
                               class="form-control placeholder-no-fix">
                    </div>
                    <div class="modal-footer">
                        <button data-dismiss="modal" class="btn btn-default" type="button">Cancel</button>
                        <button class="btn btn-primary" type="button">Submit</button>
                    </div>
                </div>
            </div>
        </div>
        <!-- modal -->
    </form>
</div>
<!-- Placed js at the end of the document so the pages load faster -->
<!-- Placed js at the end of the document so the pages load faster -->
<script src="js/jquery-1.10.2.min.js" th:src="@{/js/jquery-1.10.2.min.js}"></script>
<script src="js/bootstrap.min.js" th:src="@{/js/bootstrap.min.js}"></script>
<script src="js/modernizr.min.js" th:src="@{/js/modernizr.min.js}"></script>
</body>
</html>

手動(dòng)切換語(yǔ)言

1. 區(qū)域信息解析器自動(dòng)配置
  可以通過(guò)以下兩個(gè)對(duì)象對(duì)區(qū)域信息進(jìn)行切換奄薇,繼而切換語(yǔ)言驳阎。
    1. Locale(區(qū)域信息對(duì)象)
    2. LocaleResolver(區(qū)域信息解析器)容器中的組件,負(fù)責(zé)獲取區(qū)域信息對(duì)象
2. 手動(dòng)切換語(yǔ)言
  1. 修改login.html中的切換語(yǔ)言鏈接馁蒂,在請(qǐng)求中攜帶國(guó)際化區(qū)域信息呵晚,代碼如下
    <!--thymeleaf 模板引擎的參數(shù)用()代替 ?-->
    <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>
  2. 在com.sst.cx.component包下創(chuàng)建一個(gè)區(qū)域信息解析器MyLocalResolver沫屡,代碼如下
    package com.sst.cx.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;
    // 自定義區(qū)域信息解析器
    public class MyLocalResolver implements LocaleResolver {
        @Override
        public Locale resolveLocale(HttpServletRequest request) {
            // 獲取請(qǐng)求中參數(shù)
            String l = request.getParameter("l");
            // 獲取默認(rèn)的區(qū)域信息解析器
            Locale locale = Locale.getDefault();
            // 根據(jù)請(qǐng)求中的參數(shù)重新構(gòu)造區(qū)域信息對(duì)象
            if (StringUtils.hasText(l)) {
                String[] s = l.split("_");
                locale = new Locale(s[0], s[1]);
            }
            return locale;
        }
        @Override
        public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
        }
    }
  3. 在com.sst.cx.config包下的MyMvcConfig中添加以下方法劣纲,將自定義的區(qū)域信息解析器以組件的形式添加到容器中,代碼如下:
    // 將自定義的區(qū)域信息解析器以組件的形式添加到容器中
    @Bean
    public LocaleResolver localeResolver(){
        return new MyLocalResolver();
    }
===》SpringBoot在WebMvcAutoConfiguration類中為區(qū)域信息解析器進(jìn)行了自動(dòng)配置谁鳍,源碼如下:
    @Bean
    @ConditionalOnMissingBean(name = DispatcherServlet.LOCALE_RESOLVER_BEAN_NAME)
    @SuppressWarnings("deprecation")
    public LocaleResolver localeResolver() {
        if (this.webProperties.getLocaleResolver() == WebProperties.LocaleResolver.FIXED) {
            return new FixedLocaleResolver(this.webProperties.getLocale());
        }
        if (this.mvcProperties.getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
            return new FixedLocaleResolver(this.mvcProperties.getLocale());
        }
        AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
        Locale locale = (this.webProperties.getLocale() != null) ? this.webProperties.getLocale()
                : this.mvcProperties.getLocale();
        localeResolver.setDefaultLocale(locale);
        return localeResolver;
    }
從源碼可知:
  1. 該方法默認(rèn)向容器中添加了一個(gè)LocaleResolver區(qū)域信息解析器組件,它會(huì)根據(jù)請(qǐng)求頭中攜帶的“Accept-Language”參數(shù)劫瞳,獲取相應(yīng)Locale區(qū)域信息對(duì)象倘潜。
  2. 該方法上使用了@ConditionalOnMissingBean注解,其參數(shù)name的取值為 localeResolver(與該方法注入到容器中的組件名稱一致)志于,該注解的含義為:當(dāng)容器中不存在名稱為localResolver組件時(shí)涮因,該方法才會(huì)生效。即手動(dòng)向容器中添加一個(gè)名為“l(fā)ocaleResolver”的組件時(shí)伺绽,SpringBoot自動(dòng)配置的區(qū)域信息解析器會(huì)失效养泡,自定義的區(qū)域信息解析器則會(huì)生效。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末奈应,一起剝皮案震驚了整個(gè)濱河市澜掩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌杖挣,老刑警劉巖肩榕,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異惩妇,居然都是意外死亡株汉,警方通過(guò)查閱死者的電腦和手機(jī)筐乳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)乔妈,“玉大人蝙云,你說(shuō)我怎么就攤上這事÷氛伲” “怎么了勃刨?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)优训。 經(jīng)常有香客問(wèn)我朵你,道長(zhǎng),這世上最難降的妖魔是什么揣非? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任抡医,我火速辦了婚禮,結(jié)果婚禮上早敬,老公的妹妹穿的比我還像新娘忌傻。我一直安慰自己,他們只是感情好搞监,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布水孩。 她就那樣靜靜地躺著,像睡著了一般琐驴。 火紅的嫁衣襯著肌膚如雪俘种。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,144評(píng)論 1 285
  • 那天绝淡,我揣著相機(jī)與錄音宙刘,去河邊找鬼。 笑死牢酵,一個(gè)胖子當(dāng)著我的面吹牛悬包,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播馍乙,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼布近,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了丝格?” 一聲冷哼從身側(cè)響起撑瞧,我...
    開(kāi)封第一講書(shū)人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎显蝌,沒(méi)想到半個(gè)月后季蚂,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年扭屁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了算谈。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡料滥,死狀恐怖然眼,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情葵腹,我是刑警寧澤高每,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站践宴,受9級(jí)特大地震影響鲸匿,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜阻肩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一带欢、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧烤惊,春花似錦乔煞、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至雄右,卻和暖如春空骚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背擂仍。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工囤屹, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人防楷。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像则涯,于是被迫代替她去往敵國(guó)和親复局。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容