Spring Boot 2从入门到入坟 | 基础入门篇:Spring Boot的自动配置特性

Spring Boot特点

在这一篇文章中,我们就来说下Spring Boot给我们提供的第二大优秀特性,即自动配置。

自动配置

通过编写咱们的第一个Spring Boot的Hello World入门小程序,我们深刻体会到了Spring Boot帮我们自动配好了好多东西,例如帮我们自动配好了Tomcat开发场景。

自动配好Tomcat开发场景

不管Spring Boot帮我们自动配好了什么东西,我们都得做这样两步,第一步是如果你想要自动配好Tomcat开发场景,那么你就得必须先引入Tomcat开发场景的依赖了,我们也知道,前面我们在引入Web开发场景时,其实就已经将Tomcat开发场景(即spring-boot-starter-tomcat)引入了,这是上一章中我们讲过的Spring Boot的依赖管理特性。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <version>2.4.5</version>
    <scope>compile</scope>
</dependency>

正因为我们引入了以上Tomcat开发场景,与Tomcat服务器开发相关的那些jar包才能引入进来。

然后,我们所要做的第二步就是配置Tomcat服务器。至于Spring Boot是怎么把Tomcat服务器配置并启动好的,这已经超出了本篇文章要讲述的范围了,因为这是我们后面在自动配置原理里面要研究的东西,我们现在可以大概先体会一下。

自动配好Spring MVC

同样地,Spring Boot也帮我们自动配好了Spring MVC的Web开发模块,这是因为spring-boot-starter-web这个场景里面帮我们引入了Spring MVC的Web开发模块,我们不妨来确认一下,点进去spring-boot-starter-web这个场景里面,如下图所示,可以看到,除了引入Tomcat开发场景之外,还把Spring、Spring MVC的Web开发模块也引入进来了。

在这里插入图片描述

这儿,我们还是要做这样两步,第一步是引入Spring MVC开发的全套组件,第二步是自动配好Spring MVC常用组件,也就是我们通常所说的常用功能。那到底是哪些常用功能呢?大家不妨回顾回顾一下之前是如何来整合Spring和Spring MVC的,它俩整合的时候,你是不是得在Spring配置文件里面写一大堆的配置啊,还记得吗?比如在web.xml文件里面,我们首先需要配置的肯定是Spring MVC的前端控制器,即DispatcherServlet,让它来帮我们拦截所有请求。

<servlet>
    <servlet-name>SpringMVC</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring-mvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    <async-supported>true</async-supported>
</servlet>
<servlet-mapping>
    <servlet-name>SpringMVC</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

也就是说,如果Spring MVC想要工作,那么前端控制器(即DispatcherServlet)是必须要有的。

那我们不妨就看一下在咱们的整个应用里面有没有帮我们配DispatcherServlet。首先,修改主程序类,将其修改成下面这样。

package com.meimeixia.boot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

/**
 * @author liayun
 * @create 2021-04-19 4:02
 */
@SpringBootApplication
public class MainApplication {

    public static void main(String[] args) {
        // 1. 返回IoC容器,IoC容器里面就包含了当前应用的所有组件
        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args); // 这是固定写法哟
        // 2. 我们可以来查看下IoC容器里面所有的组件,只要能查找到某一个组件,就说明这个组件是能工作的,至于怎么工作,这就是我们后来要阐述的原理了
        String[] names = run.getBeanDefinitionNames(); // 获取所有组件定义的名字
        for (String name : names) {
            System.out.println(name);
        }
    }

}

然后,运行主程序类,这时,我们来看一下当前应用启动之后,Spring Boot到底给我们装载了多少组件,如下图所示,发现挺多的,IDEA控制台中每次换行打印出来的都是一个组件的名字。

在这里插入图片描述

那有没有我们想要的DispatcherServlet组件呢?不妨在IDEA控制台中搜索一下,如下图所示,发现确实是有一个组件的名字叫dispatcherServlet,这也就说明了,在当前应用中,Spring Boot确实是给我们装载了我们想要的DispatcherServlet组件。

