SpringBoot筆記

image.png

一、Spring與spring boot

1雪标、spring能做什么

spring的能力:
微服務(wù)零院、響應(yīng)式編程、云村刨、web告抄、Serverless、
Event Driven嵌牺、Batch
spring的生態(tài):
覆蓋了web開發(fā)打洼、數(shù)據(jù)訪問、安全控制逆粹、分布式募疮、消息服務(wù)、移動(dòng)開發(fā)僻弹、批處理

2阿浓、為什么用spring boot

springboot優(yōu)點(diǎn):
創(chuàng)建獨(dú)立spring應(yīng)用、內(nèi)嵌web服務(wù)器蹋绽、自動(dòng)starter依賴芭毙,簡化構(gòu)建配置筋蓖、自動(dòng)配置spring以及第三方功能、提供生產(chǎn)級別的監(jiān)控退敦、健康檢查以及外部優(yōu)化配置粘咖、無代碼生成無需編寫XML
springboot缺點(diǎn):
版本迭代快、封裝太深不易精通

3侈百、如何學(xué)習(xí)SpringBoot

官方文檔架構(gòu)

二瓮下、springboot2入門

1、系統(tǒng)要求

  • java8 & 兼容java14 .
  • Maven 3.3+
  • idea 2019.1.2

2 钝域、HelloWorld

需求:瀏覽發(fā)送/hello請求讽坏,響應(yīng) Hello,Spring Boot 2

2.1网梢、創(chuàng)建maven工程

2.2震缭、引入依賴

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
    </parent>


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

    </dependencies>

2.3、創(chuàng)建主程序

/**
 * 主程序類
 * @SpringBootApplication:這是一個(gè)SpringBoot應(yīng)用
 */
@SpringBootApplication
public class MainApplication {

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

2.4战虏、編寫業(yè)務(wù)

@RestController
public class HelloController {


    @RequestMapping("/hello")
    public String handle01(){
        return "Hello, Spring Boot 2!";
    }

}

2.5拣宰、測試

直接運(yùn)行main方法

2.6、簡化配置

application.properties
server.port=8888

2.7烦感、簡化部署

<build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

把項(xiàng)目打成jar包花枫,直接在目標(biāo)服務(wù)器執(zhí)行即可欧引。

三乌企、了解自動(dòng)配置原理

image.png

1拱层、Spring boot特點(diǎn)

1.1、依賴管理

  • 父項(xiàng)目做依賴管理
依賴管理    
<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
</parent>

他的父項(xiàng)目
 <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.3.4.RELEASE</version>
  </parent>

幾乎聲明了所有開發(fā)中常用的依賴的版本號,自動(dòng)版本仲裁機(jī)制
  • 開發(fā)導(dǎo)入starter場景啟動(dòng)器
1绿渣、見到很多 spring-boot-starter-* : *就某種場景
2朝群、只要引入starter,這個(gè)場景的所有常規(guī)需要的依賴我們都自動(dòng)引入
3中符、SpringBoot所有支持的場景
https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter
4姜胖、見到的  *-spring-boot-starter: 第三方為我們提供的簡化開發(fā)的場景啟動(dòng)器。
5淀散、所有場景啟動(dòng)器最底層的依賴
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter</artifactId>
  <version>2.3.4.RELEASE</version>
  <scope>compile</scope>
</dependency>
  • 無需關(guān)注版本號右莱,自動(dòng)版本仲裁
1、引入依賴默認(rèn)都可以不寫版本
2档插、引入非版本仲裁的jar慢蜓,要寫版本號。
  • 可以修改默認(rèn)版本號
1郭膛、查看spring-boot-dependencies里面規(guī)定當(dāng)前依賴的版本 用的 key晨抡。
2、在當(dāng)前項(xiàng)目里面重寫配置
    <properties>
        <mysql.version>5.1.43</mysql.version>
    </properties>

1.2、自動(dòng)配置

  • 自動(dòng)配好Tomcat
    ○ 引入Tomcat依賴耘柱。
    ○ 配置Tomcat
<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-tomcat</artifactId>
      <version>2.3.4.RELEASE</version>
      <scope>compile</scope>
    </dependency>
  • 自動(dòng)配好SpringMVC
    ○ 引入SpringMVC全套組件
    ○ 自動(dòng)配好SpringMVC常用組件(功能)
  • 自動(dòng)配好Web常見功能圆雁,如:字符編碼問題
    SpringBoot幫我們配置好了所有web開發(fā)的常見場景
  • 默認(rèn)的包結(jié)構(gòu)
    ○ 主程序所在包及其下面的所有子包里面的組件都會被默認(rèn)掃描進(jìn)來
    ○ 無需以前的包掃描配置
    ○ 想要改變掃描路徑,
@SpringBootApplication(scanBasePackages="com.atguigu")
  或者@ComponentScan 指定掃描路徑

@SpringBootApplication
等同于
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.atguigu.boot")
  • 各種配置擁有默認(rèn)值
    ○ 默認(rèn)配置最終都是映射到某個(gè)類上帆谍,如:MultipartProperties
    ○ 配置文件的值最終會綁定每個(gè)類上,這個(gè)類會在容器中創(chuàng)建對象
  • 按需加載所有自動(dòng)配置項(xiàng)
    ○ 非常多的starter
    ○ 引入了哪些場景這個(gè)場景的自動(dòng)配置才會開啟
    ○ SpringBoot所有的自動(dòng)配置功能都在 spring-boot-autoconfigure 包里面

2轴咱、容器功能

2.1汛蝙、組件添加

1、@Configuration
■ 配置類組件之間無依賴關(guān)系用Lite模式加速容器啟動(dòng)過程朴肺,減少判斷
■ 配置類組件之間有依賴關(guān)系窖剑,方法會被調(diào)用得到之前單實(shí)例組件,用Full模式

#############################Configuration使用示例######################################################
/**
 * 1戈稿、配置類里面使用@Bean標(biāo)注在方法上給容器注冊組件西土,默認(rèn)也是單實(shí)例的
 * 2、配置類本身也是組件
 * 3鞍盗、proxyBeanMethods:代理bean的方法
 *      Full(proxyBeanMethods = true)需了、【保證每個(gè)@Bean方法被調(diào)用多少次返回的組件都是單實(shí)例的】
 *      Lite(proxyBeanMethods = false)【每個(gè)@Bean方法被調(diào)用多少次返回的組件都是新創(chuàng)建的】
 *      組件依賴必須使用Full模式默認(rèn)。其他默認(rèn)是否Lite模式
 *
 *
 *
 */
@Configuration(proxyBeanMethods = false) //告訴SpringBoot這是一個(gè)配置類 == 配置文件
public class MyConfig {

    /**
     * Full:外部無論對配置類中的這個(gè)組件注冊方法調(diào)用多少次獲取的都是之前注冊容器中的單實(shí)例對象
     * @return
     */
    @Bean //給容器中添加組件般甲。以方法名作為組件的id肋乍。返回類型就是組件類型。返回的值敷存,就是組件在容器中的實(shí)例
    public User user01(){
        User zhangsan = new User("zhangsan", 18);
        //user組件依賴了Pet組件
        zhangsan.setPet(tomcatPet());
        return zhangsan;
    }

    @Bean("tom")
    public Pet tomcatPet(){
        return new Pet("tomcat");
    }
}
################################@Configuration測試代碼如下########################################
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.atguigu.boot")
public class MainApplication {

    public static void main(String[] args) {
        //1墓造、返回我們IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);

        //2、查看容器里面的組件
        String[] names = run.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }

        //3锚烦、從容器中獲取組件

        Pet tom01 = run.getBean("tom", Pet.class);

        Pet tom02 = run.getBean("tom", Pet.class);

        System.out.println("組件:"+(tom01 == tom02));


        //4觅闽、com.atguigu.boot.config.MyConfig$$EnhancerBySpringCGLIB$$51f1e1ca@1654a892
        MyConfig bean = run.getBean(MyConfig.class);
        System.out.println(bean);

        //如果@Configuration(proxyBeanMethods = true)代理對象調(diào)用方法。SpringBoot總會檢查這個(gè)組件是否在容器中有涮俄。
        //保持組件單實(shí)例
        User user = bean.user01();
        User user1 = bean.user01();
        System.out.println(user == user1);


        User user01 = run.getBean("user01", User.class);
        Pet tom = run.getBean("tom", Pet.class);

        System.out.println("用戶的寵物:"+(user01.getPet() == tom));



    }
}

2蛉拙、@Bean、@Component禽拔、@Controller刘离、@Service、@Repository
3睹栖、@ComponentScan硫惕、@Import

 @Import({User.class, DBHelper.class})
 *      給容器中自動(dòng)創(chuàng)建出這兩個(gè)類型的組件、默認(rèn)組件的名字就是全類名

@Import({User.class, DBHelper.class})
@Configuration(proxyBeanMethods = false) //告訴SpringBoot這是一個(gè)配置類 == 配置文件
public class MyConfig {
}

4.@Conditional
條件裝配:滿足Conditional指定的條件野来,則進(jìn)行組件注入

