SpringMVC源码分析 DispatcherServlet源码分析
@[toc]
前言
本篇博客是对SpringMVC中的DispatcherServlet分析,若文章中出现相关问题,请指出!
所有博客文件目录索引:博客目录索引(持续更新)
一、服务器启动过程中的操作
1、AbstractHandlerMethodMapping注册url和HandlerMethod(处理url与执行方法对应关系)
环境Spirng、SpringMVC
执行时间:当启动tomcat服务器的过程中(接收请求前),当bean被注入到容器后会执行一系列的初始化过程。
这里探讨的是url
与handlerMethod
对应关系存储的过程,所有的映射关系存储在AbstractHandlerMethodMapping
类的内部类MappingRegistry
里的registry
属性中(是一个HashMap
)。
这个过程是在AbstractHandlerMethodMapping
抽象类中完成初始化的:
我准备了一个@Controller
,并且包含@RequestMapping
注解包含url请求地址:
①:在bean注入后会执行初始化方法
//在初始化时执行检测方法
@Override
public void afterPropertiesSet() {
initHandlerMethods(); //初始化执行器方法
}
②:在①中调用初始化的方法
protected void initHandlerMethods() {
for (String beanName : getCandidateBeanNames()) { //getCandidateBeanNames()是获取到所有注册的bean名称即id名
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
//看<1>,来进行执行处理对应的bean(目的就是自定义控制器类中包含url地址的类)
processCandidateBean(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
// <1> 即处理对应的bean操作,参数为上面遍历的bean的id名称
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
//获取指定id名称的bean实例即Class类
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
if (logger.isTraceEnabled()) {
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
}
}
//判断该bean类是否符合类型,其中 isHandler(beanType)见 <2>
if (beanType != null && isHandler(beanType)) {
//见 <3> :一旦符合我们的要求就对这个类进行处理(来对去处理该类中的方法)
detectHandlerMethods(beanName);
}
}
// <2> 判断是否是我们要找的处理器,及是否包含注解
@Override
protected boolean isHandler(Class<?> beanType) {
//看到这里就很明白了,可以猜测上面的循环就是遍历找出含有@Controller、@RequestMapping的注解类,之后来进行获取注解中的url
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
// <3> 找到了执行器,那么就将该执行器中的所有方法来进行遍历取到映射地址(url以及对应handlerMethod绑定)
protected void detectHandlerMethods(Object handler) {
//获取到执行器类
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
Class<?> userType = ClassUtils.getUserClass(handlerType);
//将方法与映射地址存储到map集合中去,这一步就做完了操作
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
});
if (logger.isTraceEnabled()) {
logger.trace(formatMappings(userType, methods));
}
else if (mappingsLogger.isDebugEnabled()) {
mappingsLogger.debug(formatMappings(userType, methods));
}
//遍历map集合中的映射键值对(执行方法与url地址)
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
//关键!!!将执行器方法与url进行注册(也就是存储到AbstractHandlerMethodMapping.MappingRegistry.registry这个HashMap集合中来)
//见3中的代码解析
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
③这些Method类以及url地址应该统一进行保存,当请求来临时就会从一个HashMap中根据url去拿到这个方法类。前面也看到了显示遍历bean容器中的bean,接着筛选真正的执行器并对其中的方法来进行遍历拿到注解中的url地址存储到methods中。
最终是存储到AbstractHandlerMethodMapping.MappingRegistry.registry
这个HashMap
集合中去的,通过调用registerHandlerMethod()
方法来进行统一存储的。
- 这个handler实际上就是存储着bean的id以及hash值
//注册执行器方法
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
// 见<1>
this.mappingRegistry.register(mapping, handler, method);
}
//<1>,进行再处理
public void register(T mapping, Object handler, Method method) {
this.readWriteLock.writeLock().lock();
try {
//拿着handler以及method类创建HandlerMethod实例(其中包含了该方法的bean的相关内容)
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
validateMethodMapping(handlerMethod, mapping);
Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
for (String path : directPaths) {
this.pathLookup.add(path, mapping);
}
String name = null;
if (getNamingStrategy() != null) {
name = getNamingStrategy().getName(handlerMethod, mapping);
addMappingName(name, handlerMethod);
}
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
corsConfig.validateAllowCredentials();
this.corsLookup.put(handlerMethod, corsConfig);
}
//重点:真正放入到registry这个hashmap集合中的是映射地址以及包含了指定方法的一系列信息
this.registry.put(mapping,
new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
HandlerMethod
对象中包含了bean的相关内容,为后面反射做铺垫:
当①中的执行完之后注册url以及handlerMethod的过程就结束了!
总结:在bean
容器装配好之后,会进行url以及执行器的装配工作,这些工作都是在AbstractHandlerMethodMapping
这个抽象类中完成, afterPropertiesSet()
作为入口,简单来说就是遍历在IOC容器中注册的所有bean,筛选出具有@Controller
与@RequestMapping
注解的类,筛选过后会对指定类的方法依次去遍历搜集并对url以及method进行配对,最终统一存储到AbstractHandlerMethodMapping
抽象类中的一个内部类里的register属性(hashmap集合)中,之后方便请求来临时,根据url来去拿到指定的方法!
二、请求来临时
1、为什么DispatcherServlet的doService()方法是入口?
首先明确一点DispatcherServlet
它是一个Servlet
,在web.xml
被读取时创建的Servlet
,看下我们初始阶段进行配置的内容
当项目运行前DispatcherServlet
会在被读取web.xml时进行实例化,也就是说该servlet已经被注册到tomcat的容器里了。
①证明
DispatcherServlet
是Servlet。
这里进行一下说明为什么DispatcherServlet
本质就是一个Servlet呢?
下面是DispatcherServlet
的继承图:
如何证明?注意DispatcherServlet
的父类HttpServletBean
继承了HttpServlet类(该类是servlet-api这个jar包中拥有的,也就是tomcat中使用的servlet的jar包)
既然其是一个Servlet
并且加载web.xml
阶段就会被tomcat
中的servlet
容器所管理!
②发送请求触发
DispatcherServlet
之前可以看到web.xml中配置DispatcherServlet
的映射地址为/,也就是所有的请求。
这里介绍一下Servlet的生命周期:初始化、init()、service()、destory()。
DispatcherServlet
中也重写了其中的方法。
HttpServletBean
中重写了init()方法。FrameworkServlet
中重写了Service()方法。关键点
这和我发送一个请求过来触发DispatcherServlet
有没有什么关系呢?发送一个请求过来首先会解析请求路径,接着就会去找对应路径的servlet,接着去执行servlet
中的Service()
方法。
FrameworkServlet类部分:
在FrameworkServlet
中的Servcie()
方法中,若是一般的get或post请求会走下面黄色框的service方法,该方法调用的就是HttpServlet
的Service()
方法(也就是原生的Servlet中的方法):
执行HttpServlet
的Service()
方法中,会根据其中的方法来进行调用指定的doGet()
或doPost()
方法,很凑巧FrameworkServlet
中重写了多个doXXX()
方法,那么就会走这些重写的方法:其实重写后的方法调用的都是processRequest()
方法
该方法同样属于FrameworkServlet
类中的方法,把注意力放在其中的doService()
方法,重头来了
DispatcherServlet类部分:
实际上执行该方法时就会进入到DispatcherServlet
方法中的doService()
方法:
OK,一下子完整舒服了,绕了一大圈依旧是从service()方法中进来最后进入到doService()中去,之后我们再慢慢分析在doService()方法中到底做了些什么事情?
一切开始从中央处理器的doServcie()开始:
2、认识doDispatch
doDispatch
:执行请求的分发
-
①获取请求对应的
HandlerExecutionChain
处理器链。 -
②根据这个处理器得到指定的
Handler
对象。根据请求路径来配对对应的方法,将这个方法信息进行储存。 -
③前置处理拦截器。
-
④真正的调用handler方法,并返回视图。
-
包含
HandlerExecutionChain
(处理器链)
doDispatch()
是在doService()方法中执行的:主要用于请求的分发
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
//1、获得请求对应的 HandlerExecutionChain 对象(即执行器处理链,包含一个执行器与拦截器)
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
//2、获得当前 handler 对应的 HandlerAdapter 对象
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//3、前置处理拦截器
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//4、真正的调用handler方法,执行其中的业务操作,以及获取到modelAndView对象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//5、添加视图
applyDefaultViewName(processedRequest, mv);
//6、后置处理拦截器
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
//处理正常和异常的请求调用结果
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
//已完成 拦截器
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
//已完成 拦截器
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
- 点赞
- 收藏
- 关注作者
评论(0)