文章目录
- 依赖管理
 - pom依赖管理
 - Web依赖
 - 自定义starter
 
- 一、WebMvcAutoConfiguration
 - 1.1 Filter
 - 1.2 Interceptor
 
- 二、源码解析
 - 2.1 SpringApplication
 - 2.1.1 构造方法
 - 1、填充webApplicationType
 - 2、自动装配Initializers
 - 3、自动装配Listeners
 
- 2.1.2 run(args)
 
- 2.2 SpringApplicationRunListeners
 - 2.3 prepareEnvironment
 - 加载propertySources
 - 填充activeProfiles属性
 - 读取properties、xml、yaml后缀文件
 
- 2.4 createApplicationContext
 - 创建DefaultListableBeanFactory
 - AnnotationBeanDefinitionReader
 - ClassPathBeanDefinitionScanner
 
- 2.5 prepareContext
 - applyInitializers
 - 为beanFactory设置属性
 - 创建启动类beanDefinition
 - listeners.contextLoaded
 
- 2.6 refreshContext
 - 自动装配
 - getAutoConfigurationMetadata
 - 去重 + 排除 + @conditional
 - 1、去重-getExclusions
 - 2、排除-getExclusions
 - 3、@conditional-filter
 
- 自定义自动装配类
 
- 2.7 tomcat组件
 - 2.7.1定义
 - 2.7.2 tomcat组件
 - Server
 - Service
 - Connector
 - 3.1 Endpoint
 - 3.2 Processor
 - 3.3 Adapter
 
- Container
 - **Engine**
 - **Host 虚拟主机**
 - **Context上下文**
 - **Wrapper包装器**
 
- 2.7.3 组件Lifecycle
 
- 2.8 内嵌tomcat
 - 2.8.1 getWebServerFactory
 - 2.8.2 getWebServer
 - tomcat-所有组件init
 - tomcat-engine-启动
 
- 2.9 finishRefresh
 - 2.9.1 startWebServer-NioEndpoint启动
 - 4、Thread.run:Acceptor监听
 - 5.处理请求:Handler处理请求
 - 6、processor.process
 - 7、CoyoteAdapter.service
 - 8、Container-Engineer
 - 9、Container-Host
 - 10、Container-Context
 - 11、Container-Wrapper
 - 12、后续内容
 
- 2.9.2 publishEvent
 
- 2.10 stop-tomcat
 
- 注解
 - @SpringBootApplication
 
Springboot中文文档
依赖管理
pom依赖管理
1)在SpringBoot项目中,根pom内容
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.6.RELEASE</version>
</parent>
 
2)此spring-boot-starter-parent-2.1.6.RELEASE.pom中仍有父pom
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.1.6.RELEASE</version>
    <relativePath>../../spring-boot-dependencies</relativePath>
</parent>
 
3)spring-boot-dependencies-2.1.6.RELEASE.pom
<properties>
    <!-- 指定了我们常用中间件的版本号-->	
	<spring.version>5.1.8.RELEASE</spring.version>
	<byte-buddy.version>1.9.13</byte-buddy.version>
	<commons-lang3.version>3.8.1</commons-lang3.version>
    <elasticsearch.version>6.4.3</elasticsearch.version>
    <gson.version>2.8.5</gson.version>
    <jedis.version>2.9.3</jedis.version>
    <kafka.version>2.0.1</kafka.version>
    <lombok.version>1.18.8</lombok.version>
    <mysql.version>8.0.16</mysql.version>
    <netty.version>4.1.36.Final</netty.version>
    <rabbit-amqp-client.version>5.4.3</rabbit-amqp-client.version>
    <spring-amqp.version>2.1.7.RELEASE</spring-amqp.version>
    <spring-kafka.version>2.2.7.RELEASE</spring-kafka.version>
	----
</properties>
 
<!-- SpringBoot支持提供的场景启动器starter-->	
<dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot</artifactId>
        <version>2.1.6.RELEASE</version>
      </dependency>
    
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-test-autoconfigure</artifactId>
        <version>2.1.6.RELEASE</version>
      </dependency>
    
     <!-- 指定了我们常用中间件的版本号-->	
  </dependencies> 
</dependencyManagement>   
 
这样我们在项目根pom中引入中间件时,就无需再指定version。当然如果指定version,则以我们指定的version版本为主
Web依赖
1、我们在项目根pom中,引入了web依赖
<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-web</artifactId>
     <version>3.0.6</version>
</dependency>
 
2、spring-boot-starter-web-3.0.6.pom中
<dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-tomcat</artifactId>
      <version>3.0.6</version>
      <scope>compile</scope>
    </dependency>
    
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>6.0.8</version>
      <scope>compile</scope>
    </dependency>
    
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>6.0.8</version>
      <scope>compile</scope>
    </dependency>
    
  </dependencies>
 
又引入了tomcat 和 spring-webmvc
3、依赖传递
- 因为spring-boot-starter-web-3.0.6.pom中有spring-webmvc的依赖
 - 又因为我们项目根pom中,依赖了spring-boot-starter-web-3.0.6
 - 所以根pom有spring-webmvc的依赖
 - 所以,在我们项目中,就可以使用@Controller注解(org.springframework.web.bind.annotation)是spring-web下的内容
 
4、其它场景的starter
除了spring-boot-starter-web引入Web,还可以引入其它场景的依赖启动器
地址:SpringBoot提供的常见依赖启动器
自定义starter
1、背景
有些中间件,SpringBoot没有为其提供启动器,如mybatis、Druid。这些中间件自己主动与SpringBoot完成整合,提供了启动器
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.4</version>
</dependency>
 