在这里插入图片描述

自动配好Web常见功能

还记得我们之前在整合Spring和Spring MVC的时候,为了解决字符乱码问题,我们还得在web.xml文件里面配置一个Filter吗?

<!-- 字符编码过滤器 --> 
<filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <async-supported>true</async-supported>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

以上配置的CharacterEncodingFilter能保证我们返回中文的时候不乱码。

package com.meimeixia.boot.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author liayun
 * @create 2021-04-19 4:24
 */
@RestController
public class HelloController {

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

}

现在,我们不妨来看一下咱们当前的应用是不是能解决乱码问题。运行主程序类,然后在浏览器地址栏中重新访问hello请求,如下图所示,发现响应给浏览器的内容是没有任何乱码的,没有乱码的根本原因是Spring Boot帮我们配置好了字符编码过滤器。

在这里插入图片描述

如果你要是不信的话,那么不妨在IDEA控制台中搜索一下,相信你会找到一个名字叫characterEncodingFilter的组件的,如下图所示,这已然说明了字符编码过滤器(即CharacterEncodingFilter)这个组件在IoC容器中也是存在的。

在这里插入图片描述

除此之外,如果我们还想要做文件上传,那么要是搁以前的话,我们还得整合Spring、Spring MVC它俩,而且在Spring MVC配置文件中还得配置视图解析器等一大堆玩意,是不是这样啊?希望你没有忘记。现在,我们不妨在IDEA控制台中搜索一下,看有没有与视图解析器相关的组件,如下图所示,确实是找到了非常多的viewResolver,即与视图解析器相关的组件。

在这里插入图片描述

而且,想要做文件上传,我们之前是不是还得配置文件上传解析器才可以啊!那现在咱们当前的应用中有没有文件上传解析器呢?我们不妨也在IDEA控制台中搜索一下,如下图所示,发现是有文件上传功能相关的解析器的。

在这里插入图片描述

等等等等,过多的例子我在这里就不举了。所以,这说明了一件事情,那就是Spring Boot已经帮我们配置好了所有Web开发的常见场景。因为我们已经看到了组件在IoC容器中都有存在,既然在IoC容器中有,那么它最终就会生效,至于怎么生效,那是我们后来研究的事情了。

自动配置默认的包结构

Spring Boot在自动配置里面,也给我们配置了一些默认的包结构,那什么叫默认的包结构呢?大家回顾回顾一下,以前我们来整合Spring和Spring MVC时,是不是得指定包扫描等等一大堆的规则啊,其中就包括要扫描哪些包下的Controller。但是,你发现没有,现在我们没有配任何的包扫描,只要我们编写完Controller组件,Spring Boot似乎就能发现,原因何在呢?其实,Spring Boot的官方文档已经说的很清楚了,点进Using Spring Boot这一章节中,在Structuring Your Code这一节下就有一个默认的包扫描规则,那么这个默认的包扫描规则说的又是啥呢?我们不妨就以官方文档中的示例来解释一下。

在这里插入图片描述

从上图中可以清楚地看到,我们的主程序类(即Application.java)是位于com.example.myapplication这个包下的,此时,主程序类所在的包及其下面的所有子包里面的组件都会被默认扫描进来,这就是默认的包扫描规则。所以,这也就解释了为什么我们甚至连包扫描都不用配,编写完的Controller就能被扫描进去了。

这里,我不妨把我本人项目的目录层级给大家看一下,如下图所示,可以看到我的主程序类所在的包是com.meimeixia.boot

在这里插入图片描述

如果现在我在该包外面(即在com.meimeixia里面)再新建一个类,例如WorldController,如下图所示,可以明显看到新建的WorldController是在主程序类所在包的外面的,那么此时新建的WorldController还能被扫描进来吗?

在这里插入图片描述

