• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

全网最细的SpringBoot3系列教程

武飞扬头像
十年架构路
帮助1

1、开发第⼀个Spring Boot应用

创建POM

因为是3.0.0-M1版本,是⾥程碑版本,不是正式发布版,需要从Spring提⾼的Maven仓库中才能下载到3.0.0-M1版本的依赖包,需要在pom.xml⽂件中单独指定仓库地址。

如果使⽤的是正式版,是不需要加下⾯这段配置的

  1.  
    <!-- jar包的仓库地址-->
  2.  
    <repositories>
  3.  
           <repository>
  4.  
               <id>spring-snapshots</id>
  5.  
               <url>https://repo.spring.io/snapshot</url>
  6.  
               <snapshots><enabled>true</enabled></snapshots>
  7.  
           </repository>
  8.  
           <repository>
  9.  
               <id>spring-milestones</id>
  10.  
               <url>https://repo.spring.io/milestone</url>
  11.  
           </repository>
  12.  
       </repositories>
  13.  
    <!-- maven插件的仓库地址 -->
  14.  
       <pluginRepositories>
  15.  
           <pluginRepository>
  16.  
               <id>spring-snapshots</id>
  17.  
               <url>https://repo.spring.io/snapshot</url>
  18.  
           </pluginRepository>
  19.  
           <pluginRepository>
  20.  
               <id>spring-milestones</id>
  21.  
               <url>https://repo.spring.io/milestone</url>
  22.  
           </pluginRepository>
  23.  
       </pluginRepositories>
学新通

另外还要在pom.xml⽂件中,添加Spring Boot所提供的⽗pom.xml。

  1.  
    <parent>
  2.  
           <groupId>org.springframework.boot</groupId>
  3.  
           <artifactId>spring-boot-starter-parent</artifactId>
  4.  
           <version>3.0.0-M1</version>
  5.  
       </parent>

关于这个parent的作⽤,后续课程中会介绍。

添加依赖

最后添加⼀个开发web应⽤的starter依赖

  1.  
    <dependencies>
  2.  
       <dependency>
  3.  
           <groupId>org.springframework.boot</groupId>
  4.  
           <artifactId>spring-boot-starter-web</artifactId>
  5.  
       </dependency>
  6.  
    </dependencies>

写代码

在写代码之前,请注意,我现在⽤的JDK8,也许你也是。
创建⼀个类,类名随便取,我的叫MyApplication,包路径为com.zhouyu

  1.  
    package com.zhouyu;
  2.  
    import org.springframework.boot.SpringApplication;
  3.  
    import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
  4.  
    import org.springframework.web.bind.annotation.RequestMapping;
  5.  
    import org.springframework.web.bind.annotation.RestController;
  6.  
    @RestController
  7.  
    @EnableAutoConfiguration
  8.  
    public class MyApplication {
  9.  
       @RequestMapping("/")
  10.  
       String home() {
  11.  
           return "Hello World!";
  12.  
      }
  13.  
       public static void main(String[] args) {
  14.  
           SpringApplication.run(MyApplication.class, args);
  15.  
      }
  16.  
    }
学新通

然后,运⾏main⽅法。
我这报错了:

学新通

因为Spring Boot 3⽤的是Spring 6,⽽Spring 6需要⽤JDK17。


所以,我们要在IDEA中配上JDK17,JDk17的安装没什么特殊的,和JDK8的安装⼀样,就不在笔记⾥浪费篇幅了。