所以,从名称上看,mybaits不是spring-boot-starter-mybatis,而是mybatis-spring-boot-starter。
说明了mybaits不是SpringBoot本身就提供的启动器
注意:
- SpringBoot提供的starter,在引入时,可以不指定版本号,因为parsent的parent:spring-boot-dependencies-2.1.6.RELEASE.pom中指定了默认版本号
 - 但是SpringBoot没有提供的starter就必须自己指定版本号
 
2、整合redis
1)starter
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
     <groupId>org.springframework.data</groupId>
     <artifactId>spring-data-redis</artifactId>
     <version>2.1.9.RELEASE</version>
</dependency>
 
2)配置redis
- applicaiton.properties
 
spring.redis.host=127.0.0.1
spring.redis.port=6379
 
3)测试
@Resource
private StringRedisTemplate stringRedisTemplate;//或者RedisTemplate
  
@Test
public void t() {
    String name = "mjp";
    Integer age = 18;
    
    String key = stringRedisTemplate.opsForValue().get(name);
    if (StringUtils.isBlank(key)) {
        stringRedisTemplate.opsForValue().set(name, String.valueOf(age));
    }
}
 
- 去redis客户端工具中
 
redis的安装,参考我另一篇:redis安装
get name
 
一、WebMvcAutoConfiguration
在SpringBoot中主要作用于Web MVC的自动配置,包括视图解析器、静态资源配置、MVC配置、消息转换器和异常处理等。
1.1 Filter
1、背景
参考我设计模式:设计模式Java实战
2、使用
- Filter1
 
public class MyFilter1 implements Filter{
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 先拦截req
		System.out.println("======================filter-req");
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String uri = httpRequest.getRequestURI();
        if (StringUtils.isNoneBlank(uri)) {
            String[] split = uri.split("/");
            if (split.length > 2) {
                String firstParam = split[2]; // 第一个参数
                //return;//终止执行业务逻辑
            }
        }
        chain.doFilter(request, response);//执行目标方法
        // 再拦截resp
        System.out.println("======================filter1-resp");
    }
}
 
- Filter2
 
public class MyFilter2 implements Filter{
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("===========================filter2-req");
        chain.doFilter(request, response);
        System.out.println("===========================filter2-resp");
    }
}
 
- 注入Spring
 
@Configuration
public class WebConfig {
    @Bean
    public FilterRegistrationBean<MyFilter> myFilter1 (){
        FilterRegistrationBean<MyFilter> bean = new FilterRegistrationBean<>(new MyFilter());
        bean.setOrder(2);//指定生效顺序
        bean.setUrlPatterns(Sets.newHashSet("/query/*"));//指定拦截的请求路径
        return bean;
    }
    @Bean
    public FilterRegistrationBean<MyFilter2> myFilter2 (){
        FilterRegistrationBean<MyFilter2> bean = new FilterRegistrationBean<>(new MyFilter2());
        bean.setOrder(1);
        bean.setUrlPatterns(Sets.newHashSet("/query/*"));// 不指定,则默认"/*"
        return bean;
    }
}
 
3、补充
- 作用域是Tomcat容器级别的。所以默认对所有的Servlet进行Filter
 
1.2 Interceptor
1、作用域
MVC框架的一部分
2、自定义拦截器
@Component
public class MyHandlerInterceptor implements HandlerInterceptor {
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        // 为false会拦截请求,true会放行
        // 业务逻辑
        // eg:根据req内容查询,请求是否合法、用户是否存在等。如果不满足,则请求被拦截掉,return false
        System.out.println("=====================MyHandlerInterceptor-req符合,放行");
        return true;
    }
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                            @Nullable ModelAndView modelAndView) throws Exception {
        System.out.println("=====================MyHandlerInterceptor-resp");
    }
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
                                 @Nullable Exception ex) throws Exception {
        // 一定会执行,类似finally
        System.out.println("finally==============================");
    }
}
 
3、将自定义拦截器添加到拦截器链
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Resource
    private MyHandlerInterceptor myHandlerInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 添加自定义拦截器,并指定要拦截的路径
        registry.addInterceptor(myHandlerInterceptor)
//                .addPathPatterns("/**") // 拦截所有路径,或者指定具体的路径
                .addPathPatterns("/query/*") // 拦截所有路径,或者指定具体的路径
                .excludePathPatterns("/login"); // 排除某些路径
    }
}
 
4、Filter + HandlerInterceptor整体执行流程
=====================filter-req
=====================MyHandlerInterceptor-req符合,放行
=====do业务方法=======
=====================MyHandlerInterceptor-resp
=====================MyHandlerInterceptor-finally
=====================filter-resp
 
1)http请求 -->> Filter1#doFilter
- 过滤req1
 - chain.doFilter -->> ApplicationFilterChain#doFilter ,找到下一个Filter2,Filter2#doFilter
 - 过滤req2
 - chain.doFilter -->> ApplicationFilterChain#doFilter,找不到下一个Filter了,执行servlet.service
 
2)servlet.service
父->子关系 以及 各自方法
    
HttpServlet(3.service、4.doGet)
	FrameworkServlet(2.service、5.doGet、6.processRequest 、7.doService)
		DispatcherServlet(1.service 、8.doService、9.doDispatch)
 
servlet.service-- >> DispatcherServlet#service -->>父类FrameworkServlet#service -->> super.service即HttpServlet#service
-  
获取方法类型GET(或Post) -->>doGet(或doPost) -->> 子类FrameworkServlet重写#doGet
–>>processRequest -->> doService
–>> 子类DispatcherServlet#doService -->> doDispatch
 