好,现在我们来重新运行主程序类,并来测试一下,在浏览器地址栏中访问w请求,看看有没有给我们响应回World这样的字符串,如下图所示,发现没有,而是给我们响应了一个默认的404错误页面,这就证实了WorldController是没有被扫描进去的。

在这里插入图片描述

那也就是说,只有当我们把WorldController放在主程序类所在包下或者它下面的子包及其各种孙子包里面,WorldController才能被扫描进去。这里,我们不妨将WorldController放在主程序类所在包下,如下图所示。

在这里插入图片描述

然后再来运行主程序类测试一下,依旧在浏览器地址栏中访问w请求,如下图所示,终于发现给我们响应回World这样的字符串了。

在这里插入图片描述

这就是Spring Boot给我们自动配置的默认的包结构,也就是说,我们无需配置以前的包扫描规则了。

当然了,有些同学说我非要把WorldController放在主程序类所在包的外面,即com.meimeixia包中,就不跟主程序类放在一起,而且它还要能被扫描进去,行不行呢?

在这里插入图片描述

其实也是行的,你只要将主程序类上面标注的@SpringBootApplication注解修改成@SpringBootApplication(scanBasePackages = "com.meimeixia")就行,即通过scanBasePackages属性来指定要扫描的基础包是com.meimeixia,也就是将整个包的层级再放大一点。这样,com.meimeixia包下的WorldController就能被扫描进去了。

在这里插入图片描述

现在,我们来重新运行主程序类测试一下,依旧在浏览器地址栏中访问w请求,如下图所示,发现依旧还是可以给我们响应回World这样的字符串。

在这里插入图片描述

所以,你非要改变扫描的包路径也是可以的。如果默认的包结构就类似我上面那样,那么想要改变包扫描路径,就得使用@SpringBootApplication(scanBasePackages="要扫描的包路径")这样的注解来改变了。或者,也可以使用另外一个注解来明确地指定要扫描的包路径,这个注解就是我们后面要说的包扫描注解,即@ComponentScan

大家是不是发现了,我们以前在Spring MVC里面配置的东西,在这都能用,是不是啊?由于@SpringBootApplication注解里面已经写了这个包扫描注解,如下图所示。

在这里插入图片描述

而且该包扫描注解又是一个不能重复的注解,所以我们就没法在主程序类上面再来写该包扫描注解了。如果我们非得使用@ComponentScan注解来指定包扫描路径,那么应该怎么办呢?看到@SpringBootApplication注解的源码没,你是不是发现了它其实就是一个合成注解啊?除了那些元注解信息之外,就剩下面这三个注解了。

在这里插入图片描述

也就是说,@SpringBootApplication注解等同于@SpringBootConfiguration@EnableAutoConfiguration以及@ComponentScan这三个注解。

现在你该知道怎么使用@ComponentScan注解来指定包扫描路径了吧😀,咱们的主程序类是不是就应该修改成下面这样啊?

package com.meimeixia.boot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;

/**
 * @author liayun
 * @create 2021-04-19 4:02
 */
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.meimeixia")
public class MainApplication {

    public static void main(String[] args) {
        // 1. 返回IoC容器,IoC容器里面就包含了当前应用的所有组件
        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args); // 这是固定写法哟
        // 2. 我们可以来查看下IoC容器里面所有的组件,只要能查找到某一个组件,就说明这个组件是能工作的,至于怎么工作,这就是我们后来要阐述的原理了
        String[] names = run.getBeanDefinitionNames(); // 获取所有组件定义的名字
        for (String name : names) {
            System.out.println(name);
        }
    }

}

那除了包扫描注解(即@ComponentScan)之外,其他注解是什么意思呢?这个不急,我们后面再讲。好了,现在我们重新运行主程序类来测试一下,依旧在浏览器地址栏中访问w请求,如下图所示,发现依旧还是可以给我们响应回World这样的字符串。

在这里插入图片描述

至此,总结一下,我们使用@SpringBootApplication注解或者@ComponentScan注解来指定包扫描路径,都是可以的

