文章目录
- 前言
- 正文
-
- 一、设计原理
-
- [1.1 servlet生命周期简述](#1.1 servlet生命周期简述)
- [1.2 设计原理小结](#1.2 设计原理小结)
- 二、启动原理
-
- [2.1 AbstractHandlerMethodMapping 初始化 --RequestMapping注解解析](#2.1 AbstractHandlerMethodMapping 初始化 --RequestMapping注解解析)
- [2.2 DispatcherServlet 的初始化](#2.2 DispatcherServlet 的初始化)
- [2.3 DispatcherServlet#initHandlerMappings(...) 初始化示例说明](#initHandlerMappings(...) 初始化示例说明)
- 三、工作原理
前言
本系列文章基本环境如下:
- java8
- springboot2.7
创建项目,使用阿里的源:https://start.aliyun.com
创建前,请先设置好自己的maven环境,java版本。保持网络正常。
选择spring-web的2.7.6 版本:
使用阿里的源创建好项目后,会自动生成的有控制器Controller,启动类,以及一个页面。
如此,准备工作就做好了。
正文
在Java还没有SpringMvc时,使用的是servlet + jsp 的方式,对外提供接口,以及和页面进行数据交互等操作。
但是,这种操作,毕竟还是不方便,功能也不够强大。
曾经的写法,需要配置xml文件,如果页面够多,光配置就是一大堆。
发展到后来,servlet3.0的时候,出现了完全注解版的写法。
关于servlet的描述这里不做过多解释,本文将对对springmvc中使用到的servlet特征进行阐述,继而分析它的设计原理,springmvc启动原理,以及工作原理
等到在Spring框架中的时候,就已经是DispatcherServlet了。
而它本身就是一个servlet,其类关系图如下:
一、设计原理
1.1 servlet生命周期简述
Servlet生命周期分为三个阶段:
- 初始化阶段:调用
init()
方法实现初始化工作。 - 运行阶段:处理请求,容器创建代表HTTP请求的
ServletRequest
对象和代表HTTP响应的ServletResponse
对象,并将它们作为参数传递给Servlet
的service()
方法。 - 销毁阶段:Servlet将被销毁,生命周期结束。
Servlet
本身只是一个接口,在HttpServlet
实现类中,对service()
方法进行了实现。
而这里的实现是套用了模版方法设计模式,将service的职责拆分了,按照请求方法的类型不同划分。
比如,如果请求方法是 GET请求,则会执行到 HttpServlet
的 doGet
方法;如果是POST请求,则会执行到 HttpServlet
的 doPost
方法。
FrameworkServlet
又对HttpServlet
中的service
方法进行了重写:
java
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
if (httpMethod != HttpMethod.PATCH && httpMethod != null) {
super.service(request, response);
} else {
this.processRequest(request, response);
}
}
这里判断了请求方法,默认执行 HttpServlet
的 service
方法。
但是实际调用的是 doGet
,doPost
这类方法。而同时,这类方法也被FrameworkServlet
重写了
也就是说,servlet 会触发执行到 FrameworkServlet
的 processRequest
方法。如下图所示:
这里会执行FrameworkServlet
的 doService
方法。而这是一个抽象方法。其子类DispatcherServlet 对其进行了实现。如此便贯通了。
1.2 设计原理小结
在1.1小节中的分析中,Servlet继承&实现的关系如下:
也就是说,在servlet处理请求时,对于springmvc而言,就是执行 doService方法。
二、启动原理
这一小节,主要分析SpringBoot项目启动时,对SpringMvc部分的处理。
2.1 AbstractHandlerMethodMapping 初始化 --RequestMapping注解解析
谈起SpringMvc,最先想起来的俩注解应该是 Controller
和 RequestMapping
。
而关于启动项目时,框架对这俩注解的处理,基本都体现在AbstractHandlerMethodMapping
中。
java
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
// ...省略其他方法
public void afterPropertiesSet() {
this.initHandlerMethods();
}
}
首先,AbstractHandlerMethodMapping
是个抽象类,它的子类会放到Spring容器中。
而在它的子类 RequestMappingHandlerMapping
中,对初始化方法进行了重写,具体内容如下:
java
public void afterPropertiesSet() {
this.config = new RequestMappingInfo.BuilderConfiguration();
this.config.setTrailingSlashMatch(this.useTrailingSlashMatch());
this.config.setContentNegotiationManager(this.getContentNegotiationManager());
if (this.getPatternParser() != null) {
this.config.setPatternParser(this.getPatternParser());
Assert.isTrue(!this.useSuffixPatternMatch && !this.useRegisteredSuffixPatternMatch, "Suffix pattern matching not supported with PathPatternParser.");
} else {
this.config.setSuffixPatternMatch(this.useSuffixPatternMatch());
this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch());
this.config.setPathMatcher(this.getPathMatcher());
}
super.afterPropertiesSet();
}
在设置了一堆配置之后,最终调用的还是 AbstractHandlerMethodMapping
的initHandlerMethods
方法。
java
protected void initHandlerMethods() {
// 获取spring容器中的beanName
String[] var1 = this.getCandidateBeanNames();
int var2 = var1.length;
for(int var3 = 0; var3 < var2; ++var3) {
String beanName = var1[var3];
if (!beanName.startsWith("scopedTarget.")) {
// 通过beanName映射出methodHandler
this.processCandidateBean(beanName);
}
}
// 初始化handlerMethods
this.handlerMethodsInitialized(this.getHandlerMethods());
}
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
// 通过beanName获取当前的类型
beanType = this.obtainApplicationContext().getType(beanName);
} catch (Throwable var4) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Could not resolve type for bean '" + beanName + "'", var4);
}
}
// 当前beanName对应的类定义不为空,并且带有Controller 或 RequestMapping注解时,对其进行处理
if (beanType != null && this.isHandler(beanType)) {
this.detectHandlerMethods(beanName);
}
}
可以看到,最终处理控制器时,是调用了detectHandlerMethods
方法。具体内容如下:
java
protected void detectHandlerMethods(Object handler) {
// 通过beanName获取到对应的类型
Class<?> handlerType = handler instanceof String ? this.obtainApplicationContext().getType((String)handler) : handler.getClass();
if (handlerType != null) {
// 获取你自己定义的控制器类型
Class<?> userType = ClassUtils.getUserClass(handlerType);
// 将类中符合条件(标注了RequestMapping注解)的 method 映射为 RequestMappingInfo 对象,并放入map中;这一步的实现,在其子类中。并且聚合它们的请求路径。
Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (method) -> {
try {
return this.getMappingForMethod(method, userType);
} catch (Throwable var4) {
throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, var4);
}
});
// 记录日志
if (this.logger.isTraceEnabled()) {
this.logger.trace(this.formatMappings(userType, methods));
} else if (this.mappingsLogger.isDebugEnabled()) {
this.mappingsLogger.debug(this.formatMappings(userType, methods));
}
// 方法注册
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
this.registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
这里对方法的处理分了两步,第一步,根据类型简单处理注解,主要是聚合了请求路径。聚合后的结果如下:
可以看到,这里的路径属性已经有值了。
然后就是注册方法了。
而真正注册的方法registerHandlerMethod
是在其子类中实现的。具体内容如下:
在其父级的实现中,注册的结果如下:
最后,简单处理RequestBody
注解(如果使用了该注解,参数必填)。
2.2 DispatcherServlet 的初始化
框架中定义了自动配置类 DispatcherServletAutoConfiguration
。其有个内部类 DispatcherServletConfiguration 对 DispatcherServlet 进行了配置。具体如下:
java
@Conditional({DefaultDispatcherServletCondition.class})
@ConditionalOnClass({ServletRegistration.class})
@EnableConfigurationProperties({WebMvcProperties.class})
protected static class DispatcherServletConfiguration {
protected DispatcherServletConfiguration() {
}
@Bean(
name = {"dispatcherServlet"}
)
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 = {"multipartResolver"}
)
public MultipartResolver multipartResolver(MultipartResolver resolver) {
return resolver;
}
}
在第一节设计原理的时候,提到过DispatcherServlet
也是一个servlet 。那么它的初始化,也就包含在servlet的生命周期中。
在servlet生命周期中,有 init
方法,进行初始化。FrameworkServlet
有一个父类HttpServletBean
,其中定义了初始化方法。虽然不是原生的servlet 初始化方法,但是也是会间接调用到的(通过模版方法设计模式,由子类代为实现)
java
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware
在FrameworkServlet
触发初始化时,会执行到 initServletBean
方法。其中有两个比较关键初始化方法,具体如下:
篇幅原因,这里只拿出关键代码。initWebApplicationContext()
内,有这样一段,在处理请求时,会执行到:
java
if (!this.refreshEventReceived) {
synchronized(this.onRefreshMonitor) {
this.onRefresh(wac);
}
}
而 onRefresh
由子类DispatcherServlet
重写后,就成了这样:
java
protected void onRefresh(ApplicationContext context) {
this.initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
// 初始化上传文件解析器
initMultipartResolver(context);
// 初始化本地解析器
initLocaleResolver(context);
// 主题处理器
initThemeResolver(context);
// 映射处理器
initHandlerMappings(context);
// 处理适配器
initHandlerAdapters(context);
// 异常处理器
initHandlerExceptionResolvers(context);
// 请求到视图名的翻译器
initRequestToViewNameTranslator(context);
// 视图解析器
initViewResolvers(context);
// 初始化FlashManager
initFlashMapManager(context);
}
如果大家想看看这里执行的内容,以及初始化后的结果,可以自行打断点查看。这里因为东西较多,我就不截图了。
PS: 下一小节以 HandlerMappings 为例,进行说明
2.3 DispatcherServlet#initHandlerMappings(...) 初始化示例说明
java
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// 获取所有的handlerMapping
Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
// 对handlerMapping进行排序
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
} else {
try {
// 获取名字是 handlerMapping 的handlerMapping
HandlerMapping hm = (HandlerMapping)context.getBean("handlerMapping", HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
} catch (NoSuchBeanDefinitionException var4) {
}
}
// handlerMappings为空,需要设置默认的handlerMapping
if (this.handlerMappings == null) {
this.handlerMappings = this.getDefaultStrategies(context, HandlerMapping.class);
if (this.logger.isTraceEnabled()) {
this.logger.trace("No HandlerMappings declared for servlet '" + this.getServletName() + "': using default strategies from DispatcherServlet.properties");
}
}
Iterator var6 = this.handlerMappings.iterator();
while(var6.hasNext()) {
HandlerMapping mapping = (HandlerMapping)var6.next();
if (mapping.usesPathPatterns()) {
this.parseRequestPath = true;
break;
}
}
}
默认情况下,如果配置了使用全部,会有以下handlerMapping:
另外补充一句,如果使用默认的handlerMapping,需要配置DispatcherServlet.properties
。
三、工作原理
这一小节,以DispatcherServlet 为起点,分析SpringMvc的工作原理。
// todo 待完善--预计2024春节后补充,春节期间玩去了。