3)doDispatch
// Determine handler for the current request.
// 1.找到我们的Controller
mappedHandler = getHandler(processedRequest);
// 2.适配
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 3.是否前置拦截applyPreHandle,ture则放行,false则拦截。不执行后续的业务方法
// 执行拦截器链的一系列applyPreHandle
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
	return;
}
// 4.执行业务方法,即本次请求的方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 5.后置方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
 
二、源码解析
2.1 SpringApplication
SpringApplication.run(ApplicationLoader.class, args);
 
内部代码等价
SpringApplication springApplication = new SpringApplication(ApplicationLoader.class);
springApplication.run(args);
 
SpringApplication类定义
执行以下步骤来启动Spring应用程序
- 创建ApplicationContext-Spring上下文
 - 刷新应用上下文,加载所有单例bean
 
2.1.1 构造方法
1、填充webApplicationType
属性:REACTIVE、NONE、SERVLET(这里是SERVLET)
2、自动装配Initializers
1)读取spring.factories内容
spring-boot-2.1.6.RELEASE.jar!/META-INF/spring.factories,封装为map
补充:springboot中总共有2个spring.factories文件,一个为上述路径,还有一个路径为:
spring-boot-autoconfigure-2.1.6.RELEASE.jar!\META-INF\spring.factories,这个和自动装配@Import(AutoConfigurationImportSelector.class)注解有关
2)根据key,获取val集合
- key:ApplicationContextInitializer
 - val:spring.factories中key是ApplicationContextInitializer类对应的val
 
3)将val集合List<String全类名>实例化
- 通过ClassForName实例化
 
4)将上述自动装配map,存cache,便于下一此直接从缓存中读取,避免再次加载
3、自动装配Listeners
过程同2,不过map根据key获取val,此map注解从cache中获取。
key为:ApplicationListener
2.1.2 run(args)
springApplication.run(args);
 
其中,可以定义args:在IDEA中设置方式如图3所示
 
这样在启动时,args就为"–debug"
作用:可以人为设置参数,比如"–debug",可以打印自动装配过程日志信息
2.2 SpringApplicationRunListeners
1、内容
1)从spring-boot-2.1.6.RELEASE.jar!/META-INF/spring.factories中
获取key:SpringApplicationRunListener
对应的val:org.springframework.boot.context.event.EventPublishingRunListener
这里可以直接从cache中读取,无需从spring.factories中加载了
2)创建SpringApplicationRunListeners
将val:EventPublishingRunListener作为属性,通过构造方法,填充给SpringApplicationRunListeners
2、启动监听
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
 
–>> EventPublishingRunListener#starting
this.initialMulticaster.multicastEvent(
	new ApplicationStartingEvent(this.application, this.args)
);
 
这里发布的第一个事件:1、ApplicationStartingEvent
Spring观察者模式内容参考我另一篇:Spring源码剖析-Spring观察者模式
-  
创建一个事件:应用程序启动事件
 -  
获取多播器initialMulticaster
- 内含监听器listener
 - 这些监听器就是SpringApplication构造方法中,自动装配Listeners
 - 然后执行supportsEvent过滤,留下能够监听ApplicationStartingEvent事件的监听器
 
 -  
进行广播事件:multicastEvent
- 循环获取监听器
 - 执行invokeListener(listener, event) -->> listener.onApplicationEvent(event)
 
 
多播器会循环调用监听器的处理方法,将应用程序启动此事件,进行了广播给监听器进行处理。
2.3 prepareEnvironment
方法内容:加载环境变量、配置文件等
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments) {
	//1.创建environment,加载propertySources
	ConfigurableEnvironment environment = getOrCreateEnvironment();
	
	//2.填充activeProfiles属性:spring.profiles.active
	configureEnvironment(environment, applicationArguments.getSourceArgs());
    
    // 3.读取properties、xml、yaml后缀文件
	listeners.environmentPrepared(environment);
    
	bindToSpringApplication(environment);
	if (!this.isCustomEnvironment) {
		environment = new EnvironmentConverter(getClassLoader())
		.convertEnvironmentIfNecessary(environment,deduceEnvironmentClass());
	}
	
	ConfigurationPropertySources.attach(environment);
	return environment;
}
 
加载propertySources
getOrCreateEnvironment
- 根据webApplicationType类型SERVLET,new StandardServletEnvironment()
 - 调用其爷爷的构造方法,执行customizePropertySources --> 孙子StandardServletEnvironment#customizePropertySources
 - 完成servletContextInitParams、servletConfigInitParams两个ProertySource的加载
 - 再调用父类customizePropertySources ,完成systemEnvironment和systemProperties两个PropertySource的加载
 
至此environment中propertySources属性中有4和资源配置
填充activeProfiles属性
configureEnvironment
主要内容就是,填充activeProfiles属性。
假如我们在IDEA的 在VM Options中:-Dspring.profiles.active = prod,则此时
environment中activeProfiles属性中有一个值"prod"
读取properties、xml、yaml后缀文件
listeners.environmentPrepared
–>> EventPublishingRunListener#environmentPrepared -->> multicastEvent
- 获取所有listener,并过滤监留下听ApplicationEnvironmentPreparedEvent事件的监听器
 
这里发布的第二个事件:2、ApplicationEnvironmentPreparedEvent
- 其中最重要的监听器ConfigFileApplicationListener
 