不知道你发现没有,当我们在主程序类上只标注@SpringBootApplication这一个注解时,其实就相当于包扫描注解(即@ComponentScan)来默认扫描主程序类所在的包,即com.meimeixia.boot。如果默认扫描的是主程序类所在的包,那么WorldController肯定就不能被扫描到了,继而w请求也就没法访问了。

要不我们来确认一下该事实吧!确实啥事实呢?就是确认 @SpringBootApplication注解等同于@SpringBootConfiguration@EnableAutoConfiguration以及@ComponentScan("主程序类所在的包")这三个注解的事实。为了确认该事实,咱们首先得将主程序类修改成下面这样。

package com.meimeixia.boot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;

/**
 * @author liayun
 * @create 2021-04-19 4:02
 */
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.meimeixia.boot")
public class MainApplication {

    public static void main(String[] args) {
        // 1. 返回IoC容器,IoC容器里面就包含了当前应用的所有组件
        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args); // 这是固定写法哟
        // 2. 我们可以来查看下IoC容器里面所有的组件,只要能查找到某一个组件,就说明这个组件是能工作的,至于怎么工作,这就是我们后来要阐述的原理了
        String[] names = run.getBeanDefinitionNames(); // 获取所有组件定义的名字
        for (String name : names) {
            System.out.println(name);
        }
    }

}

然后,再来重新运行主程序类测试一下,依旧在浏览器地址栏中访问w请求,如下图所示,发现给我们响应了一个默认的404错误页面。

在这里插入图片描述

各种配置拥有默认值

Spring Boot在自动配置里面,帮我们配好了很多东西,而且它们还拥有默认值。例如Tomcat服务器拥有其默认的端口号,包括与Spring MVC相关的配置也都有其默认值,就拿跟文件上传有关的配置来说吧,你可能会问,那我怎么知道文件上传要配哪些东西呢?这里,我先提前给大家露个风,因为我们后面还会更加细致地来讲述。如下图所示,在IDEA的提示下,我们找到了文件上传时限定文件大小的配置,即spring.servlet.multipart.max-file-size,该配置会规定我们上传文件时上传的最大文件是多大,从下面可以明显看到,该配置也有其默认值,即1MB。

在这里插入图片描述

当然了,我们也可以更改其默认值,例如可以给改成10MB。

在这里插入图片描述

所以,以后我们想要更改任何配置,直接在application.properties这个文件里面修改就行了。

而且,如果你点进去这些配置里面,那么你就会发现它们其实都是绑定在一个Java类上了,这个都是我们后来要说的事情。我们不妨点进去spring.servlet.multipart.max-file-size配置里面看看,发现它是绑定在了MultipartProperties这个类上。

在这里插入图片描述

所以,可以这样来说,Spring Boot帮我们配置的所有东西都拥有默认配置,而且默认配置最终都会映射到某一个类上,例如MultipartProperties。甚至,我们可以在IoC容器里面找到这个MultipartProperties组件,如下图所示。

在这里插入图片描述

能找到它,说明在IoC容器里面有MultipartProperties类的一个对象,这个对象里面的那些属性的值就绑定在配置文件中。

总结一下,配置文件中的值最终会绑定在某个类上,并且这个类会在IoC容器中创建对象。所以,我们以后要用到这些默认值的时候,直接在Spring Boot的底层拿到某个类的对象并提取出其默认值就行了。至于是怎么绑定上的,这是我们后来要研究的内容了,这里并不会赘述。

按需加载所有自动配置项

Spring Boot帮我们自动配置的所有配置项都是按需进行加载的。举个例子,之前我们在Spring Boot的官方文档中看到了非常多的场景启动器,即starter,难道说这些starter全部都默认启动了吗?不是的,因为现在我们只引入了一个Web开发场景,那么,可想而知,Spring Boot就只会把Web开发场景里面的配置给我们默认开启,我们没有引入数据库开发场景,自然数据库开发场景里面的配置就不会默认开启了。当然了,这个自动配置原理我们在后面也会详细讲解到。