=====================測試條件裝配==========================
@Configuration(proxyBeanMethods = false) //告訴SpringBoot這是一個(gè)配置類 == 配置文件
//@ConditionalOnBean(name = "tom")
@ConditionalOnMissingBean(name = "tom")
public class MyConfig {


    /**
     * Full:外部無論對配置類中的這個(gè)組件注冊方法調(diào)用多少次獲取的都是之前注冊容器中的單實(shí)例對象
     * @return
     */

    @Bean //給容器中添加組件恼除。以方法名作為組件的id。返回類型就是組件類型。返回的值豁辉,就是組件在容器中的實(shí)例
    public User user01(){
        User zhangsan = new User("zhangsan", 18);
        //user組件依賴了Pet組件
        zhangsan.setPet(tomcatPet());
        return zhangsan;
    }

    @Bean("tom22")
    public Pet tomcatPet(){
        return new Pet("tomcat");
    }
}

public static void main(String[] args) {
        //1令野、返回我們IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);

        //2、查看容器里面的組件
        String[] names = run.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }

        boolean tom = run.containsBean("tom");
        System.out.println("容器中Tom組件:"+tom);

        boolean user01 = run.containsBean("user01");
        System.out.println("容器中user01組件:"+user01);

        boolean tom22 = run.containsBean("tom22");
        System.out.println("容器中tom22組件:"+tom22);


    }

2.2原生配置文件引入

1徽级、@ImportResource

@ImportResource("classpath:beans.xml")
public class MyConfig {}

2.3 配置綁定

如何使用Java讀取到properties文件中的內(nèi)容气破,并且把它封裝到JavaBean中,以供隨時(shí)使用餐抢;
1现使、@ConfigurationProperties

/**
 * 只有在容器中的組件,才會擁有SpringBoot提供的強(qiáng)大功能
 */
@Component
@ConfigurationProperties(prefix = "mycar")
public class Car {

    private String brand;
    private Integer price;

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public Integer getPrice() {
        return price;
    }

    public void setPrice(Integer price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Car{" +
                "brand='" + brand + '\'' +
                ", price=" + price +
                '}';
    }
}

方法一:@EnableConfigurationProperties + @ConfigurationProperties

方法二:@Component + @ConfigurationProperties

@EnableConfigurationProperties(Car.class)
*     開啟car的配置綁定功能旷痕,開啟后Car類中就可以不用寫@Component
*     把Car這個(gè)組件注冊到容器中
public class MyConfig {
}

3碳锈、自動(dòng)配置原理入門

3.1、引導(dǎo)加載自動(dòng)配置類

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

1欺抗、@SpringBootConfiguration
@Configuration售碳。代表當(dāng)前是一個(gè)配置類
2、@ComponentScan
指定掃描哪些绞呈,Spring注解贸人;
3、@EnableAutoConfiguration

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}

@AutoConfigurationPackage
自動(dòng)配置包佃声?指定了默認(rèn)的包規(guī)則

@Import(AutoConfigurationPackages.Registrar.class)  //給容器中導(dǎo)入一個(gè)組件
public @interface AutoConfigurationPackage {}

//利用Registrar給容器中導(dǎo)入一系列組件
//將指定的一個(gè)包下的所有組件導(dǎo)入進(jìn)來灸姊?MainApplication 所在包下。

@Import(AutoConfigurationImportSelector.class)

1秉溉、利用getAutoConfigurationEntry(annotationMetadata);給容器中批量導(dǎo)入一些組件
2力惯、調(diào)用List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)獲取到所有需要導(dǎo)入到容器中的配置類
3、利用工廠加載 Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader)召嘶;得到所有的組件
4父晶、從META-INF/spring.factories位置來加載一個(gè)文件。
    默認(rèn)掃描我們當(dāng)前系統(tǒng)里面所有META-INF/spring.factories位置的文件
    spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面也有META-INF/spring.factories
文件里面寫死了spring-boot一啟動(dòng)就要給容器中加載的所有配置類

3.2弄跌、按需開啟自動(dòng)配置項(xiàng)

雖然我們127個(gè)場景的所有自動(dòng)配置啟動(dòng)的時(shí)候默認(rèn)全部加載甲喝。xxxxAutoConfiguration
按照條件裝配規(guī)則(@Conditional),最終會按需配置铛只。

3.3埠胖、修改默認(rèn)配置

SpringBoot默認(rèn)會在底層配好所有的組件。但是如果用戶自己配置了以用戶的優(yōu)先
用戶可以使用@Bean注解來或者修改配置文件來自己配置組件

@Bean
    @ConditionalOnMissingBean
    public CharacterEncodingFilter characterEncodingFilter() {
    }

總結(jié):
● SpringBoot先加載所有的自動(dòng)配置類 xxxxxAutoConfiguration
● 每個(gè)自動(dòng)配置類按照條件進(jìn)行生效淳玩,默認(rèn)都會綁定配置文件指定的值直撤。xxxxProperties里面拿。xxxProperties和配置文件進(jìn)行了綁定
● 生效的配置類就會給容器中裝配很多組件
● 只要容器中有這些組件蜕着,相當(dāng)于這些功能就有了
● 定制化配置
○ 用戶直接自己@Bean替換底層的組件
○ 用戶去看這個(gè)組件是獲取的配置文件什么值就去修改谋竖。
xxxxxAutoConfiguration ---> 組件 ---> xxxxProperties里面拿值 ----> application.properties

3.4 最佳實(shí)踐

1红柱、查看自動(dòng)配置了哪些

  • 自己分析,引入場景對應(yīng)的自動(dòng)配置一般都生效了蓖乘。
  • 或配置文件中debug=true開啟自動(dòng)配置報(bào)告锤悄。Negative(不生效)\Positive(生效)

2、是否需要修改配置

  • 可以參照文檔修改配置項(xiàng)
  • 可以自己分析嘉抒。xxxxProperties綁定了配置文件的哪些零聚。
  • 可以自定義加入或者替換組件
    @Bean、@Component些侍。握牧。。
  • 可以使用自定義器 XXXXXCustomizer娩梨;

4、開發(fā)小技巧

4.1览徒、Lombok

================================簡化日志開發(fā)===================================
@Slf4j
@RestController
public class HelloController {
    @RequestMapping("/hello")
    public String handle01(@RequestParam("name") String name){
        
        log.info("請求進(jìn)來了....");
        
        return "Hello, Spring Boot 2!"+"你好:"+name;
    }
}

4.2狈定、dev-tools

熱部署

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>

項(xiàng)目或者頁面修改以后:Ctrl+F9;

4.3习蓬、Spring Initailizr

自動(dòng)依賴引入纽什、自動(dòng)創(chuàng)建項(xiàng)目結(jié)構(gòu)、自動(dòng)編寫好主配置類

四躲叼、配置文件

文件類型可為properties 或者 yaml

增加配置提示

自定義的類和配置文件綁定一般沒有提示芦缰。
可加入以下依賴使得在寫yml文件時(shí),自定義的類也會有提示

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>


 <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.springframework.boot</groupId>
                            <artifactId>spring-boot-configuration-processor</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

五枫慷、深入Web原理

image.png

1让蕾、springmvc自動(dòng)配置概覽

  • 大部分都已經(jīng)幫助我們自動(dòng)配置,包括:
    內(nèi)容協(xié)商視圖解析器和BeanName視圖解析器
    靜態(tài)資源(包括webjars)
    自動(dòng)注冊Converter, GenericConverter, Formatter
    支持HttpMessageConverters
    自動(dòng)注冊MessageCodesResolver(國際化用)
    靜態(tài)index.html支持
    自定義Favicon
    自動(dòng)使用ConfigurableWebBindingInitializer, (DataBinder負(fù)責(zé)將請求數(shù)據(jù)綁定到JavaBean上)
  • 自定義MVC方法:
    ①不用@EnableWebMvc注解或听,使用@Configuration+WebMvcConfigurer自定義規(guī)則
    ②聲明WebMvcRegistrations 改變默認(rèn)底層組件
    ③使用@EnableWebMvc+@configuration+DelegatingWebMvcConfiguration全面接管SpringMVC

2探孝、簡單功能分析

2.1靜態(tài)資源訪問