– >>invokeListener(listener, event) -->> doInvokeListener -->> listener.onApplicationEvent(event)–>>onApplicationEnvironmentPreparedEvent
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
    // 从spring.factories中找到key:EnvironmentPostProcessor对应的val集合
	List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
    // 将ConfigFileApplicationListener监听器本身也加入val集合
	postProcessors.add(this);
	AnnotationAwareOrderComparator.sort(postProcessors);
    // 遍历val集合,最重要的是:执行ConfigFileApplicationListener#postProcessEnvironment
	for (EnvironmentPostProcessor postProcessor : postProcessors) {
		postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
	}
}
 
–>> ConfigFileApplicationListener#postProcessEnvironment -->> addPropertySources
new Loader(environment, resourceLoader).load()
 
1、new Loader
this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(
	PropertySourceLoader.class,getClass().getClassLoader()
);
 
从spring.factories读取key:PropertySourceLoader对应的val集合
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
 
- PropertiesPropertySourceLoader:读取.properties文件 和 .xml文件
 - YamlPropertySourceLoader:读取.yaml文件 和 .yml文件
 
2、laod
–>> addLoadedPropertySources
将application.properties文件中内容加载到propertySources集合中(同理加载yml),至此propertySources中有五个元素
- servletContextInitParams
 - servletConfigInitParams
 - systemEnvironment
 - systemProperties
 - classpath:/application.properties
 
2.4 createApplicationContext
1、方法内容
- 根据webApplicationType类型获取对应的上下文class类:Class.forName(AnnotationConfigServletWebServerApplicationContext)
 - 通过反射ctor.newInstance(),创建上下文对象
 
2、构造函数
public AnnotationConfigServletWebServerApplicationContext() {
	this.reader = new AnnotatedBeanDefinitionReader(this);
	this.scanner = new ClassPathBeanDefinitionScanner(this);
}
 
创建DefaultListableBeanFactory
父子关系
GenericApplicationContext
	GenericWebApplicationContext
		ServletWebServerApplicationContext
			AnnotationConfigServletWebServerApplicationContext
 
- 执行AnnotationConfigServletWebServerApplicationContext构造方法之前,会先调用父类的构造方法,一层一层直到GenericApplicationContext
 
public GenericApplicationContext() {
    // 创建了beanFactory
	this.beanFactory = new DefaultListableBeanFactory();
}
 
AnnotationBeanDefinitionReader
加载5个internalXxxProcessor(bfpp)到bdMap中,如图1所示
 
ClassPathBeanDefinitionScanner
后续invokeBfpp时,会使用此Scanner,对配置类进行解析,生成对应的beanDefinition
2.5 prepareContext
方法内容:为context上下文对象填充属性
applyInitializers
- 获取模块2.1中Initializers,即从spring.factories中读取到的7个Initializer
 - 依次遍历执行他们的initialize
 - 完成对context上下文的属性填充
 
主要填充了context的BeanFactoryPostProcessors属性、applicationListeners属性
为beanFactory设置属性
allowBeanDefinitionOverriding = false
Spring 容器中有相同名称的多个 Bean 定义时,是否允许后续的 Bean 定义覆盖先前的定义
- false:如果尝试注册一个已经存在的 Bean 定义,将会抛出一个异常
 
创建启动类beanDefinition
// 获取primarySource,即启动类
Set<Object> sources = getAllSources();
// 将启动类抽取为BeanDefinition,存入bdMap
load(context, sources.toArray(new Object[0]));
 
listeners.contextLoaded
这里发布的第三个事件:3、ApplicationPreparedEvent
至此三个事件:
- ApplicationStartingEvent
 - ApplicationEnvironmentPreparedEvent
 - ApplicationPreparedEven
 
均被多播器SimpleApplicationEventMulticaster广播了出去,对此三个事件感兴趣的监听器,也完成了监听处理动作
2.6 refreshContext
对应Spring中的refresh方法。即IOC
此部分的源码剖析,参考我另一篇:Spring源码剖析
自动装配
具体流程参考我的另一篇:Spring源码剖析中第二章节IOC的法五中的@Import注解解析
这里只补充下,自动装配后的过滤逻辑:
getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata)
 
getAutoConfigurationMetadata
1、方法内容
从META-INF/spring-autoconfigure-metadata.properties路径下读取配置类的依赖类信息,封装成map
-  
文件内容如图15所示

 -  
封装的map即autoConfigurationMetadata(自动装配类.条件=值)
 
2、方法作用
为了后续将自动装配的类,进行Filter操作,只留下符合项目需要的自动装配类
1)autoConfigurationMetadata中内容
k1:org.springframework.boot.autoconfigure.aop.AopAutoConfiguration.ConditionalOnClass
v1:
org.aspectj.lang.annotation.Aspect,
org.aspectj.lang.reflect.Advice,
org.aspectj.weaver.AnnotatedElement,
org.springframework.context.annotation.EnableAspectJAutoProxy
 
2)我们看下AopAutoConfiguration此自动装配类的内容
@Configuration
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class, AnnotatedElement.class })
public class AopAutoConfiguration {
}
 
@ConditionalOnClass注解中属性:EnableAspectJAutoProxy、Aspect、Advice。
去重 + 排除 + @conditional
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
			AnnotationMetadata annotationMetadata) {
    
	AnnotationAttributes attributes = getAttributes(annotationMetadata);
    //1.自动装配-120个
	List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    //2.去重后,仍为120个
	configurations = removeDuplicates(configurations);
    
    // 3.排除后,119个
	Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    configurations.removeAll(exclusions);//排除
	// 4.自动装配类@conditional注解修饰后,还剩下
	configurations = filter(configurations, autoConfigurationMetadata);
    // 返回最终剩下的
	return new AutoConfigurationEntry(configurations, exclusions);
}
 
