前言
本系列文章基本环境如下:
- 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
方法进行了重写:
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
中。
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
// ...省略其他方法
public void afterPropertiesSet() {
this.initHandlerMethods();
}
}
首先,AbstractHandlerMethodMapping
是个抽象类,它的子类会放到Spring容器中。
而在它的子类 RequestMappingHandlerMapping
中,对初始化方法进行了重写,具体内容如下:
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
方法。
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
方法。具体内容如下:
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 进行了配置。具体如下:
@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 初始化方法,但是也是会间接调用到的(通过模版方法设计模式,由子类代为实现)
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware
在FrameworkServlet
触发初始化时,会执行到 initServletBean
方法。其中有两个比较关键初始化方法,具体如下:
篇幅原因,这里只拿出关键代码。initWebApplicationContext()
内,有这样一段,在处理请求时,会执行到:
if (!this.refreshEventReceived) {
synchronized(this.onRefreshMonitor) {
this.onRefresh(wac);
}
}
而 onRefresh
由子类DispatcherServlet
重写后,就成了这样:
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(…) 初始化示例说明
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的工作原理。
3.1 DispatcherServlet处理请求流程图
官方的示意图如下:
个人理解:
3.2 请求分发
依据模版方法设计模式,在类FrameworkServlet
中,重写了 doPost
、doGet
等方法。
其内部调用了processRequest 方法,这是一个 final 的方法,内部主要调用了 doService(request, response) 方法。其在FrameworkServlet
中的定义为:
protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
throws Exception;
doService
请求分发方法由 DispatcherServlet
实现。也就是本文要说明核心的处理方法。而 doService
的核心处理是 doDispatch(request, response)
。
3.3 获取HandlerMapping
DispatcherServlet#getHandler(...)
方法,获取HandlerMapping
并生成 HandlerExecutionChain
实例。
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 当前初始化的handlerMappings不为空,没有特殊配置的情况下会有以下5个实例:
// RequestMappingHandlerMapping、WelcomePageHandlerMapping、BeanNameHandlerMapping、RouterFunctionMapping、ResourceHandlerMapping
if (this.handlerMappings != null) {
Iterator var2 = this.handlerMappings.iterator();
while(var2.hasNext()) {
// 获取RequestMappingHandlerMapping 实例
HandlerMapping mapping = (HandlerMapping)var2.next();
// 获取执行器链,内部依据request的路径,获取HandlerMethod实例,最终获得执行器链HandlerExecutionChain 实例
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
获取执行器链的具体方法是:AbstractHandlerMapping#getHandler(request)
。这里不做过多分析。感兴趣的可以自行查看。
3.4 获取处理器适配器
DispatcherServlet
中的 getHandlerAdapter(...)
用于获取处理器适配器。
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
// 获取当前的所有适配器
if (this.handlerAdapters != null) {
Iterator var2 = this.handlerAdapters.iterator();
while(var2.hasNext()) {
HandlerAdapter adapter = (HandlerAdapter)var2.next();
// 适配器支持当前的HandlerMethod
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
handlerAdapters
有4个值:RequestMappingHandlerAdapter
、HandlerFunctionAdapter
、HttpRequestHandlerAdapter
、SimpleControllerHandlerAdapter
。
3.5 拦截器预处理请求
执行器链HandlerExecutionChain
执行 applyPreHandle
。具体如下:
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex = i++) {
HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
// 拦截器预处理
if (!interceptor.preHandle(request, response, this.handler)) {
this.triggerAfterCompletion(request, response, (Exception)null);
return false;
}
}
return true;
}
3.6 处理器适配器执行handl(…)
这里调用的是 HandlerAdapter
的 handle
方法。AbstractHandlerMethodAdapter 重写了该方法,具体如下:
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return this.handleInternal(request, response, (HandlerMethod)handler);
}
@Nullable
protected abstract ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception;
以RequestMappingHandlerAdapter
为例,它实现了handleInternal
,如下:
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
this.checkRequest(request);
ModelAndView mav;
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized(mutex) {
mav = this.invokeHandlerMethod(request, response, handlerMethod);
}
} else {
mav = this.invokeHandlerMethod(request, response, handlerMethod);
}
} else {
mav = this.invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader("Cache-Control")) {
if (this.getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
this.applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
} else {
this.prepareResponse(response);
}
}
return mav;
}
3.7 执行器链执行拦截器
HandlerExecutionChain
执行applyPostHandle
。
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
for(int i = this.interceptorList.size() - 1; i >= 0; --i) {
HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
interceptor.postHandle(request, response, this.handler, mv);
}
}
3.8 执行 render()渲染视图
这个方法是 doDispatch
核心代码的最后一环。
执行,DispatcherServlet
定义的私有方法processDispatchResult
,触发 render
方法。
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
this.logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException)exception).getModelAndView();
} else {
Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;
mv = this.processHandlerException(request, response, handler, exception);
errorView = mv != null;
}
}
if (mv != null && !mv.wasCleared()) {
// 渲染视图
this.render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
} else if (this.logger.isTraceEnabled()) {
this.logger.trace("No view rendering, null ModelAndView returned.");
}
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, (Exception)null);
}
}
}
3.9 工作原理小结
这里做一个DispatcherServlet
中的 doDIspatch
方法的执行流程小结。