mirror of
https://github.com/youthlql/JavaYouth.git
synced 2026-03-13 21:33:42 +08:00
1375 lines
46 KiB
Markdown
1375 lines
46 KiB
Markdown
|
||
|
||
# 第12章-SpringBoot源码-自动配置原理和内嵌Tomcat启动原理
|
||
|
||
## 嵌入式Tomcat与Spring整合
|
||
|
||
### 测试类
|
||
|
||
#### 测试项目目录
|
||
|
||
```java
|
||
springboot-first
|
||
├── common_usetree.txt
|
||
├── pom.xml
|
||
├── springboot-first.iml
|
||
├── src/
|
||
| ├── main/
|
||
| | ├── java/
|
||
| | | └── cn/
|
||
| | | └── imlql/
|
||
| | | └── boot/
|
||
| | | ├── config/
|
||
| | | | ├── SpringConfig.java
|
||
| | | | └── SpringMVCConfig.java
|
||
| | | ├── controller/
|
||
| | | | └── HelloController.java
|
||
| | | ├── Main.java
|
||
| | | ├── QuickAppStarter.java
|
||
| | └── resources/
|
||
| └── test/
|
||
| └── java/
|
||
└── work/
|
||
└── Tomcat/
|
||
└── localhost/
|
||
└── boot/
|
||
```
|
||
|
||
#### pom.xml
|
||
|
||
```xml
|
||
<?xml version="1.0" encoding="UTF-8"?>
|
||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||
<modelVersion>4.0.0</modelVersion>
|
||
|
||
<groupId>org.example</groupId>
|
||
<artifactId>springboot-first</artifactId>
|
||
<version>1.0-SNAPSHOT</version>
|
||
|
||
<properties>
|
||
<maven.compiler.source>8</maven.compiler.source>
|
||
<maven.compiler.target>8</maven.compiler.target>
|
||
</properties>
|
||
|
||
<dependencies>
|
||
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
|
||
<dependency>
|
||
<groupId>org.springframework</groupId>
|
||
<artifactId>spring-webmvc</artifactId>
|
||
<version>5.3.5</version>
|
||
</dependency>
|
||
|
||
<!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-core -->
|
||
<dependency>
|
||
<groupId>org.apache.tomcat.embed</groupId>
|
||
<artifactId>tomcat-embed-core</artifactId>
|
||
<version>8.5.64</version>
|
||
</dependency>
|
||
|
||
<!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-jasper -->
|
||
<dependency>
|
||
<groupId>org.apache.tomcat.embed</groupId>
|
||
<artifactId>tomcat-embed-jasper</artifactId>
|
||
<version>8.5.64</version>
|
||
</dependency>
|
||
|
||
|
||
</dependencies>
|
||
|
||
</project>
|
||
```
|
||
|
||
#### Main
|
||
|
||
```java
|
||
package cn.imlql.boot;
|
||
|
||
import org.apache.catalina.Context;
|
||
import org.apache.catalina.LifecycleException;
|
||
import org.apache.catalina.startup.Tomcat;
|
||
|
||
public class Main {
|
||
|
||
public static void main(String[] args) throws LifecycleException {
|
||
//自己写Tomcat的启动源码
|
||
Tomcat tomcat = new Tomcat();
|
||
|
||
tomcat.setPort(8888);
|
||
tomcat.setHostname("localhost");
|
||
tomcat.setBaseDir(".");
|
||
// user.dir代表当前工作目录
|
||
Context context = tomcat.addWebapp("/boot", System.getProperty("user.dir") + "/src/main");
|
||
tomcat.start();//启动tomcat 注解版MVC利用Tomcat SPI机制
|
||
|
||
|
||
tomcat.getServer().await(); //服务器等待
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
|
||
|
||
#### QuickAppStarter
|
||
|
||
```java
|
||
package cn.imlql.boot;
|
||
|
||
import cn.imlql.boot.config.SpringConfig;
|
||
|
||
import cn.imlql.boot.config.SpringMVCConfig;
|
||
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
|
||
|
||
import javax.servlet.ServletRegistration;
|
||
|
||
|
||
/**
|
||
* 最快速的整合注解版SpringMVC和Spring的
|
||
*/
|
||
public class QuickAppStarter extends AbstractAnnotationConfigDispatcherServletInitializer {
|
||
@Override //根容器的配置(Spring的配置文件===Spring的配置类)
|
||
protected Class<?>[] getRootConfigClasses() {
|
||
return new Class<?>[]{SpringConfig.class};
|
||
}
|
||
|
||
@Override //web容器的配置(SpringMVC的配置文件===SpringMVC的配置类)
|
||
protected Class<?>[] getServletConfigClasses() {
|
||
return new Class<?>[]{SpringMVCConfig.class};
|
||
}
|
||
|
||
@Override //Servlet的映射,DispatcherServlet的映射路径
|
||
protected String[] getServletMappings() {
|
||
return new String[]{"/"};
|
||
}
|
||
|
||
@Override
|
||
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
|
||
// super.customizeRegistration(registration);
|
||
|
||
// registration.addMapping("");//
|
||
}
|
||
}
|
||
```
|
||
|
||
#### SpringConfig
|
||
|
||
```java
|
||
@ComponentScan(value = "cn.imlql.boot",excludeFilters = {
|
||
@ComponentScan.Filter(type= FilterType.ANNOTATION,value = Controller.class)
|
||
})
|
||
@Configuration
|
||
public class SpringConfig {
|
||
//Spring的父容器
|
||
|
||
}
|
||
```
|
||
|
||
#### SpringMVCConfig
|
||
|
||
```java
|
||
@ComponentScan(value = "cn.imlql.boot",includeFilters = {
|
||
@ComponentScan.Filter(type= FilterType.ANNOTATION,value = Controller.class)
|
||
},useDefaultFilters = false)
|
||
public class SpringMVCConfig {
|
||
//SpringMVC的子容器,能扫描的Spring容器中的组件
|
||
|
||
|
||
}
|
||
```
|
||
|
||
#### HelloController
|
||
|
||
```java
|
||
package cn.imlql.boot.controller;
|
||
|
||
|
||
import org.springframework.web.bind.annotation.GetMapping;
|
||
import org.springframework.web.bind.annotation.RestController;
|
||
|
||
@RestController
|
||
public class HelloController {
|
||
|
||
@GetMapping("/hello66")
|
||
public String hello(){
|
||
|
||
return "66666666~~~~~";
|
||
}
|
||
}
|
||
```
|
||
|
||
|
||
|
||
### 测试效果
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_12/image-20211022185818042.png" >
|
||
|
||
|
||
|
||
|
||
|
||
## 再来简单捋一下SPI如何启动的Web容器
|
||
|
||
1. 我们看到上面很神奇的效果,我们自己写代码做到了类似SpringBoot的效果,不需要配置本地tomcat,直接就把Web应用启动起来了。
|
||
2. 这里只是简单的捋一下,详细过程在前面讲过
|
||
|
||
### META-INF/services
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_12/image-20211022190130930.png">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
### AbstractAnnotationConfigDispatcherServletInitializer继承树
|
||
|
||
我们的QuickAppStarter实现了
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_12/image-20211022190304974.png"/>
|
||
|
||
|
||
|
||
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_12/image-20211022190418464.png"/>
|
||
|
||
|
||
|
||
### SpringServletContainerInitializer
|
||
|
||
利用Java的SPI加载META-INF/services下的实现类
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_12/image-20211022190641317.png"/>
|
||
|
||
|
||
|
||
```java
|
||
|
||
@HandlesTypes(WebApplicationInitializer.class)
|
||
public class SpringServletContainerInitializer implements ServletContainerInitializer {
|
||
|
||
/**
|
||
* Delegate the {@code ServletContext} to any {@link WebApplicationInitializer}
|
||
* implementations present on the application classpath.
|
||
* <p>Because this class declares @{@code HandlesTypes(WebApplicationInitializer.class)},
|
||
* Servlet 3.0+ containers will automatically scan the classpath for implementations
|
||
* of Spring's {@code WebApplicationInitializer} interface and provide the set of all
|
||
* such types to the {@code webAppInitializerClasses} parameter of this method.
|
||
* <p>If no {@code WebApplicationInitializer} implementations are found on the classpath,
|
||
* this method is effectively a no-op. An INFO-level log message will be issued notifying
|
||
* the user that the {@code ServletContainerInitializer} has indeed been invoked but that
|
||
* no {@code WebApplicationInitializer} implementations were found.
|
||
* <p>Assuming that one or more {@code WebApplicationInitializer} types are detected,
|
||
* they will be instantiated (and <em>sorted</em> if the @{@link
|
||
* org.springframework.core.annotation.Order @Order} annotation is present or
|
||
* the {@link org.springframework.core.Ordered Ordered} interface has been
|
||
* implemented). Then the {@link WebApplicationInitializer#onStartup(ServletContext)}
|
||
* method will be invoked on each instance, delegating the {@code ServletContext} such
|
||
* that each instance may register and configure servlets such as Spring's
|
||
* {@code DispatcherServlet}, listeners such as Spring's {@code ContextLoaderListener},
|
||
* or any other Servlet API componentry such as filters.
|
||
* @param webAppInitializerClasses all implementations of
|
||
* {@link WebApplicationInitializer} found on the application classpath
|
||
* @param servletContext the servlet context to be initialized
|
||
* @see WebApplicationInitializer#onStartup(ServletContext)
|
||
* @see AnnotationAwareOrderComparator
|
||
*/
|
||
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
|
||
throws ServletException {
|
||
|
||
List<WebApplicationInitializer> initializers = Collections.emptyList();
|
||
|
||
if (webAppInitializerClasses != null) {
|
||
initializers = new ArrayList<>(webAppInitializerClasses.size());
|
||
for (Class<?> waiClass : webAppInitializerClasses) {
|
||
// Be defensive: Some servlet containers provide us with invalid classes,
|
||
// no matter what @HandlesTypes says... 所有的非接口非抽象的WebApplicationInitializer实现类
|
||
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
|
||
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
|
||
try {
|
||
initializers.add((WebApplicationInitializer) //集合负责保存满足上面条件的类
|
||
ReflectionUtils.accessibleConstructor(waiClass).newInstance());
|
||
}
|
||
catch (Throwable ex) {
|
||
throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (initializers.isEmpty()) {
|
||
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
|
||
return;
|
||
}
|
||
//下面会遍历所有满足要求的WebApplicationInitializer,调用他们的onStartup
|
||
servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
|
||
AnnotationAwareOrderComparator.sort(initializers);
|
||
for (WebApplicationInitializer initializer : initializers) {
|
||
initializer.onStartup(servletContext); //所有的 WebApplicationInitializer 的 onStartup
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
```
|
||
|
||
|
||
|
||
### @HandlesTypes
|
||
|
||
1. 其中@HandlesTypes注解表示可以处理的类,在`onStartup` 方法中,可以通过`Set<Class<?>> webAppInitializerClasses` 获取得到。
|
||
2. @HandlesTypes属于sun公司对Servlet定义的规范,包括tomcat,jetty等服务器都对它有不同的实现
|
||
3. tomcat的具体实现咱们这里不深究,可以肯定的是一定用到了Java的SPI,如下。
|
||
|
||
```java
|
||
ServiceLoader<DataSaveService> load = ServiceLoader.load(WebApplicationInitializer.class);
|
||
```
|
||
|
||
4. tomcat具体对于@HandlesTypes一定是和上面类似甚至是一样的代码来加载WebApplicationInitializer的实现
|
||
|
||
|
||
|
||
因为咱们的QuickAppStarter继承的AbstractAnnotationConfigDispatcherServletInitializer也属于WebApplicationInitializer,所以它就会被加载
|
||
|
||
|
||
|
||
### Servlet相关规范
|
||
|
||
1. tomcat会遵循sun公司的规范给每一个Servlet创建对象
|
||
2. 所以DispatcherServlet肯定也会创建对象
|
||
|
||
3. Servlet的规范
|
||
1. Servlet创建对象
|
||
2. Servlet调用Init初始化
|
||
3. 每次请求调用service处理
|
||
4. tomcat停止的时候调用destroy进行销毁
|
||
4. Serlvet是被谁调用开始初始化的属于tomcat的源码,我们这里不研究
|
||
|
||
|
||
|
||
|
||
|
||
### DispatcherServlet
|
||
|
||
1. spring-web中有一个叫DispatcherServlet的类,很明显他是一个Servlet,所以tomcat启动的时候就会加载它,加载它的话当然是从父类一层一层加载的
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_12/image-20211013195948793.png">
|
||
|
||
2. 也就是说是从Servlet最顶层开始一层一层往下面调用
|
||
3. 最终我们发现FrameworkServlet里有一个核心方法
|
||
|
||
|
||
|
||
### FrameworkServlet
|
||
|
||
```java
|
||
/** 追踪看web应用启动做了什么。
|
||
* Overridden method of {@link HttpServletBean}, invoked after any bean properties
|
||
* have been set. Creates this servlet's WebApplicationContext.
|
||
*/
|
||
@Override
|
||
protected final void initServletBean() throws ServletException {
|
||
getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
|
||
if (logger.isInfoEnabled()) {
|
||
logger.info("Initializing Servlet '" + getServletName() + "'");
|
||
}
|
||
long startTime = System.currentTimeMillis();
|
||
|
||
try {
|
||
this.webApplicationContext = initWebApplicationContext(); //初始化WebIOC容器,那我们想一下大概率是在这里启动的IOC容器
|
||
initFrameworkServlet(); //这又是留给子类的
|
||
}
|
||
catch (ServletException | RuntimeException ex) {
|
||
logger.error("Context initialization failed", ex);
|
||
throw ex;
|
||
}
|
||
|
||
if (logger.isDebugEnabled()) {
|
||
String value = this.enableLoggingRequestDetails ?
|
||
"shown which may lead to unsafe logging of potentially sensitive data" :
|
||
"masked to prevent unsafe logging of potentially sensitive data";
|
||
logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
|
||
"': request parameters and headers will be " + value);
|
||
}
|
||
|
||
if (logger.isInfoEnabled()) {
|
||
logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
|
||
}
|
||
}
|
||
```
|
||
|
||
|
||
|
||
1. `this.webApplicationContext = initWebApplicationContext();`没错,看名字就知道是从这里开始启动Web容器的。
|
||
|
||
2. 然后我们就自己搭建了一个MySpringBoot项目,我们这个项目和SpringBoot官方的区别就是官方帮我们封装了很多自动配置类,帮我们给容器中放了很多组件,使得我们感觉开发更方便了。
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
## SpringBoot启动导入了很多自动配置类
|
||
|
||
为什么 @SpringBootApplication +SpringApplication.run(SpringbootSourceApplication.class, args);能把Spring+SpringMVC+Tomcat+其他场景都整合进来
|
||
|
||
|
||
|
||
```java
|
||
@SpringBootApplication
|
||
public class SpringbootSourceApplication {
|
||
|
||
public static void main(String[] args) {
|
||
SpringApplication.run(SpringbootSourceApplication.class, args);
|
||
}
|
||
}
|
||
```
|
||
|
||
|
||
|
||
### pom.xml
|
||
|
||
```java
|
||
<?xml version="1.0" encoding="UTF-8"?>
|
||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||
<modelVersion>4.0.0</modelVersion>
|
||
<parent>
|
||
<groupId>org.springframework.boot</groupId>
|
||
<artifactId>spring-boot-starter-parent</artifactId>
|
||
<version>2.4.4</version>
|
||
<relativePath/> <!-- lookup parent from repository -->
|
||
</parent>
|
||
<groupId>com.atuigu.boot</groupId>
|
||
<artifactId>springboot-source</artifactId>
|
||
<version>0.0.1-SNAPSHOT</version>
|
||
<name>springboot-source</name>
|
||
<description>Demo project for Spring Boot</description>
|
||
<properties>
|
||
<java.version>1.8</java.version>
|
||
</properties>
|
||
<dependencies>
|
||
<dependency>
|
||
<groupId>org.springframework.boot</groupId>
|
||
<artifactId>spring-boot-starter-web</artifactId>
|
||
</dependency>
|
||
|
||
<dependency>
|
||
<groupId>org.springframework.boot</groupId>
|
||
<artifactId>spring-boot-starter-test</artifactId>
|
||
<scope>test</scope>
|
||
</dependency>
|
||
</dependencies>
|
||
|
||
<build>
|
||
<plugins>
|
||
<plugin>
|
||
<groupId>org.springframework.boot</groupId>
|
||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||
</plugin>
|
||
</plugins>
|
||
</build>
|
||
|
||
</project>
|
||
```
|
||
|
||
1. 首先是在Maven依赖上的支持,spring-boot-starter-xxx的这种依赖内部又导入了很多的依赖,包括上面说的嵌入式tomcat,以及Spring,SpringMVC
|
||
|
||
|
||
|
||
|
||
|
||
### @SpringBootApplication原理
|
||
|
||
```java
|
||
@Target(ElementType.TYPE)
|
||
@Retention(RetentionPolicy.RUNTIME)
|
||
@Documented
|
||
@Inherited
|
||
@SpringBootConfiguration
|
||
@EnableAutoConfiguration
|
||
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
|
||
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
|
||
public @interface SpringBootApplication {
|
||
|
||
//......
|
||
}
|
||
```
|
||
|
||
|
||
|
||
### @SpringBootConfiguration
|
||
|
||
```java
|
||
@Target(ElementType.TYPE)
|
||
@Retention(RetentionPolicy.RUNTIME)
|
||
@Documented
|
||
@Configuration
|
||
public @interface SpringBootConfiguration {
|
||
|
||
@AliasFor(annotation = Configuration.class)
|
||
boolean proxyBeanMethods() default true;
|
||
|
||
}
|
||
```
|
||
|
||
这个注解的功能就相当于@Configuration
|
||
|
||
### @EnableAutoConfiguration
|
||
|
||
```java
|
||
@Target(ElementType.TYPE)
|
||
@Retention(RetentionPolicy.RUNTIME)
|
||
@Documented
|
||
@Inherited
|
||
@AutoConfigurationPackage
|
||
@Import(AutoConfigurationImportSelector.class)
|
||
public @interface EnableAutoConfiguration {
|
||
|
||
//......
|
||
}
|
||
```
|
||
|
||
### @AutoConfigurationPackage
|
||
|
||
```java
|
||
@Target(ElementType.TYPE)
|
||
@Retention(RetentionPolicy.RUNTIME)
|
||
@Documented
|
||
@Inherited
|
||
@Import(AutoConfigurationPackages.Registrar.class)
|
||
public @interface AutoConfigurationPackage {
|
||
|
||
//......
|
||
}
|
||
```
|
||
|
||
## @AutoConfigurationPackage导入的AutoConfigurationPackages.Registrar类
|
||
|
||
|
||
|
||
### AutoConfigurationPackages.Registrar#registerBeanDefinitions()
|
||
|
||
```java
|
||
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
|
||
|
||
@Override
|
||
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
|
||
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
|
||
}
|
||
|
||
@Override
|
||
public Set<Object> determineImports(AnnotationMetadata metadata) {
|
||
return Collections.singleton(new PackageImports(metadata));
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_12/image-20211022211609038.png" />
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_12/image-20211022211757069.png" />
|
||
|
||
这里就是得到要注册哪些包下的信息,F7进入此方法。从这里你也能知道SpringBoot默认导的包是SpringbootXXXApplication所在的那个包层级
|
||
|
||
### AutoConfigurationPackages#register()
|
||
|
||
```java
|
||
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
|
||
if (registry.containsBeanDefinition(BEAN)) {
|
||
BasePackagesBeanDefinition beanDefinition = (BasePackagesBeanDefinition) registry.getBeanDefinition(BEAN);
|
||
beanDefinition.addBasePackages(packageNames);
|
||
}
|
||
else {
|
||
registry.registerBeanDefinition(BEAN, new BasePackagesBeanDefinition(packageNames));
|
||
}
|
||
}
|
||
|
||
private static final String BEAN = AutoConfigurationPackages.class.getName();
|
||
```
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_12/image-20211022212045090.png" />
|
||
|
||
### AutoConfigurationPackages.BasePackagesBeanDefinition
|
||
|
||
```java
|
||
static final class BasePackagesBeanDefinition extends GenericBeanDefinition {
|
||
|
||
private final Set<String> basePackages = new LinkedHashSet<>();
|
||
|
||
BasePackagesBeanDefinition(String... basePackages) {
|
||
setBeanClass(BasePackages.class);
|
||
setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||
addBasePackages(basePackages);//就是要指定最终要扫哪些包
|
||
}
|
||
|
||
@Override
|
||
public Supplier<?> getInstanceSupplier() {
|
||
return () -> new BasePackages(StringUtils.toStringArray(this.basePackages));
|
||
}
|
||
|
||
private void addBasePackages(String[] additionalBasePackages) {
|
||
this.basePackages.addAll(Arrays.asList(additionalBasePackages));
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
|
||
|
||
### BeanDefinitionMap里的数据
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_12/image-20211022212703485.png" />
|
||
|
||
此时beanDefinitionMap已经有了AutoConfigurationPackages,当处理到这个Bean的时候,最终发现这是个包导入的组件,最终就会导入这个包里面的组件
|
||
|
||
|
||
|
||
## @EnableAutoConfiguration注解导入的AutoConfigurationImportSelector类
|
||
|
||
|
||
|
||
```java
|
||
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
|
||
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
|
||
|
||
//......
|
||
|
||
}
|
||
```
|
||
|
||
1. AutoConfigurationImportSelector是用@Import注解导进来的
|
||
2. AutoConfigurationImportSelector根据它的名字很明显它是一个ImportSelector的实现类,了解ImportSelector的都应该知道它是通过`selectImports()`方法来实现导入哪些组件的
|
||
|
||
|
||
|
||
### AutoConfigurationImportSelector.AutoConfigurationGroup#process()
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_12/image-20211022214332439.png" />
|
||
|
||
F7进入此方法
|
||
|
||
### AutoConfigurationImportSelector#getAutoConfigurationEntry()
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_12/image-20211022214425824.png" />
|
||
|
||
F7进入此方法
|
||
|
||
### AutoConfigurationImportSelector#getCandidateConfigurations()
|
||
|
||
```java
|
||
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
|
||
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
|
||
getBeanClassLoader());
|
||
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
|
||
+ "are using a custom packaging, make sure that file is correct.");
|
||
return configurations;
|
||
}
|
||
|
||
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
|
||
return EnableAutoConfiguration.class;
|
||
}
|
||
```
|
||
|
||
|
||
|
||
### SpringFactoriesLoader#loadFactoryNames()
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_12/image-20211022214744474.png" />
|
||
|
||
|
||
|
||
### SpringFactoriesLoader#loadSpringFactories()加载类路径下META-INF/spring.factories的资源
|
||
|
||
```java
|
||
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
|
||
|
||
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
|
||
Map<String, List<String>> result = cache.get(classLoader);
|
||
if (result != null) {
|
||
return result;
|
||
}
|
||
|
||
result = new HashMap<>();
|
||
try {
|
||
//加载类路径下META-INF/spring.factories的资源
|
||
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
|
||
while (urls.hasMoreElements()) {
|
||
URL url = urls.nextElement();
|
||
UrlResource resource = new UrlResource(url);
|
||
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
|
||
for (Map.Entry<?, ?> entry : properties.entrySet()) {
|
||
String factoryTypeName = ((String) entry.getKey()).trim();
|
||
String[] factoryImplementationNames =
|
||
StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
|
||
for (String factoryImplementationName : factoryImplementationNames) {
|
||
result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
|
||
.add(factoryImplementationName.trim());
|
||
}
|
||
}
|
||
}
|
||
|
||
// Replace all lists with unmodifiable lists containing unique elements
|
||
result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
|
||
.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
|
||
cache.put(classLoader, result);
|
||
}
|
||
catch (IOException ex) {
|
||
throw new IllegalArgumentException("Unable to load factories from location [" +
|
||
FACTORIES_RESOURCE_LOCATION + "]", ex);
|
||
}
|
||
return result;
|
||
}
|
||
```
|
||
|
||
然后咱们就要找类路径下META-INF/spring.factories,并且名字是`org.springframework.boot.autoconfigure.EnableAutoConfiguration`的资源,这有点类似于SPI机制
|
||
|
||
### spring.factories
|
||
|
||
1. 不止这个包下有spring.factories文件,可能很多第三方的starter都有,这个包下的这些类只是Spring能想到的常用的组件。
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_12/image-20211022215644854.png" />
|
||
|
||
|
||
|
||
### 返回到AutoConfigurationImportSelector#getAutoConfigurationEntry()
|
||
|
||
1. 自此这130个组件会先被放到List里,但不一定全部导入
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_12/image-20211022220450954.png" />
|
||
|
||
2. 然后这里会有一个过滤`configurations = getConfigurationClassFilter().filter(configurations);`
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_12/image-20211022220655328.png" />
|
||
|
||
3. 最终这里只会有23个组件被放到容器中,为什么这里要过滤?看下面的@ConditionalOnClass注解,当容器中有KafkaTemplate这个类时才会导入KafkaAutoConfiguration,而KafkaTemplate这个类只有导入了kafka相关jar包才会有。意思就是你只有在maven中导入了相关jar包,才会给你自动配置
|
||
|
||
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_12/image-20211022223803718.png"/>
|
||
|
||
|
||
|
||
还有下面这个SpringMvc的,当你有DispatcherServlet这个类的时候,才会给你自动配置web相关的东西。而有DispatcherServlet类就代表你导入了web的相关依赖
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_12/image-20211022224029198.png" />
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
## 容器刷新在onRefresh步骤会启动Tomcat
|
||
|
||
|
||
|
||
1. 在刚开始的时候我们自己实现的简易SpringBoot是利用SPI机制启动的Web容器
|
||
2. 其实我们还要一个方法就是自己创建一个DispatcherServlet注册到Tomcat里,然后Tomcat就会调用Servlet相关初始化,最终调用到FrameworkServlet类里调用的`this.webApplicationContext = initWebApplicationContext();`,**进而启动Web容器。在SpringBoot里使用的就是这种方式启动Web容器**
|
||
|
||
|
||
|
||
### DispatcherServletAutoConfiguration
|
||
|
||
```java
|
||
package org.springframework.boot.autoconfigure.web.servlet;
|
||
|
||
|
||
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
|
||
@Configuration(proxyBeanMethods = false)
|
||
@ConditionalOnWebApplication(type = Type.SERVLET)
|
||
@ConditionalOnClass(DispatcherServlet.class)
|
||
//这里就是DispatcherServlet在自动配置之前,先自动配置ServletWebServerFactoryAutoConfiguration
|
||
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
|
||
public class DispatcherServletAutoConfiguration {
|
||
|
||
/**
|
||
* The bean name for a DispatcherServlet that will be mapped to the root URL "/".
|
||
*/
|
||
public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
|
||
|
||
/**
|
||
* The bean name for a ServletRegistrationBean for the DispatcherServlet "/".
|
||
*/
|
||
public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";
|
||
|
||
@Configuration(proxyBeanMethods = false)
|
||
@Conditional(DefaultDispatcherServletCondition.class)
|
||
@ConditionalOnClass(ServletRegistration.class)
|
||
@EnableConfigurationProperties(WebMvcProperties.class)
|
||
protected static class DispatcherServletConfiguration {
|
||
|
||
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
|
||
public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
|
||
DispatcherServlet dispatcherServlet = new DispatcherServlet();
|
||
dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
|
||
dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
|
||
dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
|
||
dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
|
||
dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
|
||
return dispatcherServlet;
|
||
}
|
||
|
||
@Bean
|
||
@ConditionalOnBean(MultipartResolver.class)
|
||
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
|
||
public MultipartResolver multipartResolver(MultipartResolver resolver) {
|
||
// Detect if the user has created a MultipartResolver but named it incorrectly
|
||
return resolver;
|
||
}
|
||
|
||
}
|
||
|
||
@Configuration(proxyBeanMethods = false)
|
||
@Conditional(DispatcherServletRegistrationCondition.class)
|
||
@ConditionalOnClass(ServletRegistration.class)
|
||
@EnableConfigurationProperties(WebMvcProperties.class)
|
||
@Import(DispatcherServletConfiguration.class)
|
||
protected static class DispatcherServletRegistrationConfiguration {
|
||
|
||
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
|
||
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
|
||
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
|
||
WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
|
||
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
|
||
webMvcProperties.getServlet().getPath());
|
||
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
|
||
registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
|
||
multipartConfig.ifAvailable(registration::setMultipartConfig);
|
||
return registration;
|
||
}
|
||
|
||
}
|
||
|
||
@Order(Ordered.LOWEST_PRECEDENCE - 10)
|
||
private static class DefaultDispatcherServletCondition extends SpringBootCondition {
|
||
// ......
|
||
}
|
||
|
||
@Order(Ordered.LOWEST_PRECEDENCE - 10)
|
||
private static class DispatcherServletRegistrationCondition extends SpringBootCondition {
|
||
// ......
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
1. `@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)`
|
||
2. 这里就是最关键的,@AutoConfigureAfter注解看名字就能大概明白是什么意思,这里就是DispatcherServlet在自动配置之前,先自动配置ServletWebServerFactoryAutoConfiguration
|
||
|
||
|
||
|
||
|
||
|
||
### ServletWebServerFactoryAutoConfiguration
|
||
|
||
```java
|
||
@Configuration(proxyBeanMethods = false)
|
||
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
|
||
@ConditionalOnClass(ServletRequest.class)
|
||
@ConditionalOnWebApplication(type = Type.SERVLET)
|
||
@EnableConfigurationProperties(ServerProperties.class)
|
||
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, //这里是最核心的
|
||
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, //这里都是嵌入式服务器
|
||
ServletWebServerFactoryConfiguration.EmbeddedJetty.class, //这里都是嵌入式服务器
|
||
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class }) //这里都是嵌入式服务器
|
||
public class ServletWebServerFactoryAutoConfiguration {
|
||
// ......
|
||
}
|
||
```
|
||
|
||
|
||
|
||
|
||
|
||
### ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar
|
||
|
||
```java
|
||
@Override
|
||
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
|
||
BeanDefinitionRegistry registry) {
|
||
if (this.beanFactory == null) {
|
||
return;
|
||
}
|
||
//给容器中注册一个服务器的后置处理器
|
||
registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
|
||
WebServerFactoryCustomizerBeanPostProcessor.class,
|
||
WebServerFactoryCustomizerBeanPostProcessor::new);
|
||
registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
|
||
ErrorPageRegistrarBeanPostProcessor.class, ErrorPageRegistrarBeanPostProcessor::new);
|
||
}
|
||
```
|
||
|
||
|
||
|
||
|
||
|
||
### ServletWebServerFactoryConfiguration
|
||
|
||
```java
|
||
package org.springframework.boot.autoconfigure.web.servlet;
|
||
|
||
@Configuration(proxyBeanMethods = false)
|
||
class ServletWebServerFactoryConfiguration {
|
||
|
||
@Configuration(proxyBeanMethods = false)
|
||
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
|
||
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
|
||
static class EmbeddedTomcat {
|
||
|
||
@Bean
|
||
TomcatServletWebServerFactory tomcatServletWebServerFactory(
|
||
ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
|
||
ObjectProvider<TomcatContextCustomizer> contextCustomizers,
|
||
ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
|
||
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
|
||
factory.getTomcatConnectorCustomizers()
|
||
.addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
|
||
factory.getTomcatContextCustomizers()
|
||
.addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
|
||
factory.getTomcatProtocolHandlerCustomizers()
|
||
.addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
|
||
return factory;
|
||
}
|
||
|
||
}
|
||
|
||
/**
|
||
* Nested configuration if Jetty is being used.
|
||
*/
|
||
@Configuration(proxyBeanMethods = false)
|
||
@ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })
|
||
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
|
||
static class EmbeddedJetty {
|
||
|
||
@Bean
|
||
JettyServletWebServerFactory JettyServletWebServerFactory(
|
||
ObjectProvider<JettyServerCustomizer> serverCustomizers) {
|
||
JettyServletWebServerFactory factory = new JettyServletWebServerFactory();
|
||
factory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList()));
|
||
return factory;
|
||
}
|
||
|
||
}
|
||
|
||
/**
|
||
* Nested configuration if Undertow is being used.
|
||
*/
|
||
@Configuration(proxyBeanMethods = false)
|
||
@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
|
||
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
|
||
static class EmbeddedUndertow {
|
||
|
||
@Bean
|
||
UndertowServletWebServerFactory undertowServletWebServerFactory(
|
||
ObjectProvider<UndertowDeploymentInfoCustomizer> deploymentInfoCustomizers,
|
||
ObjectProvider<UndertowBuilderCustomizer> builderCustomizers) {
|
||
UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
|
||
factory.getDeploymentInfoCustomizers()
|
||
.addAll(deploymentInfoCustomizers.orderedStream().collect(Collectors.toList()));
|
||
factory.getBuilderCustomizers().addAll(builderCustomizers.orderedStream().collect(Collectors.toList()));
|
||
return factory;
|
||
}
|
||
|
||
@Bean
|
||
UndertowServletWebServerFactoryCustomizer undertowServletWebServerFactoryCustomizer(
|
||
ServerProperties serverProperties) {
|
||
return new UndertowServletWebServerFactoryCustomizer(serverProperties);
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
ServletWebServerFactory:服务器工厂,我们可以自己放Serlvet容器,我们自己放了就会用我们自己的
|
||
|
||
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_12/image-20211022230030459.png" />
|
||
|
||
XXXProvider的意思就是这些方法的参数都是从容器中拿,如果你自定义了,就用自定义的
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
### TomcatServletWebServerFactory自己new Tomcat()
|
||
|
||
```java
|
||
public WebServer getWebServer(ServletContextInitializer... initializers) {
|
||
if (this.disableMBeanRegistry) {
|
||
Registry.disableRegistry();
|
||
}
|
||
Tomcat tomcat = new Tomcat();
|
||
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
|
||
tomcat.setBaseDir(baseDir.getAbsolutePath());
|
||
Connector connector = new Connector(this.protocol);
|
||
connector.setThrowOnFailure(true);
|
||
tomcat.getService().addConnector(connector);
|
||
customizeConnector(connector);
|
||
tomcat.setConnector(connector);
|
||
tomcat.getHost().setAutoDeploy(false);
|
||
configureEngine(tomcat.getEngine());
|
||
for (Connector additionalConnector : this.additionalTomcatConnectors) {
|
||
tomcat.getService().addConnector(additionalConnector);
|
||
}
|
||
prepareContext(tomcat.getHost(), initializers);
|
||
return getTomcatWebServer(tomcat);
|
||
}
|
||
```
|
||
|
||
#### Debug调用栈
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_12/image-20211022230533248.png" />
|
||
|
||
|
||
|
||
#### ServletWebServerApplicationContext
|
||
|
||
```java
|
||
@Override
|
||
protected void onRefresh() {
|
||
super.onRefresh();
|
||
try {
|
||
createWebServer();
|
||
}
|
||
catch (Throwable ex) {
|
||
throw new ApplicationContextException("Unable to start web server", ex);
|
||
}
|
||
}
|
||
|
||
|
||
private void createWebServer() {
|
||
WebServer webServer = this.webServer;
|
||
ServletContext servletContext = getServletContext();
|
||
if (webServer == null && servletContext == null) {
|
||
StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
|
||
ServletWebServerFactory factory = getWebServerFactory();
|
||
createWebServer.tag("factory", factory.getClass().toString());
|
||
//最终在这里调用了TomcatServletWebServerFactory#getWebServer()
|
||
this.webServer = factory.getWebServer(getSelfInitializer());
|
||
createWebServer.end();
|
||
getBeanFactory().registerSingleton("webServerGracefulShutdown",
|
||
new WebServerGracefulShutdownLifecycle(this.webServer));
|
||
getBeanFactory().registerSingleton("webServerStartStop",
|
||
new WebServerStartStopLifecycle(this, this.webServer));
|
||
}
|
||
else if (servletContext != null) {
|
||
try {
|
||
getSelfInitializer().onStartup(servletContext);
|
||
}
|
||
catch (ServletException ex) {
|
||
throw new ApplicationContextException("Cannot initialize servlet context", ex);
|
||
}
|
||
}
|
||
initPropertySources();
|
||
}
|
||
```
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
#### 返回到getWebServer并且调用prepareContext()
|
||
|
||
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_12/image-20211022231006207.png" />
|
||
|
||
|
||
|
||
注意看`ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);`这一步就是Tomcat启动加载DispatcherServlet的时机
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
## Tomcat启动加载DispatcherServlet的时机
|
||
|
||
```java
|
||
package org.springframework.boot.autoconfigure.web.servlet;
|
||
|
||
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
|
||
@Configuration(proxyBeanMethods = false)
|
||
@ConditionalOnWebApplication(type = Type.SERVLET)
|
||
@ConditionalOnClass(DispatcherServlet.class)
|
||
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
|
||
public class DispatcherServletAutoConfiguration {
|
||
|
||
/**
|
||
* The bean name for a DispatcherServlet that will be mapped to the root URL "/".
|
||
*/
|
||
public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
|
||
|
||
/**
|
||
* The bean name for a ServletRegistrationBean for the DispatcherServlet "/".
|
||
*/
|
||
public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";
|
||
|
||
@Configuration(proxyBeanMethods = false)
|
||
@Conditional(DefaultDispatcherServletCondition.class)
|
||
@ConditionalOnClass(ServletRegistration.class)
|
||
@EnableConfigurationProperties(WebMvcProperties.class)
|
||
protected static class DispatcherServletConfiguration {
|
||
|
||
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
|
||
public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
|
||
DispatcherServlet dispatcherServlet = new DispatcherServlet();
|
||
dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
|
||
dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
|
||
dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
|
||
dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
|
||
dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
|
||
return dispatcherServlet;
|
||
}
|
||
|
||
@Bean
|
||
@ConditionalOnBean(MultipartResolver.class)
|
||
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
|
||
public MultipartResolver multipartResolver(MultipartResolver resolver) {
|
||
// Detect if the user has created a MultipartResolver but named it incorrectly
|
||
return resolver;
|
||
}
|
||
|
||
}
|
||
|
||
@Configuration(proxyBeanMethods = false)
|
||
@Conditional(DispatcherServletRegistrationCondition.class)
|
||
@ConditionalOnClass(ServletRegistration.class)
|
||
@EnableConfigurationProperties(WebMvcProperties.class)
|
||
@Import(DispatcherServletConfiguration.class)
|
||
protected static class DispatcherServletRegistrationConfiguration {
|
||
|
||
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
|
||
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
|
||
//注意看DispatcherServletRegistrationBean
|
||
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
|
||
WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
|
||
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
|
||
webMvcProperties.getServlet().getPath());
|
||
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
|
||
registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
|
||
multipartConfig.ifAvailable(registration::setMultipartConfig);
|
||
return registration;
|
||
}
|
||
|
||
}
|
||
|
||
// ......
|
||
}
|
||
```
|
||
|
||
|
||
|
||
### DispatcherServletRegistrationBean继承树
|
||
|
||
我们发现DispatcherServletRegistrationBean它是一个ServletContextInitializer
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_12/image-20211022231624162.png"/>
|
||
|
||
|
||
|
||
F7进入上面说的这个`configureContext(context, initializersToUse);`
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_12/image-20211022231006207.png" />
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
### TomcatServletWebServerFactory#configureContext()
|
||
|
||
```java
|
||
protected void configureContext(Context context, ServletContextInitializer[] initializers) {
|
||
TomcatStarter starter = new TomcatStarter(initializers);//注意在这里把这些ServletContextInitializer给了TomcatStarter
|
||
// ......
|
||
for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) {
|
||
customizer.customize(context);
|
||
}
|
||
}
|
||
```
|
||
|
||
|
||
|
||
### 返回到TomcatServletWebServerFactory#getWebServer()
|
||
|
||
然后返回到getWebServer调用最后一步
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_12/image-20211022232804479.png" />
|
||
|
||
|
||
|
||
|
||
|
||
### TomcatStarter#onStartup()
|
||
|
||
最终初始化会调用到onStartup()
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_12/image-20211022232711196.png" />
|
||
|
||
```java
|
||
TomcatStarter(ServletContextInitializer[] initializers) {
|
||
this.initializers = initializers;
|
||
}
|
||
|
||
@Override
|
||
public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
|
||
try {
|
||
for (ServletContextInitializer initializer : this.initializers) {
|
||
initializer.onStartup(servletContext);
|
||
}
|
||
}
|
||
catch (Exception ex) {
|
||
this.startUpException = ex;
|
||
// Prevent Tomcat from logging and re-throwing when we know we can
|
||
// deal with it in the main thread, but log for information here.
|
||
if (logger.isErrorEnabled()) {
|
||
logger.error("Error starting Tomcat context. Exception: " + ex.getClass().getName() + ". Message: "
|
||
+ ex.getMessage());
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_12/image-20211022232431746.png" />
|
||
|
||
### RegistrationBean#onStartup()
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_12/image-20211022233001647.png" />
|
||
|
||
F7进入,省略到一些不重要的方法
|
||
|
||
|
||
|
||
### DynamicRegistrationBean#register()
|
||
|
||
```java
|
||
protected final void register(String description, ServletContext servletContext) {
|
||
D registration = addRegistration(description, servletContext);
|
||
if (registration == null) {
|
||
logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
|
||
return;
|
||
}
|
||
configure(registration);
|
||
}
|
||
```
|
||
|
||
|
||
|
||
### ServletRegistration.Dynamic#addRegistration()将dispatcherServlet放入Tomcat容器中
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_12/image-20211022234633327.png" />
|
||
|
||
servletContext这个就是tomcat容器
|
||
|
||
1. ServletRegistration.Dynamic#addRegistration()将dispatcherServlet放入tomcat容器中
|
||
|
||
2. 然后Tomcat启动之后自然就调用Servelt初始化,进而调到了dispatcherServlet,然后就是之前讲过的初始化web容器。
|
||
|
||
`this.webApplicationContext = initWebApplicationContext();`
|
||
|
||
|
||
|
||
得到下面的结论
|
||
|
||
```java
|
||
@SpringBootApplication
|
||
public class SpringbootSourceApplication {
|
||
|
||
public static void main(String[] args) {
|
||
SpringApplication.run(SpringbootSourceApplication.class, args);
|
||
}
|
||
|
||
@Bean //所有的xxxRegistrationBean都是允许我们注册原生的Servlet组件进去,
|
||
//利用 ServletContextInitializer在Tomcat启动完成以后进行回调的机制
|
||
ServletRegistrationBean<HelloServlet> registrationBean(){
|
||
|
||
ServletRegistrationBean<HelloServlet> registrationBean = new ServletRegistrationBean<>(new HelloServlet());
|
||
registrationBean.addUrlMappings("/he66");
|
||
return registrationBean;
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
|
||
|
||
## SpringApplication的run方法
|
||
|
||
```java
|
||
public ConfigurableApplicationContext run(String... args) {
|
||
StopWatch stopWatch = new StopWatch();
|
||
stopWatch.start();
|
||
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
|
||
ConfigurableApplicationContext context = null;
|
||
configureHeadlessProperty();
|
||
SpringApplicationRunListeners listeners = getRunListeners(args);
|
||
listeners.starting(bootstrapContext, this.mainApplicationClass);
|
||
try {
|
||
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
|
||
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
|
||
configureIgnoreBeanInfo(environment);
|
||
Banner printedBanner = printBanner(environment);
|
||
//创建容器
|
||
context = createApplicationContext();
|
||
context.setApplicationStartup(this.applicationStartup);
|
||
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
|
||
//刷新容器
|
||
refreshContext(context);
|
||
afterRefresh(context, applicationArguments);
|
||
stopWatch.stop();
|
||
if (this.logStartupInfo) {
|
||
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
|
||
}
|
||
listeners.started(context);
|
||
callRunners(context, applicationArguments);
|
||
}
|
||
catch (Throwable ex) {
|
||
handleRunFailure(context, ex, listeners);
|
||
throw new IllegalStateException(ex);
|
||
}
|
||
|
||
try {
|
||
listeners.running(context);
|
||
}
|
||
catch (Throwable ex) {
|
||
handleRunFailure(context, ex, null);
|
||
throw new IllegalStateException(ex);
|
||
}
|
||
return context;
|
||
}
|
||
```
|
||
|
||
就是这样很简单
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_12/image-20211022235629787.png" />
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|