⽤上JDK17之后,再次运⾏main⽅法,就能正常启动我们的第⼀个Spring Boot应⽤程序了,并且会看到如下⽇志: 

  1.  
    "C:\Program Files\Java\jdk-17.0.1\bin\java.exe" "-javaagent:C:\Program
  2.  
    Files\JetBrains\IntelliJ IDEA 2021.1.3\lib\idea_rt.jar=64855:C:\Program
  3.  
    Files\JetBrains\IntelliJ IDEA 2021.1.3\bin" -Dfile.encoding=UTF-8 -
  4.  
    classpath D:\IdeaProjects\ZhouyuSpringBootTeacher\target\classes;C:\Users\zhouyu\.m2\repository\org\springframework
  5.  
    \boot\spring-boot-starter-web\3.0.0-M1\spring-boot-starter-web-3.0.0-
  6.  
    M1.jar;C:\Users\zhouyu\.m2\repository\org\springframework\boot\springboot-starter\3.0.0-M1\spring-boot-starter-3.0.0-
  7.  
    M1.jar;C:\Users\zhouyu\.m2\repository\org\springframework\boot\springboot\3.0.0-M1\spring-boot-3.0.0-
  8.  
    M1.jar;C:\Users\zhouyu\.m2\repository\org\springframework\boot\springboot-autoconfigure\3.0.0-M1\spring-boot-autoconfigure-3.0.0-
  9.  
    M1.jar;C:\Users\zhouyu\.m2\repository\org\springframework\boot\springboot-starter-logging\3.0.0-M1\spring-boot-starter-logging-3.0.0-
  10.  
    M1.jar;C:\Users\zhouyu\.m2\repository\ch\qos\logback\logbackclassic\1.2.10\logback-classic1.2.10.jar;C:\Users\zhouyu\.m2\repository\ch\qos\logback\logbackcore\1.2.10\logback-core1.2.10.jar;C:\Users\zhouyu\.m2\repository\org\slf4j\slf4japi\1.7.33\slf4j-api1.7.33.jar;C:\Users\zhouyu\.m2\repository\org\apache\logging\log4j\log4jto-slf4j\2.17.1\log4j-to-slf4j2.17.1.jar;C:\Users\zhouyu\.m2\repository\org\apache\logging\log4j\log4japi\2.17.1\log4j-api2.17.1.jar;C:\Users\zhouyu\.m2\repository\org\slf4j\jul-toslf4j\1.7.33\jul-to-slf4j1.7.33.jar;C:\Users\zhouyu\.m2\repository\jakarta\annotation\jakarta.anno
  11.  
    tation-api\2.0.0\jakarta.annotation-api2.0.0.jar;C:\Users\zhouyu\.m2\repository\org\springframework\springcore\6.0.0-M2\spring-core-6.0.0-
  12.  
    M2.jar;C:\Users\zhouyu\.m2\repository\org\springframework\springjcl\6.0.0-M2\spring-jcl-6.0.0-
  13.  
    M2.jar;C:\Users\zhouyu\.m2\repository\org\yaml\snakeyaml\1.30\snakeyaml1.30.jar;C:\Users\zhouyu\.m2\repository\org\springframework\boot\springboot-starter-json\3.0.0-M1\spring-boot-starter-json-3.0.0-
  14.  
    M1.jar;C:\Users\zhouyu\.m2\repository\com\fasterxml\jackson\core\jacksondatabind\2.13.1\jackson-databind2.13.1.jar;C:\Users\zhouyu\.m2\repository\com\fasterxml\jackson\core\jack
  15.  
    son-annotations\2.13.1\jackson-annotations2.13.1.jar;C:\Users\zhouyu\.m2\repository\com\fasterxml\jackson\core\jack
  16.  
    son-core\2.13.1\jackson-core2.13.1.jar;C:\Users\zhouyu\.m2\repository\com\fasterxml\jackson\datatype\
  17.  
    jackson-datatype-jdk8\2.13.1\jackson-datatype-jdk8-
  18.  
    2.13.1.jar;C:\Users\zhouyu\.m2\repository\com\fasterxml\jackson\datatype\
  19.  
    jackson-datatype-jsr310\2.13.1\jackson-datatype-jsr310-
  20.  
    16
  21.  
    2.13.1.jar;C:\Users\zhouyu\.m2\repository\com\fasterxml\jackson\module\ja
  22.  
    ckson-module-parameter-names\2.13.1\jackson-module-parameter-names2.13.1.jar;C:\Users\zhouyu\.m2\repository\org\springframework\boot\spring
  23.  
    -boot-starter-tomcat\3.0.0-M1\spring-boot-starter-tomcat-3.0.0-
  24.  
    M1.jar;C:\Users\zhouyu\.m2\repository\org\apache\tomcat\embed\tomcatembed-core\10.0.16\tomcat-embed-core10.0.16.jar;C:\Users\zhouyu\.m2\repository\org\apache\tomcat\embed\tomcat
  25.  
    -embed-el\10.0.16\tomcat-embed-el10.0.16.jar;C:\Users\zhouyu\.m2\repository\org\apache\tomcat\embed\tomcat
  26.  
    -embed-websocket\10.0.16\tomcat-embed-websocket10.0.16.jar;C:\Users\zhouyu\.m2\repository\org\springframework\springweb\6.0.0-M2\spring-web-6.0.0-
  27.  
    M2.jar;C:\Users\zhouyu\.m2\repository\org\springframework\springbeans\6.0.0-M2\spring-beans-6.0.0-
  28.  
    M2.jar;C:\Users\zhouyu\.m2\repository\org\springframework\springwebmvc\6.0.0-M2\spring-webmvc-6.0.0-
  29.  
    M2.jar;C:\Users\zhouyu\.m2\repository\org\springframework\springaop\6.0.0-M2\spring-aop-6.0.0-
  30.  
    M2.jar;C:\Users\zhouyu\.m2\repository\org\springframework\springcontext\6.0.0-M2\spring-context-6.0.0-
  31.  
    M2.jar;C:\Users\zhouyu\.m2\repository\org\springframework\springexpression\6.0.0-M2\spring-expression-6.0.0-M2.jar
  32.  
    com.zhouyu.MyApplication
  33.  
    .   ____          _            __ _ _
  34.  
    /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
  35.  
    ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
  36.  
    \\/  ___)| |_)| | | | | || (_| | ) ) ) )
  37.  
     ' |____| .__|_| |_|_| |_\__, | / / / /
  38.  
    =========|_|==============|___/=/_/_/_/
  39.  
    :: Spring Boot ::             (v3.0.0-M1)
  40.  
    2022-03-16 20:43:21.849  INFO 17520 --- [           main]
  41.  
    com.zhouyu.MyApplication                 : Starting MyApplication using
  42.  
    Java 17.0.1 on DESKTOP-P9KHN97 with PID 17520
  43.  
    (D:\IdeaProjects\ZhouyuSpringBoot-Teacher\target\classes started by
  44.  
    zhouyu in D:\IdeaProjects\ZhouyuSpringBoot-Teacher)
  45.  
    2022-03-16 20:43:21.852  INFO 17520 --- [           main]
  46.  
    com.zhouyu.MyApplication                 : No active profile set, falling
  47.  
    back to default profiles: default
  48.  
    2022-03-16 20:43:23.257  INFO 17520 --- [           main]
  49.  
    o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with
  50.  
    port(s): 8080 (http)
  51.  
    2022-03-16 20:43:23.271  INFO 17520 --- [           main]
  52.  
    o.apache.catalina.core.StandardService   : Starting service [Tomcat]
  53.  
    2022-03-16 20:43:23.272  INFO 17520 --- [           main]
  54.  
    org.apache.catalina.core.StandardEngine : Starting Servlet engine:
  55.  
    [Apache Tomcat/10.0.16]
  56.  
    2022-03-16 20:43:23.406  INFO 17520 --- [           main] o.a.c.c.C.
  57.  
    [Tomcat].[localhost].[/]       : Initializing Spring embedded
  58.  
    WebApplicationContext
  59.  
    2022-03-16 20:43:23.409  INFO 17520 --- [           main]
  60.  
    w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext:
  61.  
    initialization completed in 1472 ms
  62.  
    2022-03-16 20:43:24.147  INFO 17520 --- [           main]
  63.  
    o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s):
  64.  
    8080 (http) with context path ''
  65.  
    2022-03-16 20:43:24.164  INFO 17520 --- [           main]
  66.  
    com.zhouyu.MyApplication                 : Started MyApplication in 2.934
  67.  
    seconds (JVM running for 3.532)
  68.  
     
学新通

因为我们创建的是⼀个Web⼯程,从⽇志中,我们可以发现Spring Boot默认使⽤了Tomcat,并绑定了8080端⼝。


此时打开浏览器访问localhost:8080,就可以访问到我们在上⾯所定义的controller,注意配置的路径为“/”,所以直接通过localhost:8080就可以访问home⽅法。

  1.  
    @RequestMapping("/")
  2.  
       String home() {
  3.  
           return "Hello World!";
  4.  
      }

学新通

创建可执⾏Jar包 

在实际⽣产环境中,我们是需要把应⽤程序⼯程打成⼀个Jar去运⾏的,Spring Boot也给我们提供了⼀个创建可执⾏Jar包的插件,只需要在pom.xml加上以下配置即可

  1.  
    <build>
  2.  
       <plugins>
  3.  
           <plugin>
  4.  
               <groupId>org.springframework.boot</groupId>
  5.  
               <artifactId>spring-boot-maven-plugin</artifactId>
  6.  
           </plugin>
  7.  
       </plugins>
  8.  
    </build>