在自动装配完成后,key:,对应的val集合中有装配类 120个,如图4所示
 
1、去重-getExclusions
2、排除-getExclusions
// 这个要被排除的类的全路径org.springframework.boot.autoconfigure.jdbc.类名;
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class ApplicationLoader {
    public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication(ApplicationLoader.class);
        springApplication.run(args);
    }
}
 
-  
排除前:自动装配类,如图5所示

 -  
排除后:自动装配,没有此类了,剩下119个自动装配类,如图6所示

 
3、@conditional-filter
3.1 步骤一:从spring.factories中获取key:AutoConfigurationImportFilter对应的val集合:用于filter
g.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
 
三折共同的父类FilteringSpringBootCondition 实现了Condition接口,重写了match方法,用于匹配
3.2 使用这三个Filter,过滤候选的119个自动装配类
//依次遍历Filter
// 使用Filter,在autoConfigurationMetadata内容下,判断候选装配类在classpath路径下是否有指定的(Condition)的bean、class等
// 需要的,则留下,不需要的则skipped = true
for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
	invokeAwareMethods(filter);
	boolean[] match = filter.match(candidates, autoConfigurationMetadata);
	for (int i = 0; i < match.length; i++) {
		if (!match[i]) {
			skip[i] = true;
			candidates[i] = null;
			skipped = true;
		}
	}
}
 
filter.match(candidates, autoConfigurationMetadata)
1)candidates:就119个候选装配类
2)autoConfigurationMetadata,即map:自动装配的类.条件 = 值
根据下面的条件注解@ConditionalOnXxx和元数据autoConfigurationMetadata进行筛选,如果其条件满足(即所有条件注解和元数据条件都通过),则将其标记为匹配成功。
3.3这里以2个自动装配类为例,
-  
(这里参考2.1.2 run(args),配置–debug参数,便于观察自动装配filter日志信息)
 -  
@ConditionalOnXxx相关注解含义如图2所示:即系统中满足指定的条件,才会去实例化此对象
作用:配置类中某些地方会使用到Xxx.方法,这就要求Xxx必须存在,否则npe
 

其中
- 类org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration因符合条件被留下来
 
matched:
      - @ConditionalOnClass found required classes 'javax.sql.DataSource', 'org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType' (OnClassCondition)
 
- 类org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration因为不符合条件被过滤掉
 
Did not match:
    - @ConditionalOnClass did not find required class 	'org.springframework.kafka.core.KafkaTemplate' (OnClassCondition)
 
3.1)DataSourceAutoConfiguration
// 和@Conditional相关注解如下
@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
public class DataSourceAutoConfiguration {
    
}
 
-  
自动装配类DataSourceAutoConfiguration被@ConditionalOnClass注解修饰
 -  
此注解的作用:系统中有指定的类时,自动装配生效
 -  
获取@ConditionalOnClass注解内容:DataSource和EmbeddedDatabaseType
 -  
我pom文件中,引入了mysql-connector-java相关依赖,所以beanFactory中有DataSource类
 
3.2)KafkaAutoConfiguration
// 和@Conditional相关注解如下
@Configuration
@ConditionalOnClass(KafkaTemplate.class)
public class KafkaAutoConfiguration {
}
 
-  
自动装配类KafkaAutoConfiguration被@ConditionalOnClass注解修饰
 -  
此注解的作用:系统中有指定的类时,自动装配生效
 -  
我的pom文件中,未引入任何与kafak相关的依赖
 -  
所以filter.match(candidates, autoConfigurationMetadata) -->>
filter.match(KafkaAutoConfiguration, autoConfigurationMetadata)不符合条件,会过滤掉
 
自定义自动装配类
@Configuration
@ConditionalOnBean(UserController.class)
public class MyAutoConfig {
}
 
- @Configuration
 - @ConditionalXxx相关注解
 
MyAutoConfig matched:
      - @ConditionalOnBean (types: com.mjp.tx.UserController; SearchStrategy: all) found bean 'userController' (OnBeanCondition)
 
自定义的装配类被加载了
2.7 tomcat组件
因为Springboot内嵌了tomcat。这里前置先分析下tomcat的源码,为后续getWebServer方法剖析作铺垫
2.7.1定义
-  
tomcat是web服务器,本质是Servlet容器
 -  
Servlet容器是一个接口规则,定义了Java类被Tomcat服务器识别的规则
 
2.7.2 tomcat组件
整体组件架构图如图7所示
 
Server
1、定义
表示一个Tomcat实例
2、结构
包括多个Service服务
Service
1、定义
提供服务来处理请求的组件
2、结构
包括多个Connector和一个Container
Connector
1、定义
-  
是客户端连接到Tomcat容器的服务点 ,使得客户端可以通过Connector与服务器建立连接、发送请求并接收响应
 -  
每个连接器监控一个指定的IP及端口,并通过指定的协议做出响应
 
<Service name="Catalina">
    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443"/>
    
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443"/>
    ...
<Service/>
 
2、具体实现
Coyote
3、结构
如图8所示

3.1 Endpoint
1)定义
- 通信端点即通信监听的接口,是具体的Socket接收处理类
 
2)具体实现
- Tomcat并没Endpoint接口,而是一个抽象类AbstractEndpoint其默认实现类NioEndpoint
 