总结一下,我们引入了哪些场景,那么这些场景里面的自动配置才会开启。

其实,Spring Boot对于所有东西的自动配置全部集中于spring-boot-autoconfigure这样一个包(依赖)中。我们不妨点进spring-boot-starter-web依赖里面去看一看,如下图所示,发现它又依赖了spring-boot-starter

在这里插入图片描述

我们不妨再点进spring-boot-starter依赖里面去看一看,如下图所示,发现它又依赖了spring-boot-autoconfigure

在这里插入图片描述

spring-boot-autoconfigure中最后一个单词autoconfigure翻译过来应该就是自动配置吧,所以,你现在该知道这点了吧,就是Spring Boot所有的自动配置功能都在这个场景(依赖,或者也可以说成是包)里面。

好,我们可以来给大家看一下这个包里面到底都有些什么?展开External Libraries目录下的Maven:org.springframework.boot:spring-boot-autoconfigure:2.4.5目录,你会看到spring-boot-autoconfigure-2.4.5.jar这样一个jar包,并且在该jar包下有一个org.springframework.boot.autoconfigure包,再展开该包,你将会看到Spring Boot所有场景的自动配置,如下图所示。

在这里插入图片描述

可以看到,有做amqp的、做aop(即切面)的、做codec的(即编解码)、做缓存的以及做批处理任务的等等等等所有场景的自动配置全部都集中在这了。我们未来会见到非常多的XxxAutoConfiguration,一看到这,你就应该立马知道这是自动配置了,但是自动配置不一定全部都能生效哟😥。为什么我会这么说呢?因为有的时候可能打开一个类,例如BatchAutoConfiguration,你会发现它里面有很多类都是发红的,既然发红了,那就意味着它是不生效的了,至于为什么不生效,这又是我们后来要研究的事情了。不过,我在这儿并没有发现这个现象,可能是因为BatchAutoConfiguration生效了吧!

在这里插入图片描述

但是,有一点我们是能知道的,那就是我们引入什么场景,那么这个场景里面的自动配置才能生效。如果我们想要做批处理了,那么只须把批处理的场景引入进来就哦了。你可能要问了,那我怎么知道批处理场景用的是哪个场景启动器呢?按照官方的一般命名方式,官方命名都是叫spring-boot-starter-*的,然后再用你的大脑好好想一想,你就应该能想到批处理场景用的就该是spring-boot-starter-batch这个场景启动器了,如下所示。

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

如果你要是发现引入的spring-boot-starter-batch场景启动器报红了,那么不妨打开IDEA右侧的Maven视图,然后选中咱们的maven项目,并在Maven视图中点击一下那个刷新小图标,如下图所示。

在这里插入图片描述

这时,你只须稍等一下,因为引入spring-boot-starter-batch场景启动器,肯定是会要下载一些依赖的,这从IDEA底部状态栏中可以看出来。等到所有的依赖都下载完成之后,引入的spring-boot-starter-batch场景启动器就不会再报红了。而且,如果要是之前打开的BatchAutoConfiguration类中有很多类都是发红的,那么现在就不会有这样子的现象了,按照我们最简单的理解,那就是BatchAutoConfiguration类生效了。

这儿是为了向大家演示怎么引入spring-boot-starter-batch场景启动器,才说了这么一大堆,但现在我们压根就用不到它,所以大家不妨将其注释掉即可。

最后,我再来总结一下,只要我们引入了什么场景,那么这个场景里面的自动配置才会开启(生效),这就是我们所说的按需加载所有自动配置项。至于自动配置的原理,我们后续再来进行深入研究。想要懂得自动配置原理,那么接下来要讲的这些底层注解我们都得学会,学会了以后,我们才能懂得自动配置的原理。

相关推荐
©️2020 CSDN 皮肤主题: 猿与汪的秘密 设计师:白松林 返回首页