然后再执⾏maven的package命令就可以打包了,不过⼀般建议先执⾏clean命令进⾏清空,再执⾏其他命令。


执⾏完package命令后,就会在本项⽬⼯程中的target⽂件夹出现所打出来的Jar包。


此时就可以运⾏java命令来执⾏这个Jar包

学新通

 执⾏的效果就相当于运⾏main⽅法。

2、Spring Boot中的Starters

Spring Boot中的starter是Spring Boot的神器之⼀,Spring Boot提⾼了很多的starter,⽽每个starter其实就是⼀个pom.xml⽂件。


⽐如在我们项⽬的pom.xml⽂件中,我们依赖了

  1.  
    <dependency>
  2.  
     <groupId>org.springframework.boot</groupId>
  3.  
     <artifactId>spring-boot-starter-web</artifactId>
  4.  
    </dependency>

这段相当于我们的⼯程依赖了spring-boot-starter-web,但是我们并没有指定具体的version,那到底依赖的是哪个版本的spring-boot-starter-web呢?


这就是由<parent>控制的。

  1.  
    <parent>
  2.  
    <groupId>org.springframework.boot</groupId>
  3.  
    <artifactId>spring-boot-starter-parent</artifactId>
  4.  
    <version>3.0.0-M1</version>
  5.  
    </parent>

这段代码是在给我们⾃⼰的⼯程指定了⼀个⽗⼯程,那这个⽗⼯程在哪呢?

Maven会先从本地仓库根据groupId和artifactId看是否有匹配的Jar包,如果没有就会进⾏下载,⽐如在我电脑的.m2中就已经有了

学新通

这个⽂件夹就是我们项⽬⼯程的⽗⼯程,我们的项⽬可以直接⽤⽗⼯程中所提供的。
那⽗⼯程中有什么东⻄呢?

学新通 

其实就是⼀个pom⽂件。
所以,在我们的项⽬中,只要加上 

  1.  
    <parent>
  2.  
    <groupId>org.springframework.boot</groupId>
  3.  
    <artifactId>spring-boot-starter-parent</artifactId>
  4.  
    <version>3.0.0-M1</version>
  5.  
    </parent>

这段代码,就相当于引⼊了spring-boot-starter-parent-3.0.0-M1.pom这个pom⽂件。
在spring-boot-starter-parent-3.0.0-M1.pom中有很对内容,其中最重要的是:

  1.  
    <parent>
  2.  
       <groupId>org.springframework.boot</groupId>
  3.  
       <artifactId>spring-boot-dependencies</artifactId>
  4.  
       <version>3.0.0-M1</version>
  5.  
     </parent>
  6.  
     <artifactId>spring-boot-starter-parent</artifactId>
  7.  
     <packaging>pom</packaging>
  8.  
     <name>spring-boot-starter-parent</name>
  9.  
     <description>Parent pom providing dependency and plugin management for
  10.  
    applications built with Maven</description>
  11.  
     <properties>
  12.  
       <java.version>17</java.version>
  13.  
       <resource.delimiter>@</resource.delimiter>
  14.  
       <maven.compiler.source>${java.version}</maven.compiler.source>
  15.  
       <maven.compiler.target>${java.version}</maven.compiler.target>
  16.  
       <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  17.  
       <project.reporting.outputEncoding>UTF8</project.reporting.outputEncoding>
  18.  
     </properties>
学新通

⼜指定了⼀个⽗⼯程,以及⼀些properties,⽐如java.version为17也就是,我们⾃⼰项⽬的⽗⼯程为spring-boot-starter-parent-3.0.0-M1,⽽它的⽗⼯程为springboot-dependencies

同样,我们可以在Maven仓库中找到spring-boot-dependencies

学新通

 进⽽看看spring-boot-dependencies⼜是什么?

学新通

 其实也就是⼀个pom⽂件,但是这个⽂件的内容就⾮常⾮常关键,因为它管理了Spring Boot默认⽀持的所有依赖以及对应的版本。

就是因为了有了这个pom⽂件,在我们⾃⼰项⽬的pom⽂件中,如果想要⽤某个依赖,只要这个依赖在spring-boot-dependencies-3.0.0-M1.pom中提供了,那你就可以不写版本,就相当于⽤的SpringBoot给你提供的版本。


回到我们的项⽬,所以我们可以不写version。


那spring-boot-starter-web这个starter⾥⾯⼜有什么呢?


⾸先我们知道,这个依赖表示,我们是⼀个Spring Boot的web⼯程,正常来说,搭建⼀个web⼯程,是要依赖很多东⻄的,⽐如tomcat,spring-web,spring-webmvc等等依赖,⽽这就是spring-bootstarter-web的作⽤,spring-boot-starter-web的作⽤就是帮我们提前把我们要开发⼀个web应⽤的依赖都写好了,我们只要依赖spring-boot-starter-web,就相当于了依赖其他很多相关的依赖。
Spring Boot真的很贴⼼。

除开web场景,还有很多其他场景也是类似的,所以Spring Boot默认提供了很多starter,具体可以看官⽹的统计:https://docs.spring.io/spring-boot/docs/3.0.0-
M1/reference/html/using.html#using.build-systems.starters

值得注意的是,Spring Boot官⽅虽然提供了很多starter,但是有时可能仍然需要第三⽅来来⾃⼰实现⼀个starter并提供出来,对于这种情况,Spring Boot是有规范的,Spring Boot官⽅默认提供的starter命名格式为 spring-boot-starter-* ,第三⽅⾃⼰实现的starter的命名格式为 *-spring-boot
-starter 。

3、Spring Boot中的配置类

在Spring中,我们可以使⽤XML的⽅式来对Spring进⾏配置,也可以通过Java Config(也就是类 注解)的⽅式进⾏配置,在Spring Boot中也是⼀样的。