3)AbstractEndpoint(具体实现Nio2Endpoint)结构
- 属性Acceptor(默认实现):用于监听请求
 - 内部接口Handler(默认实现ConnectionHandler): 用于处理接收到的Socket,在内部调用Processor进行处理。
 
3.2 Processor
1)定义
- Processor是协议处理接口
 
2)具体实现类
Http11Processor
3)功能
- 将tcp/IP请求转为http1.1协议请求
 - 将http请求封装成request对象
 
3.3 Adapter
1)定义
适配器,request对象转换为ServletRequest交给Container进行处理
4、总结
http://127.0.0.1:8080/xxx
- Nio2Endpoint中Acceptor监听请求
 - ConnectionHandler处理接收到的Socket,交由Processor
 - Processor将tcp/ip协议转为http1.1请求,并封装request
 - Adapter将request换为ServletRequest,交由Container
 
Container
Container用于封装和管理Servlet,以及具体处理Request请求;
整体结构如图9所示
 
Container-Engine:
- host1:127.0.0.1 
  
- context1:/login 
    
- Wrapper
 
 - context2:/query 
    
- Wrapper
 
 
 - context1:/login 
    
 - host2:123.456.78.90
 
Engine
将传入请求委托给适当的虚拟主机处理
Host 虚拟主机
- 一个虚拟主机下都可以部署一个或者多个Web App,每个Web App对应于一个Context
 - 当Host获得一个请求时,将把该请求匹配到某个Context上
 
Context上下文
表示Web应用程序本身 ,即Servlet上下文
Wrapper包装器
- 代表一个 Servle。负责管理一个 Servlet的装载、初始化、执行以及资源回收 :如init()、service()和destroy()方法 。
 
总结
来自客户的请求为:http://localhost:8080/user/login 请求被发送到本机端口8080
1、Connection
- Nio2Endpoint中Acceptor监听请求
 - ConnectionHandler处理接收到的Socket,交由Processor
 - Processor将tcp/ip协议转为http1.1请求,并封装request
 - Adapter将request换为ServletRequest,交由Container
 
2、Container
Engine来处理,并等待Engine的回应
-  
Engine获得请求localhost:8080/user/login,匹配到名为localhost的Host
 -  
localhost Host获得请求/user/login,匹配它所拥有的所有Context
 -  
Context获得请求,寻找对应的servlet
匹配到HttpServlet类,构造HttpServletRequest对象和HttpServletResponse对象
 -  
调用HttpServlet的doGet或doPost方法
 
2.7.3 组件Lifecycle
1、内容
将所有组件的生命周期抽象为一个接口
2、生命周期 以及 结构
父子关系以及方法图
Lifecycle(全抽象方法init、start、stop、destroy、getState)
    LifecycleBase(init、抽象initInternal、start、抽象startInternal)
    	LifecycleMBeanBase(initInternal、destroyInternal)
			StandardServer(操作Server相关方法、initInternal、startInternal)
			StandardService(操作Container、Connector相关方法、initInternal、	startInternal、destroyInternal)
 
2.8 内嵌tomcat
onFeresh
2.8.1 getWebServerFactory
1、方法作用
根据ServletWebServerFactory.class找到对应的WebServerFactory:tomcatServletWebServerFactory
2、方法调用流程
onRefresh -->> createWebServer -->> getWebServerFactory -->> getBeanNamesForType -->> DefaultListableBeanFactory#getBeanNamesForType -->> doGetBeanNamesForType -->>
3、bdMap中为什么会存在tomcatServletWebServerFactory
1)SpringBoot自动装配,将ServletWebServerFactoryAutoConfiguration类从spring.factories文件中读取出来。
类结构如下
@Configuration
@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 {
	@Bean
	@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
	public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
			ServerProperties serverProperties) {
		return new TomcatServletWebServerFactoryCustomizer(serverProperties);
	}
}
 
内含TomcatServletWebServerFactoryCustomizer此bean
2)pom中引入了starter-web依赖
<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-web</artifactId>
     <version>3.0.6</version>
</dependency>
 
所以,在自动装配过滤的时候,不会将ServletWebServerFactoryAutoConfiguration此装配类过滤掉。
原因:
@ConditionalOnClass(ServletRequest.class):web服务
 @ConditionalOnWebApplication(type = Type.SERVLET):从2.1.1中可知,类型为SERVLET
4、getBean(“tomcatServletWebServerFactory”, ServletWebServerFactory.class)
创建tomcatServletWebServerFactory此bean对象
2.8.2 getWebServer
tomcat-所有组件init
public WebServer getWebServer(ServletContextInitializer... initializers) {
    // 1.创建tomcat
	Tomcat tomcat = new Tomcat();
	File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
	tomcat.setBaseDir(baseDir.getAbsolutePath());
    
    // 2.创建connector
	Connector connector = new Connector(this.protocol);//Http11NioProtocol
	tomcat.getService().addConnector(connector);
	customizeConnector(connector);
	tomcat.setConnector(connector);
    
    // 3.创建Engine、Host、Service
	tomcat.getHost().setAutoDeploy(false);
	configureEngine(tomcat.getEngine());
    
    // 4.创建Wrapper、Servlet
	prepareContext(tomcat.getHost(), initializers);
    
    // 5.创建TomcatWebServer:完成组件的init
	return getTomcatWebServer(tomcat);
}
 
1、tomcalt属性
- hostname:“localhost”
 - port:8080
 
2、connector属性
protocolHandler:Http11NioProtocol
- maxHttpHeaderSize:8192
 - endpoint 
  