1.靜態(tài)資源目錄
只要靜態(tài)資源放在類路徑下: called /static (or /public or /resources or /META-INF/resources
訪問 : 當(dāng)前項(xiàng)目根路徑/ + 靜態(tài)資源名
原理: 靜態(tài)映射/**。
請求進(jìn)來誉裆,先去找Controller看能不能處理顿颅。不能處理的所有請求又都交給靜態(tài)資源處理器。靜態(tài)資源也找不到則響應(yīng)404頁面
改變默認(rèn)的靜態(tài)資源路徑:

spring:
  resources:
    static-locations: [classpath:/haha/]

2足丢、靜態(tài)資源訪問前綴

spring:
  mvc:
    static-path-pattern: /res/**

當(dāng)前項(xiàng)目 + static-path-pattern + 靜態(tài)資源名 = 靜態(tài)資源文件夾下找
3粱腻、webjar
自動(dòng)映射 /webjars/** (添加依賴后,路徑mapping也可以直接訪問webjars里面的東西斩跌,webjars也算靜態(tài)資源)

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

eg:http://localhost:8080/webjars/jquery/3.5.1/jquery.js

2.2歡迎頁支持

靜態(tài)資源路徑下 index.html
可以配置靜態(tài)資源路徑
但是不可以配置靜態(tài)資源的訪問前綴绍些。否則導(dǎo)致 index.html不能被默認(rèn)訪問
controller能處理/index

2.3自定義Favicon

favicon.ico 放在靜態(tài)資源目錄下即可

2.4靜態(tài)資源配置原理

SpringMvc功能的自動(dòng)配置類WebMvcAutoConfiguration生效->配置文件的相關(guān)屬性和xxx類進(jìn)行了綁定。(WebMvcProperties==spring.mvc耀鸦、ResourceProperties==spring.resources)

spring:
  resources:
    add-mappings: false   禁用所有靜態(tài)資源規(guī)則

3遇革、請求參數(shù)原理

3.1 請求映射

1、rest使用與原理
● @xxxMapping;
● Rest風(fēng)格支持(使用HTTP請求方式動(dòng)詞來表示對資源的操作)
○ 以前:/getUser 獲取用戶 /deleteUser 刪除用戶 /editUser 修改用戶 /saveUser 保存用戶
○ 現(xiàn)在: /user GET-獲取用戶 DELETE-刪除用戶 PUT-修改用戶 POST-保存用戶
○ 核心Filter萝快;HiddenHttpMethodFilter
■ 用法: 表單method=post锻霎,隱藏域 _method=put
■ SpringBoot中手動(dòng)開啟

Rest原理(表單提交要使用REST的時(shí)候)
● 表單提交會帶上_method=PUT
● 請求過來被HiddenHttpMethodFilter攔截
○ 請求是否正常,并且是POST
■ 獲取到_method的值揪漩。
■ 兼容以下請求旋恼;PUT.DELETE.PATCH
■ 原生request(post),包裝模式requesWrapper重寫了getMethod方法奄容,返回的是傳入的值冰更。
■ 過濾器鏈放行的時(shí)候用wrapper。以后的方法調(diào)用getMethod是調(diào)用requesWrapper的昂勒。

Rest使用客戶端工具蜀细,
● 如PostMan直接發(fā)送Put、delete等方式請求戈盈,無需Filter

2請求映射原理

SpringMVC功能分析都從 org.springframework.web.servlet.DispatcherServlet->doDispatch()
RequestMappingHandlerMapping:保存了所有@RequestMapping 和handler的映射規(guī)則奠衔。
所有的請求映射(controller路徑與資源的映射)都在HandlerMapping中。
● 請求進(jìn)來塘娶,挨個(gè)嘗試所有的HandlerMapping看是否有請求信息归斤。
○ 如果有就找到這個(gè)請求對應(yīng)的handler
○ 如果沒有就是下一個(gè) HandlerMapping

3.2普通參數(shù)與基本注解

2.1、注解
@PathVariable刁岸、@RequestHeader脏里、@ModelAttribute、@RequestParam虹曙、@MatrixVariable迫横、@CookieValue、@RequestBody

@MatrixVariable 矩陣變量:

 //1酝碳、語法: 請求路徑:/cars/sell;low=34;brand=byd,audi,yd
    //2员淫、SpringBoot默認(rèn)是禁用了矩陣變量的功能
    //      手動(dòng)開啟:原理。對于路徑的處理击敌。UrlPathHelper進(jìn)行解析介返。
    //              removeSemicolonContent(移除分號內(nèi)容)支持矩陣變量的
    //3、矩陣變量必須有url路徑變量才能被解析
    @GetMapping("/cars/{path}")
    public Map carsSell(@MatrixVariable("low") Integer low,
                        @MatrixVariable("brand") List<String> brand,
                        @PathVariable("path") String path){
        Map<String,Object> map = new HashMap<>();

        map.put("low",low);
        map.put("brand",brand);
        map.put("path",path);
        return map;
    }

    // /boss/1;age=20/2;age=10

    @GetMapping("/boss/{bossId}/{empId}")
    public Map boss(@MatrixVariable(value = "age",pathVar = "bossId") Integer bossAge,
                    @MatrixVariable(value = "age",pathVar = "empId") Integer empAge){
        Map<String,Object> map = new HashMap<>();

        map.put("bossAge",bossAge);
        map.put("empAge",empAge);
        return map;

    }

2.2 Servlet API:
ServletRequestMethodArgumentResolver 可以解析的參數(shù):
WebRequest沃斤、ServletRequest圣蝎、MultipartRequest、 HttpSession衡瓶、javax.servlet.http.PushBuilder徘公、Principal、InputStream哮针、Reader关面、HttpMethod坦袍、Locale、TimeZone等太、ZoneId

2.3 復(fù)雜參數(shù):
Map捂齐、Model(map、model里面的數(shù)據(jù)會被放在request的請求域 request.setAttribute)缩抡、Errors/BindingResult奠宜、RedirectAttributes( 重定向攜帶數(shù)據(jù))、ServletResponse(response)瞻想、SessionStatus压真、UriComponentsBuilder、ServletUriComponentsBuilder

Map<String,Object> map, Model model, HttpServletRequest request 都是可以給request域中放數(shù)據(jù)蘑险,
Map和Model的底層都是同一個(gè)對象

3.3POJO封裝過程

ServletModelAttributeMethodProcessor

3.4參數(shù)處理原理

● HandlerMapping中找到能處理請求的Handler(Controller.method())
● 為當(dāng)前Handler 找一個(gè)適配器 HandlerAdapter滴肿; RequestMappingHandlerAdapter
● 適配器執(zhí)行目標(biāo)方法并確定方法參數(shù)的每一個(gè)值

處理步驟:
1、HandlerAdapter
2佃迄、執(zhí)行目標(biāo)方法
3泼差、參數(shù)解析器-HandlerMethodArgumentResolver
確定將要執(zhí)行的目標(biāo)方法的每一個(gè)參數(shù)的值是什么;
SpringMVC目標(biāo)方法能寫多少種參數(shù)類型。取決于參數(shù)解析器
● 當(dāng)前解析器是否支持解析這種參數(shù)
● 支持就調(diào)用 resolveArgument
4和屎、返回值處理器
5、如何確定目標(biāo)方法每一個(gè)參數(shù)的值
5.1 挨個(gè)判斷所有參數(shù)解析器那個(gè)支持解析這個(gè)參數(shù)
5.2 解析這個(gè)參數(shù)的值
5.3 自定義類型參數(shù) 封裝POJO
ServletModelAttributeMethodProcessor 這個(gè)參數(shù)處理器支持是否為簡單類型春瞬。
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
WebDataBinder :web數(shù)據(jù)綁定器柴信,將請求參數(shù)的值綁定到指定的JavaBean里面
WebDataBinder 利用它里面的 Converters 將請求數(shù)據(jù)轉(zhuǎn)成指定的數(shù)據(jù)類型。再次封裝到JavaBean中
GenericConversionService:在設(shè)置每一個(gè)值的時(shí)候宽气,找它里面的所有converter那個(gè)可以將這個(gè)數(shù)據(jù)類型(request帶來參數(shù)的字符串)轉(zhuǎn)換到指定的類型
6随常、目標(biāo)方法執(zhí)行完成
將所有的數(shù)據(jù)都放在ModelAndViewContainer;包含要去的頁面地址View萄涯。還包含Model數(shù)據(jù)绪氛。
7、處理派發(fā)結(jié)果
暴露模型作為請求域?qū)傩?br> exposeModelAsRequestAttributes(model, request);
model中的所有數(shù)據(jù)遍歷挨個(gè)放在請求域中

4涝影、數(shù)據(jù)相應(yīng)與內(nèi)容協(xié)商

4.1響應(yīng)JSON

1.1 jackson.jar+@ResponseBody

web場景自動(dòng)引入了json場景
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-json</artifactId>
      <version>2.3.4.RELEASE</version>
      <scope>compile</scope>
    </dependency>

給前端自動(dòng)返回json數(shù)據(jù)枣察;
1、返回值解析器
2燃逻、返回值解析器原理
● 1序目、返回值處理器判斷是否支持這種類型返回值 supportsReturnType
● 2、返回值處理器調(diào)用 handleReturnValue 進(jìn)行處理
● 3伯襟、RequestResponseBodyMethodProcessor 可以處理返回值標(biāo)了@ResponseBody 注解的猿涨。
○ 1. 利用 MessageConverters 進(jìn)行處理 將數(shù)據(jù)寫為json
■ 1、內(nèi)容協(xié)商(瀏覽器默認(rèn)會以請求頭的方式告訴服務(wù)器他能接受什么樣的內(nèi)容類型)
■ 2姆怪、服務(wù)器最終根據(jù)自己自身的能力叛赚,決定服務(wù)器能生產(chǎn)出什么樣內(nèi)容類型的數(shù)據(jù)澡绩,
■ 3、SpringMVC會挨個(gè)遍歷所有容器底層的 HttpMessageConverter 俺附,看誰能處理肥卡?
● 1、得到MappingJackson2HttpMessageConverter可以將對象寫為json
● 2昙读、利用MappingJackson2HttpMessageConverter將對象轉(zhuǎn)為json再寫出去召调。
1.2、SpringMVC到底支持哪些返回值

ModelAndView
Model
View
ResponseEntity 
ResponseBodyEmitter
StreamingResponseBody
HttpEntity
HttpHeaders
Callable
DeferredResult
ListenableFuture
CompletionStage
WebAsyncTask
有 @ModelAttribute 且為對象類型的
@ResponseBody 注解 ---> RequestResponseBodyMethodProcessor蛮浑;

1.3唠叛、HTTPMessageConverter原理
1、MessageConverter規(guī)范
HttpMessageConverter: 看是否支持將 此 Class類型的對象沮稚,轉(zhuǎn)為MediaType類型的數(shù)據(jù)艺沼。
2、默認(rèn)的MessageConverter
最終 MappingJackson2HttpMessageConverter 把對象轉(zhuǎn)為JSON(利用底層的jackson的objectMapper轉(zhuǎn)換的)

4.2 內(nèi)容協(xié)商

根據(jù)客戶端接收能力不同蕴掏,返回不同媒體類型的數(shù)據(jù)障般。
1、引入xml依賴

 <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
</dependency>

2盛杰、postman分別測試返回json和xml
只需要改變請求頭中Accept字段挽荡。Http協(xié)議中規(guī)定的,告訴服務(wù)器本客戶端可以接收的數(shù)據(jù)類型即供。
3定拟、開啟瀏覽器參數(shù)方式內(nèi)容協(xié)商功能
為了方便內(nèi)容協(xié)商,開啟基于請求參數(shù)的內(nèi)容協(xié)商功能逗嫡。

spring:
    contentnegotiation:
      favor-parameter: true  #開啟請求參數(shù)內(nèi)容協(xié)商模式

eg:
http://localhost:8080/test/person?format=json
4青自、內(nèi)容協(xié)商原理
● 1、判斷當(dāng)前響應(yīng)頭中是否已經(jīng)有確定的媒體類型驱证。MediaType
● 2延窜、獲取客戶端(PostMan、瀏覽器)支持接收的內(nèi)容類型抹锄。(獲取客戶端Accept請求頭字段)【application/xml】
用最佳匹配媒體類型 的converter逆瑞。調(diào)用它進(jìn)行轉(zhuǎn)化

5.視圖解析與模板引擎

視圖解析:SpringBoot默認(rèn)不支持 JSP,需要引入第三方模板引擎技術(shù)實(shí)現(xiàn)頁面渲染伙单。

5.1 視圖解析

視圖解析原理流程
1呆万、目標(biāo)方法處理的過程中,所有數(shù)據(jù)都會被放在 ModelAndViewContainer 里面车份。包括數(shù)據(jù)和視圖地址
2谋减、方法的參數(shù)是一個(gè)自定義類型對象(從請求參數(shù)中確定的),把他重新放在 ModelAndViewContainer
3扫沼、任何目標(biāo)方法執(zhí)行完成以后都會返回 ModelAndView(數(shù)據(jù)和視圖地址)出爹。
4庄吼、processDispatchResult 處理派發(fā)結(jié)果(頁面改如何響應(yīng))
● 1、render(mv, request, response); 進(jìn)行頁面渲染邏輯
○ 1严就、根據(jù)方法的String返回值得到 View 對象【定義了頁面的渲染邏輯】
■ 1总寻、所有的視圖解析器嘗試是否能根據(jù)當(dāng)前返回值得到View對象
■ 2、得到了 redirect:/main.html --> Thymeleaf new RedirectView()
■ 3梢为、ContentNegotiationViewResolver 里面包含了下面所有的視圖解析器渐行,內(nèi)部還是利用下面所有視圖解析器得到視圖對象。
■ 4铸董、view.render(mv.getModelInternal(), request, response); 視圖對象調(diào)用自定義的render進(jìn)行頁面渲染工作
● RedirectView 如何渲染【重定向到一個(gè)頁面】
● 1祟印、獲取目標(biāo)url地址
● 2、response.sendRedirect(encodedURL);

5.2粟害、模板引擎-Thymeleaf

6蕴忆、攔截器

6.1、HandlerInterceptor 接口

6.2悲幅、配置攔截器

/**
 * 1套鹅、編寫一個(gè)攔截器實(shí)現(xiàn)HandlerInterceptor接口
 * 2、攔截器注冊到容器中(實(shí)現(xiàn)WebMvcConfigurer的addInterceptors)
 * 3汰具、指定攔截規(guī)則【如果是攔截所有卓鹿,靜態(tài)資源也會被攔截】
 */
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**")  //所有請求都被攔截包括靜態(tài)資源
                .excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**"); //放行的請求
    }
}