我们可以通过@ImportResource注解来导⼊⼀个XML⽂件作为Spring的配置⽂件

  1.  
    @EnableAutoConfiguration
  2.  
    @ImportResource("spring.xml")
  3.  
    public class MyApplication {
  4.  
       public static void main(String[] args) {
  5.  
           SpringApplication.run(MyApplication.class, args);
  6.  
      }
  7.  
    }
  1.  
    @RestController
  2.  
    public class UserController {
  3.  
       @Autowired
  4.  
       private UserService userService;
  5.  
       @RequestMapping("/")
  6.  
       String home() {
  7.  
           return userService.test();
  8.  
      }
  9.  
    }
  1.  
    public class UserService {
  2.  
       public String test() {
  3.  
           return "hello world, nice";
  4.  
      }
  5.  
    }
  1.  
    <?xml version="1.0" encoding="UTF-8"?>
  2.  
    <beans xmlns="http://www.springframework.org/schema/beans"
  3.  
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4.  
      xsi:schemaLocation="http://www.springframework.org/schema/beans
  5.  
      https://www.springframework.org/schema/beans/spring-beans.xsd">
  6.  
    <bean id="userService" class="com.zhouyu.service.UserService" />
  7.  
    <bean id="userController" class="com.zhouyu.controller.UserController"
  8.  
    />
  9.  
    </beans>

我们也可以通过@Import @Configuration @Bean来进⾏等价替换掉XML的形式

  1.  
    @Configuration
  2.  
    public class AppConfig {
  3.  
       @Bean
  4.  
       public UserService userService(){
  5.  
           return new UserService();
  6.  
      }
  7.  
       @Bean
  8.  
       public UserController userController(){
  9.  
           return new UserController();
  10.  
      }
  11.  
    }
  1.  
    @Import(AppConfig.class)
  2.  
    @EnableAutoConfiguration
  3.  
    public class MyApplication {
  4.  
       public static void main(String[] args) {
  5.  
           SpringApplication.run(MyApplication.class, args);
  6.  
      }
  7.  
    }

当然,我们可以直接让MyApplication成为⼀个配置类,这样就不⽤额外添加⼀个AppConfig类了。

  1.  
    @EnableAutoConfiguration
  2.  
    @Configuration
  3.  
    public class MyApplication {
  4.  
       @Bean
  5.  
       public UserService userService(){
  6.  
           return new UserService();
  7.  
      }
  8.  
       @Bean
  9.  
       public UserController userController(){
  10.  
           return new UserController();
  11.  
      }
  12.  
       public static void main(String[] args) {
  13.  
           SpringApplication.run(MyApplication.class, args);
  14.  
      }
  15.  
    }
学新通

配置类的作⽤除开可以通过@Bean来定义Bean之外,也可以配置扫描路径,⽐如:

  1.  
    @EnableAutoConfiguration
  2.  
    @Configuration
  3.  
    @ComponentScan("com.zhouyu")
  4.  
    public class MyApplication {
  5.  
       public static void main(String[] args) {
  6.  
           SpringApplication.run(MyApplication.class, args);
  7.  
      }
  8.  
    }
  9.  
    1

这样就不需要⽤@Bean了,但是得在类上加上@Component注解来定义Bean


扫描过程中,除开可以扫描到@Component、@Service、@Controller、@RestController等注解之外,也能扫描到@Configuration


也就是我们可以在扫描路径下定义其他的配置类。


另外,由于MyApplication类所在包就是com.zhouyu,所以我们可以直接这么写:

  1.  
    @EnableAutoConfiguration
  2.  
    @Configuration
  3.  
    @ComponentScan
  4.  
    public class MyApplication {
  5.  
       public static void main(String[] args) {
  6.  
           SpringApplication.run(MyApplication.class, args);
  7.  
      }
  8.  
    }

此时扫描路径就是@ComponentScan注解所在的类的包路径。
此时MyApplication就存在三个注解:
@EnableAutoConfiguration
@Configuration
@ComponentScan

在Spring Boot中,提供了⼀个注解来替代这三个注解,这个注解就是@SpringBootApplication
所以代码就可以改成

  1.  
    @SpringBootApplication
  2.  
    public class MyApplication {
  3.  
       public static void main(String[] args) {
  4.  
           SpringApplication.run(MyApplication.class, args);
  5.  
      }
  6.  
    }

所以,这个经典的写法,它表示了什么意思呢
1. 定义了MyApplication类是⼀个配置类
2. 定义了扫描路径,就是MyApplication所在的包路径
3. 加了@EnableAutoConfiguration,那这个注解⼜表示什么意思呢?翻译⼀下就是开启⾃动配置


接下来就来看看什么是⾃动配置

4、Spring Boot中的⾃动配置

Spring Boot⾃动配置会根据项⽬中所添加的依赖进⾏⾃动配置,⽐如我们项⽬中添加了

  1.  
    <dependency>
  2.  
    <groupId>org.springframework.boot</groupId>
  3.  
    <artifactId>spring-boot-starter-web</artifactId>
  4.  
    </dependency>

⽽这个依赖中,间接添加了

  1.  
    <dependency>
  2.  
         <groupId>org.springframework.boot</groupId>
  3.  
         <artifactId>spring-boot-starter</artifactId>
  4.  
         <version>3.0.0-M1</version>
  5.  
         <scope>compile</scope>
  6.  
       </dependency>
  7.  
    <dependency>
  8.  
         <groupId>org.springframework.boot</groupId>
  9.  
         <artifactId>spring-boot-starter-tomcat</artifactId>
  10.  
         <version>3.0.0-M1</version>
  11.  
         <scope>compile</scope>
  12.  
       </dependency>
  13.  
       <dependency>
  14.  
         <groupId>org.springframework</groupId>
  15.  
         <artifactId>spring-web</artifactId>
  16.  
         <version>6.0.0-M2</version>
  17.  
         <scope>compile</scope>
  18.  
       </dependency>
  19.  
       <dependency>
  20.  
         <groupId>org.springframework</groupId>
  21.  
         <artifactId>spring-webmvc</artifactId>
  22.  
         <version>6.0.0-M2</version>
  23.  
         <scope>compile</scope>
  24.  
       </dependency>
学新通

我们知道,我们在搭建Spring MVC⼯程时,除开要假如spring-web,spring-webmvc等依赖包之外,最复杂的就是还要进⾏很多额外的配置


那在Spring Boot中,这些配置在哪呢?


注意,在我们项⽬中引⼊的spring-boot-starter-web中,引⼊了spring-boot-starter,⽽这个⾥⾯⼜
引⼊了spring-boot-autoconfigure

在spring-boot-autoconfigure依赖中存在⼀个⽂件spring.factories,这个⽂件中记录了各种各样的
*****AutoConfiguration类,这些⾃动配置类(其实就是配置类)就是⽤来进⾏⾃动配置的。

那这个spring.factories⽂件中所记录的⾃动配置类,是什么时候⽣效的呢,这就是
@EnableAutoConfiguration注解的作⽤,只有加了这个注解,那这些⾃动配置类才会⽣效,因为
@EnableAutoConfiguration注解会去寻找spring.factories⽂件,并解析内容,所以能解析出来⾃动配置类,并进⼀步对配置类进⾏解析。