- handler:ConnectionHandler 
    
- proto:Http11NioProtocol
 
 - acceptor:在最后2.9.1中完成创建
 - selectorPool 
    
- maxSelector:200
 
 - nioChannel:
 - maxConnection:10000
 - port:8080
 - maxThreads:200
 
 - handler:ConnectionHandler 
    
 - **adapter:**init时会创建CoyoteAdapter
 
3、Engine属性
-  
children:standardHost
- contextClass:standardContext
 - appBase:webApps
 
 -  
defaultHost:“localhost”
 -  
service:standardService
- connectors:Connector[HTTP/1.1-8080]
 - engineer:StandardEngine[Tomcat]
 
 -  
state:LifeCycleState:new
 
4、Wrapper属性
new StandardWrapper
- servletClass:org.apache.catalina.servlets.DefaultServlet
 
5、创建TomcatWebServer:完成组件的init初始化
new TomcatWebServer -->> initialize -->> this.tomcat.start() -->>
Tomcat#start -->> server.start(StandardServer)-- >> LifecycleBase#start
if (state.equals(LifecycleState.NEW)) {//显然Engine属性state是NEW
    // 步骤一:init
    init();
}
// 步骤二:启动
startInternal();
 
=======================步骤一:init开始-=------------------------------
LifecycleBase#init – initInternal -->> 孙子StandardServer#initInternal -->> services[i].init() -->>LifecycleBase#init
-  
engineer#init
 -  
connector#init
- adapter完成初始化
 
adapter = new CoyoteAdapter(this); protocolHandler.setAdapter(adapter);- protocolHandler#init
 - endpoint#init
 
 
=======================步骤一:init结束-=------------------------------
tomcat-engine-启动
=======================步骤二:启动-=------------------------------
父子关系以及方法图
Lifecycle(全抽象方法init、start、stop、destroy、getState)
    LifecycleBase(init、抽象initInternal、start、抽象startInternal)
    	LifecycleMBeanBase(initInternal、destroyInternal)
			StandardServer(操作Server相关方法、initInternal、startInternal)
			StandardService(操作Container、Connector相关方法、initInternal、	startInternal、destroyInternal)
 
LifecycleBase#startInternal–>>StandardServer#startInternal -->> services[i].start()
– >> LifecycleBase#start -->> startInternal -->> StandardService#startInternal
engine.start();//child.start,一系列Host、Context等start             
 
2.9 finishRefresh
2.9.1 startWebServer-NioEndpoint启动
webServer.start -->> TomcatWebServer#start -->> addPreviouslyRemovedConnectors -->>
service.addConnector -->> connector.start -->>LifecycleBase#start -->> startInternal -->>
Connector#startInternal -->>
AbstractProtocol#start -->>
AbstractEndpoint#start -->>
public final void start() throws Exception {
	// 步骤一.创建Socket
    if (bindState == BindState.UNBOUND) {
        bindWithCleanup();
        bindState = BindState.BOUND_ON_START;
    }
    // 步骤二.NioEndpoint#startInternal
    startInternal();
}
 
步骤一:bindWithCleanup
ServerSocketChannel serverSock = ServerSocketChannel.open();
serverSock.socket().bind(addr,getAcceptCount());//绑定8081端口,acceptCount = 100
// NioSelectorPool==》》BlockPoller.start阻塞轮询器启动
selectorPool.open(getName());
 
步骤二:NioEndpoint#startInternal
// 1.创建Executor
createExecutor()
// 2.创建Poller
poller = new Poller();
Thread pollerThread = new Thread(poller, getName() + "-ClientPoller");
pollerThread.setPriority(threadPriority);
pollerThread.setDaemon(true);
pollerThread.start();
// 3.创建Thread并start-Acceptor!!!
startAcceptorThread();
 
1、createExecutor
- keepAlive:60000000000ns即60s
 - workers:10个ThreadPoolExecutor
 - coolPoolSize:10
 - maximumPoolSize:200
 
2、创建poller
- selector
 - 同步队列
 
3、startAcceptorThread
protected void startAcceptorThread() {
    // 3.1 创建acceptor
    acceptor = new Acceptor<>(this);
    String threadName = getName() + "-Acceptor";
    acceptor.setThreadName(threadName);
    // 3.2 创建线程
    Thread t = new Thread(acceptor, threadName);
    t.setPriority(getAcceptorThreadPriority());
    t.setDaemon(getDaemon());
    // 3.3启动
    t.start();
}
 
-  
name:http-nio-8080-Acceptor
 -  
tid:
 -  
target:Acceprot
- endpoint:nioEndpoint
 
 -  
group
- name:“main”
 - nThreads:16
 
如图10所示
 

至此整个tomcat容器完成启动,控制台打印日志:Tomcat started on port(s): 8080 (http) with context path。SpringBoot启动完成
4、Thread.run:Acceptor监听
Acceptor会一直监听,随时准备accept请求
–>> target.run -->> Acceptor#run -->>
Acceptor implements Runnable{
}
 