6.3、攔截器原理

1留荔、根據(jù)當(dāng)前請求吟孙,找到HandlerExecutionChain【可以處理請求的handler以及handler的所有 攔截器】
2、先來順序執(zhí)行 所有攔截器的 preHandle方法
● 1存谎、如果當(dāng)前攔截器prehandler返回為true拔疚。則執(zhí)行下一個(gè)攔截器的preHandle
● 2肥隆、如果當(dāng)前攔截器返回為false既荚。直接 倒序執(zhí)行所有已經(jīng)執(zhí)行了的攔截器的 afterCompletion;
3栋艳、如果任何一個(gè)攔截器返回false恰聘。直接跳出不執(zhí)行目標(biāo)方法
4、所有攔截器都返回True吸占。執(zhí)行目標(biāo)方法
5晴叨、倒序執(zhí)行所有攔截器的postHandle方法。
6矾屯、前面的步驟有任何異常都會直接倒序觸發(fā) afterCompletion
7兼蕊、頁面成功渲染完成以后,也會倒序觸發(fā) afterCompletion


image.png

7件蚕、文件上傳

7.1孙技、頁面表單

<form method="post" action="/upload" enctype="multipart/form-data">
    <input type="file" name="file"><br>
    <input type="submit" value="提交">
</form>

7.2产禾、文件上傳代碼

@PostMapping("/upload")
    public String upload(@RequestParam("email") String email,
                         @RequestParam("username") String username,
                         @RequestPart("headerImg") MultipartFile headerImg,
                         @RequestPart("photos") MultipartFile[] photos) throws IOException {

        log.info("上傳的信息:email={},username={}牵啦,headerImg={}亚情,photos={}",
                email,username,headerImg.getSize(),photos.length);

        if(!headerImg.isEmpty()){
            //保存到文件服務(wù)器,OSS服務(wù)器
            String originalFilename = headerImg.getOriginalFilename();
            headerImg.transferTo(new File("H:\\cache\\"+originalFilename));
        }

        if(photos.length > 0){
            for (MultipartFile photo : photos) {
                if(!photo.isEmpty()){
                    String originalFilename = photo.getOriginalFilename();
                    photo.transferTo(new File("H:\\cache\\"+originalFilename));
                }
            }
        }


        return "main";
    }

7.3哈雏、自動(dòng)配置原理

文件上傳自動(dòng)配置類-MultipartAutoConfiguration-MultipartProperties
● 自動(dòng)配置好了 StandardServletMultipartResolver 【文件上傳解析器】
● 原理步驟
○ 1楞件、請求進(jìn)來使用文件上傳解析器判斷(isMultipart)并封裝(resolveMultipart,返回MultipartHttpServletRequest)文件上傳請求
○ 2裳瘪、參數(shù)解析器來解析請求中的文件內(nèi)容封裝成MultipartFile
○ 3土浸、將request中文件信息封裝為一個(gè)Map;MultiValueMap<String, MultipartFile>
FileCopyUtils盹愚。實(shí)現(xiàn)文件流的拷貝

8栅迄、異常處理

錯(cuò)誤處理
1、默認(rèn)規(guī)則
● 默認(rèn)情況下皆怕,Spring Boot提供/error處理所有錯(cuò)誤的映射
● 對于機(jī)器客戶端毅舆,它將生成JSON響應(yīng),其中包含錯(cuò)誤愈腾,HTTP狀態(tài)和異常消息的詳細(xì)信息憋活。對于瀏覽器客戶端,響應(yīng)一個(gè)“ whitelabel”錯(cuò)誤視圖虱黄,以HTML格式呈現(xiàn)相同的數(shù)據(jù)
error/下的4xx悦即,5xx頁面會被自動(dòng)解析
2、定制錯(cuò)誤處理邏輯
● 自定義錯(cuò)誤頁
○ error/404.html error/5xx.html橱乱;有精確的錯(cuò)誤狀態(tài)碼頁面就匹配精確辜梳,沒有就找 4xx.html;如果都沒有就觸發(fā)白頁
● @ControllerAdvice+@ExceptionHandler處理全局異常泳叠;底層是 ExceptionHandlerExceptionResolver 支持的
● @ResponseStatus+自定義異常 作瞄;底層是 ResponseStatusExceptionResolver ,把responsestatus注解的信息底層調(diào)用 response.sendError(statusCode, resolvedReason)危纫;tomcat發(fā)送的/error
● Spring底層的異常宗挥,如 參數(shù)類型轉(zhuǎn)換異常;
自定義實(shí)現(xiàn) HandlerExceptionResolver 處理異常种蝶;可以作為默認(rèn)的全局異常處理規(guī)則.
● ErrorViewResolver 實(shí)現(xiàn)自定義處理異常契耿;
○ response.sendError 。error請求就會轉(zhuǎn)給controller
○ 你的異常沒有任何人能處理螃征。tomcat底層 response.sendError搪桂。error請求就會轉(zhuǎn)給controller
○ basicErrorController 要去的頁面地址是 ErrorViewResolver