⽐如在spring.factories⽂件中存在⼀个DispatcherServletAutoConfiguration,很明显是⽤来对
DispatcherServlet进⾏⾃动配置的,具体的细节,我们暂时就不深⼊了,本节课只需⼤体理解⾃动配置的作⽤。

⾃动配置并不是去帮助我们配置扫描路径之类的,⽽是针对各种各样的场景,Spring Boot已经给我们配置好了本来是我们需要配置的⼀些Bean以及⼀些参数。

5、Spring Boot中的条件注解

只⾃动配置类中,通常能看到很多条件注解(⽐如@ConditionalOnClass、@ConditionalOnBean),这是因为,如果我们要⽤Spring Boot的⾃动配置功能,就会加上@EnableAutoConfiguration注解,从⽽就会将解析spring.factories⽂件中的所有⾃动配置类,但是在⼀个项⽬中并不是所有⾃动配置类都要使⽤到。


⽐如我不需要⽤到MVC,那么WebMvcAutoConfiguration就没什么⽤,⽐如我不⽤Jta,那
JtaAutoConfiguration也就没什么⽤,没⽤的配置类,就不要让Spring去解析它。

条件注解就⽤来实现这种控制的。

⽐如WebMvcAutoConfiguration上就有

  1.  
    @ConditionalOnWebApplication(type = Type.SERVLET)
  2.  
    @ConditionalOnClass({Servlet.class, DispatcherServlet.class,
  3.  
    WebMvcConfigurer.class})
  4.  
    @ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
  • @ConditionalOnWebApplication(type = Type.SERVLET),表示WebMvcAutoConfiguration只有在应⽤类型是SERVLET类型是才⽣效
  • @ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class}),表示只有在项⽬以及依赖中存在这三个类时,WebMvcAutoConfiguration才⽣效
  • @ConditionalOnMissingBean({WebMvcConfigurationSupport.class}),表示只有Spring容器中没有WebMvcConfigurationSupport.class类型的Bean时,WebMvcAutoConfiguration才⽣效

所以总结⼀下,WebMvcAutoConfiguration⽣效的条件是:当前应⽤类型是SERVLET类型的Web项⽬,并且项⽬的classpath中存在Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class三个,并且项⽬的Spring容器中没有WebMvcConfigurationSupport.class类型的Bean。

我们现在并不需要去理解为什么是这么⼀个条件,我们先来看看Spring Boot中到底有多少种这些条件注解,分别表示什么意思。

1. ConditionalOnBean:是否存在某个某类或某个名字的Bean
2. ConditionalOnMissingBean:是否缺失某个某类或某个名字的Bean
3. ConditionalOnSingleCandidate:是否符合指定类型的Bean只有⼀个
4. ConditionalOnClass:是否存在某个类
5. ConditionalOnMissingClass:是否缺失某个类
6. ConditionalOnExpression:指定的表达式返回的是true还是false
7. ConditionalOnJava:判断Java版本
8. ConditionalOnJndi:JNDI指定的资源是否存在
9. ConditionalOnWebApplication:当前应⽤是⼀个Web应⽤
10. ConditionalOnNotWebApplication:当前应⽤不是⼀个Web应⽤
11. ConditionalOnProperty:Environment中是否存在某个属性
12. ConditionalOnResource:指定的资源是否存在
13. ConditionalOnWarDeployment:当前项⽬是不是以War包部署的⽅式运⾏
14. ConditionalOnCloudPlatform:是不是在某个云平台上

6、Spring Boot中的spring.factories

在我们⾃⼰的项⽬中,我们⾃⼰定义的配置类,我们⾃然可以扫描到它,但是如果某个配置类不在我们的扫描范围内,该怎么办呢?

此时就可以利⽤spring.factories机制,可以在spring.factories中指定想添加的配置类,并可以使⽤

  • @AutoConfigureAfter(BppConfig.class):表示本配置类要在BppConfig解析之后才解析
  • @AutoConfigureBefore(BppConfig.class):表示本配置类要在BppConfig解析之前才解析
  • @AutoConfigureOrder(1):直接⽤数字定义顺序
  1.  
    @Configuration
  2.  
    @AutoConfigureOrder(2)
  3.  
    public class AppConfig {
  4.  
       @Bean
  5.  
       public OrderService orderService(){
  6.  
           return new OrderService();
  7.  
      }
  8.  
    }
  1.  
    @Configuration
  2.  
    @AutoConfigureOrder(2)
  3.  
    public class AppConfig {
  4.  
       @Bean
  5.  
       public OrderService orderService(){
  6.  
           return new OrderService();
  7.  
      }
  8.  
    }

META-INF/spring.factories

  1.  
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  2.  
    com.zhouyu.AppConfig,\
  3.  
    com.zhouyu.BppConfig

注意,上⾯的上个能排序的注解,只能针对spring.factories中的配置类进⾏排序,如果配置类是我们⾃⼰扫描出来的,则这三个注解⽆效,⽽且如果是扫描出来的使⽤@Order或者Ordered接⼝来排序也是没有⽤的,因为Spring的扫描中根本就不会进⾏排序。

7、Spring Boot中的属性绑定

我们可以使⽤@Value("${xxx}")的⽅式来获取properties中的属性值。

如果properties⽂件的名字是application.properties,那就不需要⽤@PropertySource注解,如果不
是,就需要@PropertySource("zhouyu.properties")

在Spring Boot提供了⼀种更⽅便的⽅式来获取properties⽂件中的属性值。

⽐如我们⽤@Value,在UserService得写⼀遍所有的@Value,可能在其他Service也得写⼀遍

  1.  
    @Component
  2.  
    public class UserService {
  3.  
       @Value("${username}")
  4.  
       private String username;
  5.  
       @Value("${password}")
  6.  
       private String password;
  7.  
       public String test() {
  8.  
           return username ":" password;
  9.  
      }
  10.  
    }

现在⽤Spring Boot,我们可以

  1.  
    @ConfigurationProperties
  2.  
    @Component
  3.  
    public class MyProperties {
  4.  
       private String username;
  5.  
       private String password;
  6.  
       // setter getter
  7.  
    }

在UserService中,把MyProperties当作⼀个Bean⽤即可

  1.  
    @Component
  2.  
    public class UserService {
  3.  
       @Autowired
  4.  
       private MyProperties myProperties;
  5.  
       public String test() {
  6.  
           return myProperties.getUsername() ":"
  7.  
    myProperties.getPassword();
  8.  
      }
  9.  
    }

