文章目录
- 依赖管理
- 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构造方法中,