3、異常處理自動(dòng)配置原理
● ErrorMvcAutoConfiguration 自動(dòng)配置異常處理規(guī)則
○ 容器中的組件:類型:DefaultErrorAttributes -> id:errorAttributes
■ public class DefaultErrorAttributes implements ErrorAttributes, HandlerExceptionResolver
■ DefaultErrorAttributes:定義錯(cuò)誤頁面中可以包含哪些數(shù)據(jù)盯滚。
○ 容器中的組件:類型:BasicErrorController --> id:basicErrorController(json+白頁 適配響應(yīng))
■ 處理默認(rèn) /error 路徑的請求踢械;頁面響應(yīng) new ModelAndView("error", model)拙泽;
■ 容器中有組件 View->id是error;(響應(yīng)默認(rèn)錯(cuò)誤頁)
■ 容器中放組件 BeanNameViewResolver(視圖解析器)裸燎;按照返回的視圖名作為組件的id去容器中找View對象顾瞻。
○ 容器中的組件:類型:DefaultErrorViewResolver -> id:conventionErrorViewResolver
■ 如果發(fā)生錯(cuò)誤,會以HTTP的狀態(tài)碼 作為視圖頁地址(viewName)德绿,找到真正的頁面
■ error/404荷荤、5xx.html
如果想要返回頁面;就會找error視圖【StaticView】移稳。(默認(rèn)是一個(gè)白頁)

4蕴纳、異常處理步驟流程
1、執(zhí)行目標(biāo)方法个粱,目標(biāo)方法運(yùn)行期間有任何異常都會被catch古毛、而且標(biāo)志當(dāng)前請求結(jié)束;并且用 dispatchException
2都许、進(jìn)入視圖解析流程(頁面渲染稻薇?)
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
3、mv = processHandlerException胶征;處理handler發(fā)生的異常塞椎,處理完成返回ModelAndView;
● 1睛低、遍歷所有的 handlerExceptionResolvers案狠,看誰能處理當(dāng)前異常
2、系統(tǒng)默認(rèn)的 異常解析器
○ 1钱雷、DefaultErrorAttributes先來處理異常骂铁。把異常信息保存到rrequest域,并且返回null罩抗;
○ 2拉庵、默認(rèn)沒有任何人能處理異常锚国,所以異常會被拋出
■ 1、如果沒有任何人能處理最終底層就會發(fā)送 /error 請求娃属。會被底層的BasicErrorController處理
■ 2温亲、解析錯(cuò)誤視圖;遍歷所有的 ErrorViewResolver 看誰能解析跟畅。
■ 3、默認(rèn)的 DefaultErrorViewResolver ,作用是把響應(yīng)狀態(tài)碼作為錯(cuò)誤頁的地址,error/500.html
■ 4馍刮、模板引擎最終響應(yīng)這個(gè)頁面 error/500.html

9、Web原生組件注入(Servlet窃蹋、Filter卡啰、Listener)

1静稻、使用Servlet API
@ServletComponentScan(basePackages = "com.atguigu.admin") :指定原生Servlet組件都放在那里
@WebServlet(urlPatterns = "/my"):效果:直接響應(yīng),不用經(jīng)過Spring的攔截器
@WebFilter(urlPatterns={"/css/","/images/"})
@WebListener
2匈辱、使用RegistrationBean
ServletRegistrationBean, FilterRegistrationBean, and ServletListenerRegistrationBean

10振湾、嵌入式Servlet容器

1、切換嵌入式Servlet容器

  • 默認(rèn)支持的webServer

  • Tomcat, Jetty, or Undertow

  • ServletWebServerApplicationContext 容器啟動(dòng)尋找ServletWebServerFactory 并引導(dǎo)創(chuàng)建服務(wù)器

  • 切換服務(wù)器

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

● 原理
○ SpringBoot應(yīng)用啟動(dòng)發(fā)現(xiàn)當(dāng)前是Web應(yīng)用亡脸。web場景包-導(dǎo)入tomcat
○ web應(yīng)用會創(chuàng)建一個(gè)web版的ioc容器 ServletWebServerApplicationContext
○ ServletWebServerApplicationContext 啟動(dòng)的時(shí)候?qū)ふ?ServletWebServerFactory(Servlet 的web服務(wù)器工廠---> Servlet 的web服務(wù)器)
○ SpringBoot底層默認(rèn)有很多的WebServer工廠押搪;TomcatServletWebServerFactory, JettyServletWebServerFactory, or UndertowServletWebServerFactory
○ 底層直接會有一個(gè)自動(dòng)配置類。ServletWebServerFactoryAutoConfiguration
○ ServletWebServerFactoryAutoConfiguration導(dǎo)入了ServletWebServerFactoryConfiguration(配置類)
○ ServletWebServerFactoryConfiguration 配置類 根據(jù)動(dòng)態(tài)判斷系統(tǒng)中到底導(dǎo)入了那個(gè)Web服務(wù)器的包浅碾。(默認(rèn)是web-starter導(dǎo)入tomcat包)大州,容器中就有 TomcatServletWebServerFactory
○ TomcatServletWebServerFactory 創(chuàng)建出Tomcat服務(wù)器并啟動(dòng);TomcatWebServer 的構(gòu)造器擁有初始化方法initialize---this.tomcat.start();
○ 內(nèi)嵌服務(wù)器垂谢,就是手動(dòng)把啟動(dòng)服務(wù)器的代碼調(diào)用(tomcat核心jar包存在)

2厦画、定制Servlet容器
● 實(shí)現(xiàn) WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>
○ 把配置文件的值和ServletWebServerFactory 進(jìn)行綁定
● 修改配置文件 server.xxx
● 直接自定義 ConfigurableServletWebServerFactory
xxxxxCustomizer:定制化器,可以改變xxxx的默認(rèn)規(guī)則

@Component
public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {

    @Override
    public void customize(ConfigurableServletWebServerFactory server) {
        server.setPort(9000);
    }

}

11滥朱、定制化原理

1根暑、定制化的常見方式
● 修改配置文件;
● xxxxxCustomizer徙邻;
● 編寫自定義的配置類 xxxConfiguration购裙;+ @Bean替換、增加容器中默認(rèn)組件鹃栽;視圖解析器
● Web應(yīng)用 編寫一個(gè)配置類實(shí)現(xiàn) WebMvcConfigurer 即可定制化web功能躏率;+ @Bean給容器中再擴(kuò)展一些組件
@EnableWebMvc + WebMvcConfigurer —— @Bean 可以全面接管SpringMVC,所有規(guī)則全部自己重新配置民鼓; 實(shí)現(xiàn)定制和擴(kuò)展功能

2薇芝、原理分析套路
場景starter - xxxxAutoConfiguration - 導(dǎo)入xxx組件 - 綁定xxxProperties -- 綁定配置文件項(xiàng)

六、數(shù)據(jù)訪問

1丰嘉、SQL

1.1數(shù)據(jù)源的自動(dòng)配置-HikariDataSource

1夯到、導(dǎo)入JDBC場景
2、分析自動(dòng)配置
1饮亏、自動(dòng)配置的類
● DataSourceAutoConfiguration : 數(shù)據(jù)源的自動(dòng)配置
○ 修改數(shù)據(jù)源相關(guān)的配置:spring.datasource
○ 數(shù)據(jù)庫連接池的配置耍贾,是自己容器中沒有DataSource才自動(dòng)配置的
○ 底層配置好的連接池是:HikariDataSource
● DataSourceTransactionManagerAutoConfiguration: 事務(wù)管理器的自動(dòng)配置
● JdbcTemplateAutoConfiguration: JdbcTemplate的自動(dòng)配置,可以來對數(shù)據(jù)庫進(jìn)行crud
○ 可以修改這個(gè)配置項(xiàng)@ConfigurationProperties(prefix = "spring.jdbc") 來修改JdbcTemplate
○ @Bean@Primary JdbcTemplate路幸;容器中有這個(gè)組件
● JndiDataSourceAutoConfiguration: jndi的自動(dòng)配置
● XADataSourceAutoConfiguration: 分布式事務(wù)相關(guān)的
3荐开、修改配置項(xiàng)

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/db_account
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver

4、測試

1.2使用Druid數(shù)據(jù)源

自定義方式
1简肴、創(chuàng)建數(shù)據(jù)源