在Spring Boot⾃身的源码中,通常不会在MyProperties上加@Component,⽽是在配置类上加上
@EnableConfigurationProperties(MyProperties.class)

  1.  
    @Configuration
  2.  
    @ConditionalOnClass(MyApplication.class)
  3.  
    @EnableConfigurationProperties(MyProperties.class)
  4.  
    public class AppConfig {
  5.  
    }

这样,就可以控制只有在符合指定的条件时,才会使得MyProperties成为Bean
另外还可以使⽤@ConfigurationPropertiesScan来进⾏扫描

  1.  
    @SpringBootApplication
  2.  
    @ConfigurationPropertiesScan("com.zhouyu.service")
  3.  
    public class MyApplication {
  4.  
       public static void main(String[] args) {
  5.  
           SpringApplication.run(MyApplication.class, args);
  6.  
      }
  7.  
    }

这样,照样可以是MyProperties⽣效。
有时,如果我们想利⽤我们⾃⼰的properties来构造第三⽅提供的Bean,那就可以利⽤
@Bean @ConfigurationProperties

8、Spring Boot中的外部配置

Spring Boot虽然会⾃动给我们做⼀些配置,当有些配置肯定是得我们⾃⼰来配的,⽐如数据库的连接地
址,⽤户名,密码等。
我们可以通过Java properties files, YAML files, environment variables, and command-line
arguments来进⾏配置。
application.properties中配置

password=zhouyu123456

UserService类是这么写:

  1.  
    @Component
  2.  
    @ConfigurationProperties
  3.  
    public class UserService {
  4.  
       private String password;
  5.  
       public String test() {
  6.  
           return password;
  7.  
      }
  8.  
       // setter getter
  9.  
    }

最后userName属性就为zhouyu。

删掉application.properties⽂件,新建application.yml

password: zhouyu123456yml

重启项⽬,得到的是zhouyu123456yml


如果同时存在application.properties和application.yml,application.properties的优先级更⾼


假如在JVM环境变量中配置,如果是通过java命令运⾏,就是-Dpassword=zhouyu123456jvm

学新通

最后password取到的值为JVM环境变量中的值
我们在操作系统的环境变量中设置,设置完IDEA重启才能⽣效

学新通

 最后结果仍然是JVM环境变量中的值,那如果把JVM中的环境变量中配置的删除掉,就能获取掉操作系统环境变量中所配置的值了


所以,JVM环境变量优先级 > 操作系统环境变量 > application.properties > application.yml


还可以通过命令⾏参数来设置值

学新通

 相当于:java -jar ZhouyuSpringBoot-Teacher-1.0-SNAPSHOT.jar --password=1111


这种⽅式的优先级⾼于JVM环境变量优先级


所以
命令⾏参数 > VM环境变量优先级 > 操作系统环境变量 > application.properties > application.yml

除了这⼏种配置参数之外,还有很多其他⽅式,优先级从低到⾼为:
1. SpringBoot默认值,通过SpringApplication.setDefaultProperties所设置的
2. 在@Configuration配置类上通过@PropertySource注解引⼊的properties⽂件,注意在Spring容          器刷新之前这种配置是不会⽣效的,所以通过这种⽅式所配置的loggin.*,spring.main.*的配          置项在容器启动过程中是不会⽣效的。
3. Config data
        a. 同⼀个Jar包内的application.properties和YAML
        b. 同⼀个Jar包内的application-{profile}.properties and YAML
        c. jar包外的application.properties and YAML
        d. jar包外的application-{profile}.properties and YAML
4. 操作系统环境变量
5. JVM环境变量

6. ServletContext初始化参数
7. ServletConfig初始化参数
8. 嵌⼊在环境变量中的⾏内JSON,⽐如Dspring.application.json='{"password":"zhouyu123456json"}'

学新通

 9. 命令⾏参数
10. 测试环境
11. Devtools全局设置

对于application.properties and application.yaml⽂件,Spring Boot会(优先级从低到⾼):
1. 先从classpath下找
        a. classpath根⽬录
        b. classpath下的config⽬录
2. 应⽤启动时的当前⽬录
        a. 当前⽬录
        b. 当前⽬录下的/config⼦⽬录
        c. /config⼦⽬录下的⼦⽬录(这个⼦⽬录名字随便叫,只要⾥⾯有application.properties and
        application.yaml⽂件就可以)

默认会找名字叫做application的⽂件,我们可以通过--spring.config.name=myproject换⼀个名字

9、Spring Boot中的Profiles

Spring Boot⽀持某个Bean、某个配置类、某个@ConfigurationProperties在某个特定环境下才⽣效。


⽐如

  1.  
    @SpringBootApplication
  2.  
    public class MyApplication {
  3.  
       @Bean
  4.  
       @Profile("dev")
  5.  
       public UserService userServiceDev(){
  6.  
           return new UserService("zhouyudev");
  7.  
      }
  8.  
       @Bean
  9.  
       @Profile("prod")
  10.  
       public UserService userServiceProd(){
  11.  
           return new UserService("zhouyuprod");
  12.  
      }
  13.  
       public static void main(String[] args) {
  14.  
           SpringApplication.run(MyApplication.class, args);
  15.  
      }
  16.  
    }
学新通
  1.  
    public class UserService {
  2.  
       private String password;
  3.  
       public UserService(String password) {
  4.  
           this.password = password;
  5.  
      }
  6.  
       public String test() {
  7.  
           return password;
  8.  
      }
  9.  
    }

application.properties:

spring.profiles.active=prod

此时userServiceProd⽣效。
我们可以通过其他各种⽅式来配置spring.profiles.active。
我们还可以

  1.  
    @ConfigurationProperties
  2.  
    @Profile("dev")
  3.  
    @Component
  4.  
    public class UserService {
  5.  
       private String password;
  6.  
       public String test() {
  7.  
           return password;
  8.  
      }
  9.  
       public String getPassword() {
  10.  
           return password;
  11.  
      }
  12.  
       public void setPassword(String password) {
  13.  
           this.password = password;
  14.  
      }
  15.  
    }
学新通

application.properties:

password=zhouyu

application-dev.properties:

password=zhouyudev

application-prod.properties:

password=zhouyuprod

通过命令⾏的⽅式指定profile:

学新通

 我们可以在properties⽂件中定义⼀个profile groups

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

这样可以通过--spring.profiles.active=production,来激活proddb和prodmq这两种profile