run方法内容
public void run() {
    int errorDelay = 0;
    // 一直循环监听
    while (endpoint.isRunning()) {
    	// 1.当前状态设置为Running
        state = AcceptorState.RUNNING;
        try {
            //if we have reached max connections, wait
            // 2.当到达了最大连接,则等待
            endpoint.countUpOrAwaitConnection();
            // Endpoint might have been paused while waiting for latch
            // If that is the case, don't accept new connections
            if (endpoint.isPaused()) {
                continue;
            }
            U socket = null;
            try {
                // Accept the next incoming connection from the server
                // socket
                //!!!接收来自客户端的请求
                socket = endpoint.serverSocketAccept();
            } catch (Exception ioe) {
 
            }
            // Successful accept, reset the error delay
            errorDelay = 0;
            // 其它closeSocket、destroySocket
    }
}
 
– >> endpoint.serverSocketAccept -->> NioEndpoint#serverSocketAccept -->>accept -->> ServerSocketChannelImpl#accept -->> accept -->> accept0
private native int accept0() {
}
 
5.处理请求:Handler处理请求
1、客户端发起请求:http://localhost:8080/query/18
2、服务器端
Thread#run -->> target.run -->> TaskThread内部类WrappingRunnable#run–>>wrappedRunnable.run
–>> ThreadPoolExecutor#run -->> runWorker -->> task.run -->> SocketProcessorBase#run -->> doRun -->> NioEndpoint#doRun -->> getHandler().process -->> AbstractProtocol(继承ProtocolHandler)#process
// 创建Processor,new Http11Processor
Processor processor = getProtocol().createProcessor();
 
6、processor.process
– AbstractProcessorLight#process -->> service -->> Http11Processor#service
- 将tcp/IP请求转为http1.1协议请求
 - 将http请求封装成request对象
 
Request对象属性如图11所示

getAdapter().service(request, response)
- 获取CoyoteAdapter
 - 执行service
 
7、CoyoteAdapter.service
1、方法作用
适配器,request对象转换为ServletRequest交给Container进行处理
2、内容
connector.getService().getContainer().getPipeline().getFirst().invoke(
                        request, response);
 
- getContainer获取container即StandardEngineer
 
– >> StandardEngineValve#invoke
8、Container-Engineer
StandardEngineer -->> StandardEngineValve#invoke -->>
host.getPipeline().getFirst().invoke(request, response);
 
9、Container-Host
StandardHost -->> ErrorReportValve#invoke -->> StandardHostValve#invoke
context.getPipeline().getFirst().invoke(request, response);
 
10、Container-Context
–>> AuthenticatorBase#invoke -->> getNext().invoke(request, response) -->>StandardContextValve#invoke
wrapper.getPipeline().getFirst().invoke(request, response);
 
pipleLine结构图如图12所示
 
11、Container-Wrapper
–>> StandardWrapperValve#invoke -->>
- 属性requestCount,用于记录请求次数
 
// 步骤一.获取Servlet
Servlet servlet = wrapper.allocate();
 
DispatcherServlet属性如图13所示
 
//步骤二:为本次请求,创建Filter过滤器链
ApplicationFilterChain filterChain =
               ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
 
- filterChain = new ApplicationFilterChain()
 - filterChain .add(系统自带的 和 用户定义的)
 
// 步骤三:执行Filter过滤器链
filterChain.doFilter
          (request.getRequest(), response.getResponse());
 
过滤器链如图14所示
 
其中MyFilter和MyFilter2是模块一中1.1 我们自定义的Filter
12、后续内容
–>> XxxFilter#doFilter
上接模块一中1.1 Filer,具体执行流程可参考1.2 Interceptor中执行流程
2.9.2 publishEvent
广播第四个事件:ServletWebServerInitializedEvent。ServletWeb容器已经完成实例化了,至此四个事件都已广播出去,并被相关监听器监听处理
2.10 stop-tomcat
private void refreshContext(ConfigurableApplicationContext context) {
	//1.spring的refresh方法
	refresh(context);
	//2.注册钩子函数
	context.registerShutdownHook();	
	}
 
上文执行完成整个Spring的refresh方法后,会注册一个钩子函数
1、钩子函数
refreshContext -->> AbstractApplicationContext#registerShutdownHook
public void registerShutdownHook() {
		if (this.shutdownHook == null) {
			this.shutdownHook = new Thread() {
				@Override
				public void run() {
					synchronized (startupShutdownMonitor) {
						doClose();
					}
				}
			};
			Runtime.getRuntime().addShutdownHook(this.shutdownHook);
		}
	}
 
1、将shutdownHook此线程注入hooks
2、当上下文AbstractApplicationContext关闭时,则会
执行ApplicationShutdownHooks#静态代码块
class ApplicationShutdownHooks {
    private static IdentityHashMap<Thread, Thread> hooks;
    
    static {
        try {
            Shutdown.add(1 /* shutdown hook invocation order */,
                false /* not registered if shutdown in progress */,
                new Runnable() {
                    public void run() {
                        runHooks();
                    }
                }
            );
            hooks = new IdentityHashMap<>();
        }
     }
}
 
3、runHooks
for (Thread hook : threads) {
    hook.start();
}
 
遍历所有的钩子线程,执行start。此时我们AbstractApplicationContext中的shutdownHook线程就会执行run
–>> doClose
// Destroy all cached singletons in the context's BeanFactory.
destroyBeans();
// Close the state of this context itself.
closeBeanFactory();
// Let subclasses do some final clean-up if they wish...
// stop-tomcat
onClose();
 
onClose -->>ServletWebServerApplicationContext#onClose -->> stopAndReleaseWebServer -->> webServer.stop -->> TomcatWebServer#stop
stopTomcat();//停止
this.tomcat.destroy();//销毁
 
注解
@SpringBootApplication
使用@SpringBootApplication注解后,就无需使用@ComponentScan注解。因为@SpringBootApplication默认会扫描同级别下的所有包
如果想单独指定扫描路径,则可以使用@ComponentScan


