<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.17</version>
        </dependency>

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
        destroy-method="close">
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <property name="maxActive" value="20" />
        <property name="initialSize" value="1" />
        <property name="maxWait" value="60000" />
        <property name="minIdle" value="1" />
        <property name="timeBetweenEvictionRunsMillis" value="60000" />
        <property name="minEvictableIdleTimeMillis" value="300000" />
        <property name="testWhileIdle" value="true" />
        <property name="testOnBorrow" value="false" />
        <property name="testOnReturn" value="false" />
        <property name="poolPreparedStatements" value="true" />
        <property name="maxOpenPreparedStatements" value="20" />

2晃听、StatViewServlet
StatViewServlet的用途包括:
提供監(jiān)控信息展示的html頁面
提供監(jiān)控信息的JSON API

    <servlet>
        <servlet-name>DruidStatView</servlet-name>
        <servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>DruidStatView</servlet-name>
        <url-pattern>/druid/*</url-pattern>
    </servlet-mapping>

3、StatFilter
用于統(tǒng)計(jì)監(jiān)控信息;如SQL監(jiān)控能扒、URI監(jiān)控

使用官方starter方式
1佣渴、引入druid-starter
、分析自動(dòng)配置
● 擴(kuò)展配置項(xiàng) spring.datasource.druid
● DruidSpringAopConfiguration.class, 監(jiān)控SpringBean的初斑;配置項(xiàng):spring.datasource.druid.aop-patterns
● DruidStatViewServletConfiguration.class, 監(jiān)控頁的配置:spring.datasource.druid.stat-view-servlet辛润;默認(rèn)開啟
● DruidWebStatFilterConfiguration.class, web監(jiān)控配置;spring.datasource.druid.web-stat-filter见秤;默認(rèn)開啟
● DruidFilterConfiguration.class}) 所有Druid自己filter的配置
3频蛔、配置示例

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/db_account
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver

    druid:
      aop-patterns: com.atguigu.admin.*  #監(jiān)控SpringBean
      filters: stat,wall     # 底層開啟功能,stat(sql監(jiān)控)秦叛,wall(防火墻)

      stat-view-servlet:   # 配置監(jiān)控頁功能
        enabled: true
        login-username: admin
        login-password: admin
        resetEnable: false

      web-stat-filter:  # 監(jiān)控web
        enabled: true
        urlPattern: /*
        exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'


      filter:
        stat:    # 對上面filters里面的stat的詳細(xì)配置
          slow-sql-millis: 1000
          logSlowSql: true
          enabled: true
        wall:
          enabled: true
          config:
            drop-table-allow: false

1.3整合MyBatis操作

1晦溪、配置模式
● 全局配置文件
● SqlSessionFactory: 自動(dòng)配置好了
● SqlSession:自動(dòng)配置了 SqlSessionTemplate 組合了SqlSession
● @Import(AutoConfiguredMapperScannerRegistrar.class);
● Mapper: 只要我們寫的操作MyBatis的接口標(biāo)準(zhǔn)了 @Mapper 就會被自動(dòng)掃描進(jìn)來

配置 private Configuration configuration; mybatis.configuration下面的所有挣跋,就是相當(dāng)于改mybatis全局配置文件中的值

# 配置mybatis規(guī)則
mybatis:
#  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml
  configuration:
    map-underscore-to-camel-case: true
    
 可以不寫全局三圆;配置文件,所有全局配置文件的配置都放在configuration配置項(xiàng)中即可

● 導(dǎo)入mybatis官方starter
● 編寫mapper接口避咆。標(biāo)準(zhǔn)@Mapper注解
● 編寫sql映射文件并綁定mapper接口
● 在application.yaml中指定Mapper配置文件的位置舟肉,以及指定全局配置文件的信息 (建議;配置在mybatis.configuration)

2查库、注解模式

@Mapper
public interface CityMapper {

    @Select("select * from city where id=#{id}")
    public City getById(Long id);

    public void insert(City city);

}

1.4整合 MyBatis-Plus 完成CRUD

優(yōu)點(diǎn):
● 只需要我們的Mapper繼承 BaseMapper 就可以擁有crud能力

2路媚、NOSQL

Redis 是一個(gè)開源(BSD許可)的,內(nèi)存中的數(shù)據(jù)結(jié)構(gòu)存儲系統(tǒng)樊销,它可以用作數(shù)據(jù)庫整慎、緩存和消息中間件

1、Redis自動(dòng)配置
自動(dòng)配置:
● RedisAutoConfiguration 自動(dòng)配置類围苫。RedisProperties 屬性類 --> spring.redis.xxx是對redis的配置
● 連接工廠是準(zhǔn)備好的裤园。LettuceConnectionConfiguration、JedisConnectionConfiguration
● 自動(dòng)注入了RedisTemplate<Object, Object> : xxxTemplate剂府;
● 自動(dòng)注入了StringRedisTemplate拧揽;k:v都是String
● key:value
● 底層只要我們使用 StringRedisTemplate、RedisTemplate就可以操作redis

redis環(huán)境搭建
1腺占、阿里云按量付費(fèi)redis淤袜。經(jīng)典網(wǎng)絡(luò)
2、申請redis的公網(wǎng)連接地址
3衰伯、修改白名單 允許0.0.0.0/0 訪問

2铡羡、RedisTemplate與Lettuce

3、切換至jedis

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

<!--        導(dǎo)入jedis-->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
spring:
  redis:
      host: r-bp1nc7reqesxisgxpipd.redis.rds.aliyuncs.com
      port: 6379
      password: lfy:Lfy123456
      client-type: jedis
      jedis:
        pool:
          max-active: 10

七嚎研、單元測試

1蓖墅、JUNIT5的變化

以前:
@SpringBootTest + @RunWith(SpringTest.class)

現(xiàn)在:
SpringBoot整合Junit以后。
● 編寫測試方法:@Test標(biāo)注(注意需要使用junit5版本的注解)
● Junit類具有Spring的功能临扮,@Autowired论矾、比如 @Transactional 標(biāo)注測試方法,測試完成后自動(dòng)回滾

2杆勇、JUnit5常用注解

● @Test :表示方法是測試方法贪壳。但是與JUnit4的@Test不同,他的職責(zé)非常單一不能聲明任何屬性蚜退,拓展的測試將會由Jupiter提供額外測試
● @ParameterizedTest :表示方法是參數(shù)化測試
● @RepeatedTest :表示方法可重復(fù)執(zhí)行
● @DisplayName :為測試類或者測試方法設(shè)置展示名稱
● @BeforeEach :表示在每個(gè)單元測試之前執(zhí)行
● @AfterEach :表示在每個(gè)單元測試之后執(zhí)行
● @BeforeAll :表示在所有單元測試之前執(zhí)行
● @AfterAll :表示在所有單元測試之后執(zhí)行
● @Tag :表示單元測試類別闰靴,類似于JUnit4中的@Categories
● @Disabled :表示測試類或測試方法不執(zhí)行,類似于JUnit4中的@Ignore
● @Timeout :表示測試方法運(yùn)行如果超過了指定時(shí)間將會返回錯(cuò)誤
● @ExtendWith :為測試類或測試方法提供擴(kuò)展類引用

3钻注、斷言(assertions)

斷言(assertions)是測試方法中的核心部分蚂且,用來對測試需要滿足的條件進(jìn)行驗(yàn)證。這些斷言方法都是 org.junit.jupiter.api.Assertions 的靜態(tài)方法
1幅恋、簡單斷言
assertEquals 判斷兩個(gè)對象或兩個(gè)原始類型是否相等
assertNotEquals 判斷兩個(gè)對象或兩個(gè)原始類型是否不相等
assertSame 判斷兩個(gè)對象引用是否指向同一個(gè)對象
assertNotSame 判斷兩個(gè)對象引用是否指向不同的對象
assertTrue 判斷給定的布爾值是否為 true
assertFalse 判斷給定的布爾值是否為 false
assertNull 判斷給定的對象引用是否為 null
assertNotNull 判斷給定的對象引用是否不為 null

@Test
@DisplayName("simple assertion")
public void simple() {
     assertEquals(3, 1 + 2, "simple math");
     assertNotEquals(3, 1 + 1);

     assertNotSame(new Object(), new Object());
     Object obj = new Object();
     assertSame(obj, obj);

     assertFalse(1 > 2);
     assertTrue(1 < 2);

     assertNull(null);
     assertNotNull(new Object());
}

2杏死、數(shù)組斷言
通過 assertArrayEquals 方法來判斷兩個(gè)對象或原始類型的數(shù)組是否相等

@Test
@DisplayName("array assertion")
public void array() {
 assertArrayEquals(new int[]{1, 2}, new int[] {1, 2});
}

3、組合斷言
assertAll 方法接受多個(gè) org.junit.jupiter.api.Executable 函數(shù)式接口的實(shí)例作為要驗(yàn)證的斷言捆交,可以通過 lambda 表達(dá)式很容易的提供這些斷言

@Test
@DisplayName("assert all")
public void all() {
 assertAll("Math",
    () -> assertEquals(2, 1 + 1),
    () -> assertTrue(1 > 0)
 );
}

4淑翼、異常斷言
在JUnit4時(shí)期,想要測試方法的異常情況時(shí)品追,需要用@Rule注解的ExpectedException變量還是比較麻煩的玄括。而JUnit5提供了一種新的斷言方式Assertions.assertThrows() ,配合函數(shù)式編程就可以進(jìn)行使用。

@Test
@DisplayName("異常測試")
public void exceptionTest() {
    ArithmeticException exception = Assertions.assertThrows(
           //扔出斷言異常
            ArithmeticException.class, () -> System.out.println(1 % 0));

}

5肉瓦、超時(shí)斷言
Junit5還提供了Assertions.assertTimeout() 為測試方法設(shè)置了超時(shí)時(shí)間

@Test
@DisplayName("超時(shí)測試")
public void timeoutTest() {
    //如果測試方法時(shí)間超過1s將會異常
    Assertions.assertTimeout(Duration.ofMillis(1000), () -> Thread.sleep(500));
}

6遭京、快速失敗
通過 fail 方法直接使得測試失敗

@Test
@DisplayName("fail")
public void shouldFail() {
 fail("This should fail");
}

4、前置條件(assumptions)

JUnit 5 中的前置條件(assumptions【假設(shè)】)類似于斷言泞莉,不同之處在于不滿足的斷言會使得測試方法失敗洁墙,而不滿足的前置條件只會使得測試方法的執(zhí)行終止。前置條件可以看成是測試方法執(zhí)行的前提戒财,當(dāng)該前提不滿足時(shí)热监,就沒有繼續(xù)執(zhí)行的必要。

@DisplayName("前置條件")
public class AssumptionsTest {
 private final String environment = "DEV";
 
 @Test
 @DisplayName("simple")
 public void simpleAssume() {
    assumeTrue(Objects.equals(this.environment, "DEV"));
    assumeFalse(() -> Objects.equals(this.environment, "PROD"));
 }
 
 @Test
 @DisplayName("assume then do")
 public void assumeThenDo() {
    assumingThat(
       Objects.equals(this.environment, "DEV"),
       () -> System.out.println("In DEV")
    );
 }
}

assumeTrue 和 assumFalse 確保給定的條件為 true 或 false饮寞,不滿足條件會使得測試執(zhí)行終止孝扛。assumingThat 的參數(shù)是表示條件的布爾值和對應(yīng)的 Executable 接口的實(shí)現(xiàn)對象。只有條件滿足時(shí)幽崩,Executable 對象才會被執(zhí)行苦始;當(dāng)條件不滿足時(shí),測試執(zhí)行并不會終止

5慌申、嵌套測試

Unit 5 可以通過 Java 中的內(nèi)部類和@Nested 注解實(shí)現(xiàn)嵌套測試陌选,從而可以更好的把相關(guān)的測試方法組織在一起理郑。在內(nèi)部類中可以使用@BeforeEach 和@AfterEach 注解,而且嵌套的層次沒有限制咨油。

@DisplayName("A stack")
class TestingAStackDemo {

    Stack<Object> stack;

    @Test
    @DisplayName("is instantiated with new Stack()")
    void isInstantiatedWithNew() {
        new Stack<>();
    }

    @Nested
    @DisplayName("when new")
    class WhenNew {

        @BeforeEach
        void createNewStack() {
            stack = new Stack<>();
        }

        @Test
        @DisplayName("is empty")
        void isEmpty() {
            assertTrue(stack.isEmpty());
        }

        @Test
        @DisplayName("throws EmptyStackException when popped")
        void throwsExceptionWhenPopped() {
            assertThrows(EmptyStackException.class, stack::pop);
        }

        @Test
        @DisplayName("throws EmptyStackException when peeked")
        void throwsExceptionWhenPeeked() {
            assertThrows(EmptyStackException.class, stack::peek);
        }

        @Nested
        @DisplayName("after pushing an element")
        class AfterPushing {

            String anElement = "an element";

            @BeforeEach
            void pushAnElement() {
                stack.push(anElement);
            }

            @Test
            @DisplayName("it is no longer empty")
            void isNotEmpty() {
                assertFalse(stack.isEmpty());
            }

            @Test
            @DisplayName("returns the element when popped and is empty")
            void returnElementWhenPopped() {
                assertEquals(anElement, stack.pop());
                assertTrue(stack.isEmpty());
            }

            @Test
            @DisplayName("returns the element when peeked but remains not empty")
            void returnElementWhenPeeked() {
                assertEquals(anElement, stack.peek());
                assertFalse(stack.isEmpty());
            }
        }
    }
}

6您炉、參數(shù)化測試

參數(shù)化測試是JUnit5很重要的一個(gè)新特性,它使得用不同的參數(shù)多次運(yùn)行測試成為了可能役电,也為我們的單元測試帶來許多便利赚爵。
@ValueSource: 為參數(shù)化測試指定入?yún)碓矗С职舜蠡A(chǔ)類以及String類型,Class類型
@NullSource: 表示為參數(shù)化測試提供一個(gè)null的入?yún)?br> @EnumSource: 表示為參數(shù)化測試提供一個(gè)枚舉入?yún)?br> @CsvFileSource:表示讀取指定CSV文件內(nèi)容作為參數(shù)化測試入?yún)?br> @MethodSource:表示讀取指定方法的返回值作為參數(shù)化測試入?yún)?注意方法返回需要是一個(gè)流)

@ParameterizedTest
@ValueSource(strings = {"one", "two", "three"})
@DisplayName("參數(shù)化測試1")
public void parameterizedTest1(String string) {
    System.out.println(string);
    Assertions.assertTrue(StringUtils.isNotBlank(string));
}


@ParameterizedTest
@MethodSource("method")    //指定方法名
@DisplayName("方法來源參數(shù)")
public void testWithExplicitLocalMethodSource(String name) {
    System.out.println(name);
    Assertions.assertNotNull(name);
}

static Stream<String> method() {
    return Stream.of("apple", "banana");
}

7法瑟、遷移指南

<article id="content" class="article-content" tabindex="0" style="outline-style: none;">

在進(jìn)行遷移的時(shí)候需要注意如下的變化:

  • 注解在 org.junit.jupiter.api 包中冀膝,斷言在 org.junit.jupiter.api.Assertions 類中,前置條件在 org.junit.jupiter.api.Assumptions 類中霎挟。
  • 把@Before 和@After 替換成@BeforeEach 和@AfterEach窝剖。
  • 把@BeforeClass 和@AfterClass 替換成@BeforeAll 和@AfterAll。
  • 把@Ignore 替換成@Disabled酥夭。
  • 把@Category 替換成@Tag枯芬。
  • 把@RunWith、@Rule 和@ClassRule 替換成@ExtendWith采郎。

八千所、指標(biāo)監(jiān)控

8.1、SpringBoot Actuator

1蒜埋、簡介
未來每一個(gè)微服務(wù)在云上部署以后淫痰,我們都需要對其進(jìn)行監(jiān)控、追蹤整份、審計(jì)待错、控制等。SpringBoot就抽取了Actuator場景烈评,使得我們每個(gè)微服務(wù)快速引用即可獲得生產(chǎn)級別的應(yīng)用監(jiān)控火俄、審計(jì)等功能。
2讲冠、如何使用

management:
  endpoints:
    enabled-by-default: true #暴露所有端點(diǎn)信息
    web:
      exposure:
        include: '*'  #以web方式暴露

3瓜客、可視化
https://github.com/codecentric/spring-boot-admin

8.2、Actuator Endpoint

1竿开、最常使用的端點(diǎn)
最常用的Endpoint
● Health:監(jiān)控狀況
● Metrics:運(yùn)行時(shí)指標(biāo)
● Loggers:日志記錄
2谱仪、Health Endpoint
健康檢查端點(diǎn),我們一般用于在云平臺否彩,平臺會定時(shí)的檢查應(yīng)用的健康狀況疯攒,我們就需要Health Endpoint可以為平臺返回當(dāng)前應(yīng)用的一系列組件健康狀況的集合。
重要的幾點(diǎn):
● health endpoint返回的結(jié)果列荔,應(yīng)該是一系列健康檢查后的一個(gè)匯總報(bào)告
● 很多的健康檢查默認(rèn)已經(jīng)自動(dòng)配置好了敬尺,比如:數(shù)據(jù)庫枚尼、redis等
● 可以很容易的添加自定義的健康檢查機(jī)制

3、Metrics Endpoint
供詳細(xì)的砂吞、層級的署恍、空間指標(biāo)信息,這些信息可以被pull(主動(dòng)推送)或者push(被動(dòng)獲任厥妗)方式得到锭汛;
● 通過Metrics對接多種監(jiān)控系統(tǒng)
● 簡化核心Metrics開發(fā)
● 添加自定義Metrics或者擴(kuò)展已有Metrics

4笨奠、管理Endpoints
1袭蝗、開啟與禁用Endpoints
● 默認(rèn)所有的Endpoint除過shutdown都是開啟的。
● 需要開啟或者禁用某個(gè)Endpoint般婆。配置模式為 management.endpoint.<endpointName>.enabled = true
或者禁用所有的Endpoint然后手動(dòng)開啟指定的Endpoint
2到腥、暴露Endpoints
支持的暴露方式
● HTTP:默認(rèn)只暴露health和info Endpoint
● JMX(例如Jconsole):默認(rèn)暴露所有Endpoint
● 除過health和info,剩下的Endpoint都應(yīng)該進(jìn)行保護(hù)訪問蔚袍。如果引入SpringSecurity乡范,則會默認(rèn)配置安全訪問規(guī)則

8.3、定制 Endpoint

1啤咽、定制 Health 信息
2晋辆、定制info信息
3、定制Metrics信息

九宇整、原理解析

9.1瓶佳、Profile功能

1、application-profile功能
● 默認(rèn)配置文件 application.yaml鳞青;任何時(shí)候都會加載
● 指定環(huán)境配置文件 application-{env}.yaml
● 激活指定環(huán)境
○ 配置文件激活
○ 命令行激活:java -jar xxx.jar --spring.profiles.active=prod --person.name=haha
■ 修改配置文件的任意值霸饲,命令行優(yōu)先
● 默認(rèn)配置與環(huán)境配置同時(shí)生效
● 同名配置項(xiàng),profile配置優(yōu)先
2臂拓、@Profile條件裝配功能

@Configuration(proxyBeanMethods = false)
@Profile("production")
public class ProductionConfiguration {

    // ...

}

3厚脉、profile分組

spring.profiles.group.production[0]=proddb
spring.profiles.group.production[1]=prodmq

使用:--spring.profiles.active=production  激活

9.2、外部化配置

1胶惰、外部配置源
常用:Java屬性文件傻工、YAML文件、環(huán)境變量孵滞、命令行參數(shù)精钮;
2、配置文件查找位置
(1) classpath 根路徑
(2) classpath 根路徑下config目錄
(3) jar包當(dāng)前目錄
(4) jar包當(dāng)前目錄的config目錄
(5) /config子目錄的直接子目錄
3剃斧、配置文件加載順序:

  1. 當(dāng)前jar包內(nèi)部的application.properties和application.yml
  2. 當(dāng)前jar包內(nèi)部的application-{profile}.properties 和 application-{profile}.yml
  3. 引用的外部jar包的application.properties和application.yml
  4. 引用的外部jar包的application-{profile}.properties 和 application-{profile}.yml

4轨香、指定環(huán)境優(yōu)先,外部優(yōu)先幼东,后面的可以覆蓋前面的同名配置項(xiàng)

9.3臂容、自定義starter

1科雳、starter啟動(dòng)原理
starter-pom引入 autoconfigurer 包
● autoconfigure包中配置使用 META-INF/spring.factories 中 EnableAutoConfiguration 的值,使得項(xiàng)目啟動(dòng)加載指定的自動(dòng)配置類
● 編寫自動(dòng)配置類 xxxAutoConfiguration -> xxxxProperties
○ @Configuration
○ @Conditional
○ @EnableConfigurationProperties
○ @Bean
○ ......
引入starter --- xxxAutoConfiguration --- 容器中放入組件 ---- 綁定xxxProperties ---- 配置項(xiàng)

2脓杉、自定義starter
atguigu-hello-spring-boot-starter(啟動(dòng)器)
atguigu-hello-spring-boot-starter-autoconfigure(自動(dòng)配置包)

9.4糟秘、SpringBoot原理

SpringBoot啟動(dòng)過程:
● 創(chuàng)建 SpringApplication
○ 保存一些信息。
○ 判定當(dāng)前應(yīng)用的類型球散。ClassUtils尿赚。Servlet
○ bootstrappers:初始啟動(dòng)引導(dǎo)器(List<Bootstrapper>):去spring.factories文件中找 org.springframework.boot.Bootstrapper
○ 找 ApplicationContextInitializer;去spring.factories找 ApplicationContextInitializer
■ List<ApplicationContextInitializer<?>> initializers
○ 找 ApplicationListener 蕉堰;應(yīng)用監(jiān)聽器凌净。去spring.factories找 ApplicationListener
■ List<ApplicationListener<?>> listeners
● 運(yùn)行 SpringApplication
○ StopWatch
○ 記錄應(yīng)用的啟動(dòng)時(shí)間
○ 創(chuàng)建引導(dǎo)上下文(Context環(huán)境)createBootstrapContext()
■ 獲取到所有之前的 bootstrappers 挨個(gè)執(zhí)行 intitialize() 來完成對引導(dǎo)啟動(dòng)器上下文環(huán)境設(shè)置
○ 讓當(dāng)前應(yīng)用進(jìn)入headless模式。java.awt.headless
○ 獲取所有 RunListener(運(yùn)行監(jiān)聽器)【為了方便所有Listener進(jìn)行事件感知】
■ getSpringFactoriesInstances 去spring.factories找 SpringApplicationRunListener.
○ 遍歷 SpringApplicationRunListener 調(diào)用 starting 方法屋讶;
■ 相當(dāng)于通知所有感興趣系統(tǒng)正在啟動(dòng)過程的人冰寻,項(xiàng)目正在 starting。
○ 保存命令行參數(shù)皿渗;ApplicationArguments
○ 準(zhǔn)備環(huán)境 prepareEnvironment();
■ 返回或者創(chuàng)建基礎(chǔ)環(huán)境信息對象斩芭。StandardServletEnvironment
■ 配置環(huán)境信息對象。
● 讀取所有的配置源的配置屬性值乐疆。
■ 綁定環(huán)境信息
■ 監(jiān)聽器調(diào)用 listener.environmentPrepared()划乖;通知所有的監(jiān)聽器當(dāng)前環(huán)境準(zhǔn)備完成
○ 創(chuàng)建IOC容器(createApplicationContext())
■ 根據(jù)項(xiàng)目類型(Servlet)創(chuàng)建容器,
■ 當(dāng)前會創(chuàng)建 AnnotationConfigServletWebServerApplicationContext
○ 準(zhǔn)備ApplicationContext IOC容器的基本信息 prepareContext()
■ 保存環(huán)境信息
■ IOC容器的后置處理流程挤土。
■ 應(yīng)用初始化器琴庵;applyInitializers;
● 遍歷所有的 ApplicationContextInitializer 耕挨。調(diào)用 initialize.细卧。來對ioc容器進(jìn)行初始化擴(kuò)展功能
● 遍歷所有的 listener 調(diào)用 contextPrepared。EventPublishRunListenr筒占;通知所有的監(jiān)聽器contextPrepared
■ 所有的監(jiān)聽器 調(diào)用 contextLoaded贪庙。通知所有的監(jiān)聽器 contextLoaded;
○ 刷新IOC容器翰苫。refreshContext
■ 創(chuàng)建容器中的所有組件(Spring注解)
○ 容器刷新完成后工作止邮?afterRefresh
○ 所有監(jiān)聽 器 調(diào)用 listeners.started(context); 通知所有的監(jiān)聽器 started
○ 調(diào)用所有runners;callRunners()
■ 獲取容器中的 ApplicationRunner
■ 獲取容器中的 CommandLineRunner
■ 合并所有runner并且按照@Order進(jìn)行排序
■ 遍歷所有的runner奏窑。調(diào)用 run 方法
○ 如果以上有異常导披,
■ 調(diào)用Listener 的 failed
○ 調(diào)用所有監(jiān)聽器的 running 方法 listeners.running(context); 通知所有的監(jiān)聽器 running
○ running如果有問題。繼續(xù)通知 failed 埃唯。調(diào)用所有 Listener 的 failed撩匕;通知所有的監(jiān)聽器 failed

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市墨叛,隨后出現(xiàn)的幾起案子止毕,更是在濱河造成了極大的恐慌模蜡,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扁凛,死亡現(xiàn)場離奇詭異忍疾,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)谨朝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門卤妒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人字币,你說我怎么就攤上這事则披。” “怎么了纬朝?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵收叶,是天一觀的道長骄呼。 經(jīng)常有香客問我共苛,道長,這世上最難降的妖魔是什么蜓萄? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任隅茎,我火速辦了婚禮,結(jié)果婚禮上嫉沽,老公的妹妹穿的比我還像新娘辟犀。我一直安慰自己,他們只是感情好绸硕,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布堂竟。 她就那樣靜靜地躺著,像睡著了一般玻佩。 火紅的嫁衣襯著肌膚如雪出嘹。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天咬崔,我揣著相機(jī)與錄音税稼,去河邊找鬼。 笑死垮斯,一個(gè)胖子當(dāng)著我的面吹牛郎仆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播兜蠕,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼扰肌,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了熊杨?” 一聲冷哼從身側(cè)響起曙旭,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤墩剖,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后夷狰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體岭皂,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年沼头,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了爷绘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,090評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡进倍,死狀恐怖土至,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情猾昆,我是刑警寧澤陶因,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站垂蜗,受9級特大地震影響楷扬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜贴见,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一烘苹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧片部,春花似錦镣衡、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至辖所,卻和暖如春惰说,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背奴烙。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工助被, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人切诀。 一個(gè)月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓揩环,卻偏偏與公主長得像,于是被迫代替她去往敵國和親幅虑。 傳聞我的和親對象是個(gè)殘疾皇子丰滑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評論 2 355

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