10、Spring Boot中的⽇志

日志格式

默认的⽇志格式为

  1.  
    2022-03-21 14:09:30.465  INFO 15156 --- [           main]
  2.  
    com.zhouyu.MyApplication                 : No active profile set, falling
  3.  
    back to default profiles: default
  4.  
    2022-03-21 14:09:32.133  INFO 15156 --- [           main]
  5.  
    o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with
  6.  
    port(s): 8080 (http)
  7.  
    2022-03-21 14:09:32.141  INFO 15156 --- [           main]
  8.  
    o.apache.catalina.core.StandardService   : Starting service [Tomcat]
  9.  
    2022-03-21 14:09:32.141  INFO 15156 --- [           main]
  10.  
    org.apache.catalina.core.StandardEngine : Starting Servlet engine:
  11.  
    [Apache Tomcat/10.0.16]
  12.  
    2022-03-21 14:09:32.251  INFO 15156 --- [           main] o.a.c.c.C.
  13.  
    [Tomcat].[localhost].[/]       : Initializing Spring embedded
  14.  
    WebApplicationContext
  15.  
    2022-03-21 14:09:32.251  INFO 15156 --- [           main]
  16.  
    w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext:
  17.  
    initialization completed in 1717 ms
  18.  
    2022-03-21 14:09:32.834  INFO 15156 --- [           main]
  19.  
    o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s):
  20.  
    8080 (http) with context path ''
  21.  
    2022-03-21 14:09:32.834  INFO 15156 --- [           main]
  22.  
    com.zhouyu.MyApplication                 : Started MyApplication in 2.917
  23.  
    seconds (JVM running for 3.373)
学新通
  • ⽇期和时间:精确到毫秒
  • ⽇志级别:ERROR, WARN, INFO, DEBUG, or TRACE
  • 进程ID
  • --- 分隔符
  • 线程名
  • Logger名:通常就是类名(经常是缩写)
  • ⽇志消息

控制台打印

默认⽇志配置在会将消息回显到控制台。默认情况下,会记录 ERROR 级别、WARN 级别和 INFO 级别的消息。可以通过使⽤ --debug 在启动应⽤程序时启⽤调试模式。


也可以在application.properties配置debug=true


调试模式下会打印更多的信息。

文件输出

默认情况下Spring Boot只会把⽇志输出到控制台,不会写⼊到⽂件中。我们可以在
application.properties中配置logging.file.name或logging.file.path指定⽇志⽂件路径,从⽽可以额外
的将⽇志写⼊⽂件中。

logging.file.name logging.file.path  描述
仅在控制台打印
具体的某个⽂件 名称可以是某个具体路径,或者
相对于当前⽬录的某个⽂件
⽆  具体的某个⽬录 写⼊到指定⽬录下的spring.log

⽇志⽂件达到10MB时就会rotate,默认会记录ERROR、WARN、INFO级别的⽇志。

11、Spring Boot中的异步和任务调度

如果我们没有配置⼀个Executor的Bean,Spring Boot会默认给我们配置⼀个具有合理默认值的
ThreadPoolTaskExecutor,⽤来在@EnableAsync时进⾏异步执⾏。


默认线程池会使⽤8个核⼼线程,并可以根据负载增⻓和收缩,可以通过spring.task.execution.*进⾏配置,⽐如:

  1.  
    spring.task.execution.pool.max-size=16
  2.  
    spring.task.execution.pool.queue-capacity=100
  3.  
    spring.task.execution.pool.keep-alive=10s

默认情况下在@EnableScheduling时会配置⼀个ThreadPoolTaskScheduler,这个线程池默认只会有⼀个线程,可以通过spring.task.scheduling*来进⾏配置,⽐如:

spring.task.scheduling.thread-name-prefix=schedulingspring.task.scheduling.pool.size=2

  1.  
    @Component
  2.  
    public class UserService {
  3.  
       private static Log log = LogFactory.getLog(UserService.class);
  4.  
       @Async
  5.  
       public void test() {
  6.  
           log.info("test");
  7.  
      }
  8.  
    }
  1.  
    debug=true
  2.  
    spring.task.execution.threadNamePrefix=zhouyu

可以利⽤TaskExecutorBuilder来快速的⾃定义⼀个线程池,利⽤部分默认值。

  1.  
    @Bean
  2.  
       public ThreadPoolTaskExecutor taskExecutor(TaskExecutorBuilder
  3.  
    builder){
  4.  
           ThreadPoolTaskExecutor taskExecutor = builder.build();
  5.  
           taskExecutor.setThreadNamePrefix("zhouyu123--");
  6.  
           return taskExecutor;
  7.  
      }

12、Spring Boot中的SpringApplicationRunListener

SpringApplicationRunListener

  1.  
    public interface SpringApplicationRunListener {
  2.  
    /**
  3.  
    * 应⽤刚启动时调⽤.
  4.  
    */
  5.  
    default void starting() {
  6.  
    }
  7.  
    /**
  8.  
    * Environment准备好之后调⽤
  9.  
    */
  10.  
    default void environmentPrepared(ConfigurableEnvironment environment)
  11.  
    {
  12.  
    }
  13.  
    /**
  14.  
    * 创建完ApplicationContext对象后调⽤
  15.  
    */
  16.  
    default void contextPrepared(ConfigurableApplicationContext context)
  17.  
    {
  18.  
    }
  19.  
    /**
  20.  
    * 将Main类注册为BeanDefinition之后调⽤
  21.  
    */
  22.  
    default void contextLoaded(ConfigurableApplicationContext context) {
  23.  
    }
  24.  
    /**
  25.  
    * ApplicationContext完成了刷新,应⽤已经启动,但是CommandLineRunner和
  26.  
    ApplicationRunner还没调⽤
  27.  
    */
  28.  
    default void started(ConfigurableApplicationContext context) {
  29.  
    }
  30.  
    /**
  31.  
    * CommandLineRunner和ApplicationRunner调⽤完
  32.  
    */
  33.  
    default void running(ConfigurableApplicationContext context) {
  34.  
    }
  35.  
    /**
  36.  
    * 容器启动出现异常后调⽤
  37.  
    */
  38.  
    default void failed(ConfigurableApplicationContext context, Throwable
  39.  
    exception) {
  40.  
    }
  41.  
    }
学新通

13、ApplicationRunner和CommandLineRunner

这两个接⼝只有定义上有点区别,在功能和执⾏时机上都⼀样。

  1.  
    public interface CommandLineRunner {
  2.  
    void run(String... args) throws Exception;
  3.  
    }
  1.  
    public interface ApplicationRunner {
  2.  
    void run(ApplicationArguments args) throws Exception;
  3.  
    }

可以发现,区别在于run⽅法所接收的⼊参不同,所以这⾥的重点就是ApplicationArguments表示什么。


定义⼀个ZhouyuApplicationRunner:

  1.  
    @Component
  2.  
    public class ZhouyuApplicationRunner implements ApplicationRunner {
  3.  
    @Override
  4.  
    public void run(ApplicationArguments args) throws Exception {
  5.  
    System.out.println("nonOptionArgs: " args.getNonOptionArgs());
  6.  
    System.out.println("optionNames: " args.getOptionNames());
  7.  
    for (String sourceArg : args.getSourceArgs()) {
  8.  
    System.out.println("sourceArg: " sourceArg);
  9.  
    }
  10.  
    }
  11.  
    }

启动Spring Boot时,增加⼀下参数

  1.  
    @SpringBootApplication
  2.  
    public class Main {
  3.  
    public static void main(String[] args) {
  4.  
    SpringApplication.run(Main.class, "--k1=v1", "--k2=v2", "t3",
  5.  
    "t4");
  6.  
    }
  7.  
    }

runner中打印的结果为:

  1.  
    nonOptionArgs: [t3, t4]
  2.  
    optionNames: [k1, k2]
  3.  
    sourceArg: --k1=v1
  4.  
    sourceArg: --k2=v2
  5.  
    sourceArg: t3
  6.  
    sourceArg: t4

可以发现,加了"--"的都是option, 没有加的就是nonOption,sourceArg就是原始的参数。⽽
CommandLineRunner中接收的就是sourceArg

14、Spring Boot中的@WebServlet注解

我们知道,在⼀个Java Web应⽤中,我们可以通过@WebServlet来定义⼀个Servlet,Tomcat会负责扫描项⽬中哪些类上⾯添加了@WebServlet,从⽽把相对应的Servlet对象添加到Tomcat中去。


但是在Spring Boot中,我们除开照样可以通过@WebServlet来定义⼀个Servlet之外,还需要额外在配置类上添加⼀个@ServletComponentScan注解,顾名思义,这个注解是负责去扫描Servlet的。


也就是在⽤Spring Boot时,@WebServlet的扫描⼯作从Tomcat中转移到了Spring Boot。


那底层是如何⼯作的呢?

ServletRegistrationBean

⾸先我们得学习⼀下ServletRegistrationBean,很明显,这个是⼀个Bean,这个Bean可以⽤来注册Servlet。


我们可以通过BeanDefinition来定义⼀个ServletRegistrationBean,并指定我们想要注册的Servlet,⽐如:

  1.  
    BeanDefinitionBuilder builder =
  2.  
    BeanDefinitionBuilder.rootBeanDefinition(ServletRegistrationBean.class);
  3.  
    builder.addPropertyValue("initParameters", servlet的初始化参数);
  4.  
    builder.addPropertyValue("loadOnStartup", loadOnStartup的值);
  5.  
    builder.addPropertyValue("name", servlet的名字);
  6.  
    builder.addPropertyValue("servlet", servlet对象);
  7.  
    builder.addPropertyValue("urlMappings", servlet的urlMappings);
  8.  
    registry.registerBeanDefinition(name, builder.getBeanDefinition()); // 把
  9.  
    BeanDefinition注册到Spring容器中

那ServletRegistrationBean是如何⼯作的呢?

ServletRegistrationBean继承了ServletContextInitializer接⼝,⽽ServletContextInitializer是Spring
Boot所提供的⼀个函数式接⼝。

在Spring Boot启动流程中,会⽣成⼀个TomcatStarter对象,TomcatStarter实现了
ServletContainerInitializer接⼝,注意,这个接⼝是Servlet规范中所定义的接⼝。

  • ServletContextInitializer:这是Spring Boot定义的
  • ServletContainerInitializer:这是Servlet规范定义的

并且Tomcat在启动之前会把TomcatStarter添加到Tomcat中去,从⽽Tomcat在启动过程中,就会调⽤ServletContainerInitializer接⼝的onStartup()⽅法,也就是会执⾏TomcatStarter的onStartup()⽅法。

⽽TomcatStarter的onStartup()⽅法会从Spring容器中找到所有的ServletContextInitializer类型的
Bean,然后循环执⾏ServletContextInitializer的onStartup(ServletContext servletContext)⽅法,从
⽽就会执⾏到某个ServletRegistrationBean的onStartup()。

⽽ServletRegistrationBean的onStartup⽅法就会把⾃⼰所拥有的Servlet对象添加到ServletContext中去,从⽽完成了Servlet的添加的。

@ServletComponentScan

⽽@ServletComponentScan注解的作⽤就是去指定路径下扫描@WebServlet,扫描到了⽣成
ServletRegistrationBean。


⽽核⼼是会使⽤⼀个ClassPathScanningCandidateComponentProvider来进⾏扫描,这个类并不是Spring Boot中的,⽽是Spring Framework中的,Spring Boot只是利⽤它,当然Spring Boot会做⼀些设置,⽐如向ClassPathScanningCandidateComponentProvider中添加IncludeFilter。

⽐如会添加三个IncludeFilter:
1. new AnnotationTypeFilter(WebServlet.class);
2. new AnnotationTypeFilter(WebFilter.class);
3. new AnnotationTypeFilter(WebListener.class);

就是有了这三个Filter,扫描时才能扫描到这三个注解,并⽣成对应的BeanDefinition,⽐如:
1. @WebServlet对应ServletRegistrationBean
2. @WebFilter对应FilterRegistrationBean
3. @WebListener对应ServletListenerRegistrationBean

总结

Spring Boot启动时:
1. 先扫描@WebServlet,⽣成ServletRegistrationBean
2. 向Tomcat中添加TomcatStarter
3. 启动Tomcat时执⾏TomcatStarter的onStartup()
4. 从⽽执⾏ServletRegistrationBean的onStartup(ServletContext servletContext)
5. 从⽽将Servlet对象注册到Tomcat中

Ps:由于篇幅限制,笔记无法全部为大家展示出来,就以截图主要内容的形式让大家参考啦,需要完整版的小伙伴可以点击这里即可获取到文章中的文档,资料

学新通

学新通 

学新通 

学新通 

学新通 

学新通 

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgakehg
系列文章
更多 icon
同类精品
更多 icon
继续加载