SpringMVC是基于MVC的架构模式,MVC是有Model(模型),V(视图),C(控制器)三部分组成的。C负责接收客户端请求,对请求进行处理然后转发到模型进行业务逻辑处理最后转发到相应的V进行显示处理。
SpringMVC执行过程
- 客户端发送请求到服务端,前端控制器(DispatcherServlet)接收请求,通过处理器映射器HandlerMapping找到处理器执行链HandlerExecutorChain(拦截器就在这)
- 通过处理器适配器HandlerAdapter找到合适的调用处理器的适配器
- 通过处理器适配器调用处理器方法,也就是我们的Controller,Controller返回ModelAndView
- 前端控制器接收到处理器适配器返回的ModelAndView通过视图解析器ViewResolver进行视图解析
- 前端控制器将ModelAndView中的Model渲染到View中返回给客户端进行响应
上面有几个重要的概念,
前端控制器(DispatcherServlet):接收请求,处理响应结果
处理器映射器(HandlerMapping):根据请求URL,找到对应的Handler
处理器适配器(HandlerAdapter):调用处理器
Handler类别 对应适配器 描述 Controller SimpleControllerHandlerAdapter 标准控制器,返回ModelAndView HttpRequestHandler HttpRequestHandlerAdapter 业务自行处理 请求,不需要通过modelAndView转到视图 Servlet SimpleServletHandlerAdapter 基于标准的servlet 处理 HandlerMethod RequestMappingHandlerAdapter 基于@requestMapping对应方法处理 视图解析器(ViewResolver):视图解析,把逻辑视图名称解析成真正的物理视图
Demo
这里我们不使用传统的xml形式配置,我们使用注解形式的配置
添加初始化类
public class MyStarterInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* IOC父容器的启动类
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{RootConfig.class};
}
/**
* IOC子容器配置 web容器配置
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebAppConfig.class};
}
/**
* 前端控制器DispatcherServlet的拦截路径
*/
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
RootConfig
/**
* IOC容器,不扫描Controller的注解
*/
@Configuration
@ComponentScan(basePackages = "com.dm"
, excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,value={RestController.class,Controller.class}),
@ComponentScan.Filter(type = ASSIGNABLE_TYPE,value = com.dm.config.WebAppConfig.class ),
})
public class RootConfig {
}
WebAppConfig
@Configuration
@ComponentScan(basePackages = {"com.dm"},includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,value = {RestController.class, Controller.class})
},useDefaultFilters =false)
@EnableWebMvc
public class WebAppConfig implements WebMvcConfigurer{
/**
* 配置试图解析器
*/
@Bean
public InternalResourceViewResolver internalResourceViewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setSuffix(".jsp");
viewResolver.setPrefix("/WEB-INF/jsp/");
return viewResolver;
}
/**
* JSON
*/
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new MappingJackson2HttpMessageConverter());
}
}
测试controller
@Controller
public class DemoController {
@RequestMapping("/hello")
public ModelAndView index(ModelAndView modelAndView) {
modelAndView.addObject("name","dm");
modelAndView.setViewName("hello");
return modelAndView;
}
}
根目录下webapp下新建WEB-INF/jsp/hello.jsp,启动即可。
SpringMVC容器启动源码分析
我们这里没有使用xml形式,是如何启动的呢,还有按照我们上面SpringMVC流程分析前端控制器DispatcherServlet在哪?
首先我们这里配置了MyStarterInitializer看一下类图发现它实现了WebApplicationInitializer接口

然后我们发现在spring-web-版本.jar下面META-INF/services下有个文件,文件名为接口javax.servlet.ServletContainerInitializer,内容为实现了文件名接口的类全路径名org.springframework.web.SpringServletContainerInitializer,通过这一重要发现就明白这可能使用了SPI机制。
我们进到SpringServletContainerInitializer看下
SpringServletContainerInitializer类
类里面有个onStartup方法且实现了ServletContainerInitializer,我们知道根据Servlet3的规范,web容器(tomcat)要负责回调项目中实现了ServletContainerInitializer接口的类的onStartup方法,同时,如果方法上添加了@HandlesTypes注解,还要把@HandlesTypes注解当中的class作为参数传递到onStartup方法中
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
/**
* 容器启动的时候会调用该方法,并且传入@HandlesTypes(WebApplicationInitializer.class)类型的所有子类作为入参.
我们发现我们刚刚实现的MyStarterInitializer就是实现的WebApplicationInitializer接口
* @param webAppInitializerClasses 感兴趣类的集合
* @param servletContext 我们应用的上下文对象
*/
@Override
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList<>();
//传入webAppInitializerClasses的所有子类
if (webAppInitializerClasses != null) {
for (Class<?> waiClass : webAppInitializerClasses) {
//不是接口,不是抽象类
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
//通过反射调用创感兴趣类的实例,然后加入到initializers
initializers.add((WebApplicationInitializer)
ReflectionUtils.accessibleConstructor(waiClass).newInstance());
}
catch (Throwable ex) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
}
}
}
}
if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
return;
}
servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
//若我们的WebApplicationInitializer的实现类 实现了Orderd接口或者是标注了@Order注解,会进行排序
AnnotationAwareOrderComparator.sort(initializers);
//依次循环调用我们的感兴趣的类的实例的onStartup方法
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
}
从这发现他会调用我们的MyStarterInitializer中的onStartup()方法
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
//实例化我们的spring的上下文,父容器
super.onStartup(servletContext);
//注册我们的DispatcherServlet 创建我们spring web 上下文对象
registerDispatcherServlet(servletContext);
}
进到super.onStartup发现调用了registerContextLoaderListener方法
registerContextLoaderListener
/**
* 注册我们上下文监听器对象
*/
protected void registerContextLoaderListener(ServletContext servletContext) {
/**
* 创建我们的根的上下文环境WebApplicationContext(AnnotationConfigWebApplicationContext)对象,但是该方在本类中没有被实现,留个了子类实现
* AbstractAnnotationConfigDispatcherServletInitializer实现
*
*/
WebApplicationContext rootAppContext = createRootApplicationContext();
//创建的对象WebApplicationContext不为空
if (rootAppContext != null) {
/**
* <listener>
* <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
* </listener>
*
* <context-param>
* <param-name>contextConfigLocation</param-name>
* <param-value>/WEB-INF/app-context.xml</param-value>
* </context-param>
* 创建一个监听器对象,容器启动完成后会回调listener的contextInitialized
*/
ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
/**
* 第一步:获取根应用的getRootApplicationContextInitializers()
* 第二步:把初始化器设置到监听器中
*/
listener.setContextInitializers(getRootApplicationContextInitializers());
//把监听器加入到我们上下文中.
servletContext.addListener(listener);
}
else {
logger.debug("No ContextLoaderListener registered, as " +
"createRootApplicationContext() did not return an application context");
}
}
registerDispatcherServlet方法
protected void registerDispatcherServlet(ServletContext servletContext) {
//获取我们的DispatcherServlet的名称
String servletName = getServletName();
Assert.hasLength(servletName, "getServletName() must not return null or empty");
//创建WebApplicationContext对象
WebApplicationContext servletAppContext = createServletApplicationContext();
Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");
/**
* 创建我们的DispatcherServlet对象,所以tomcat会对DispatcherServlet进行生命周期管理
*/
FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null");
/**
* 获取我们的ServletApplicationContextInitializers对象,然后把ServletApplicationContextInitializers
* 注册到我们的dispatcherServlet
*/
dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());
//注册我们的dispatcherServlet
ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
if (registration == null) {
throw new IllegalStateException("Failed to register servlet with name '" + servletName + "'. " +
"Check if there is another servlet registered under the same name.");
}
//设置我们的dispatcherServlet的属性
registration.setLoadOnStartup(1);
registration.addMapping(getServletMappings());
registration.setAsyncSupported(isAsyncSupported());
Filter[] filters = getServletFilters();
if (!ObjectUtils.isEmpty(filters)) {
for (Filter filter : filters) {
registerServletFilter(servletContext, filter);
}
}
customizeRegistration(registration);
}
进到的ContextLoaderListener.contextInitialized发现调用initWebApplicationContext方法
initWebApplicationContext方法
/**
* 初始化我们的根容器的上下文对象
*/
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException(
"Cannot initialize context because there is already a root application context present - " +
"check whether you have multiple ContextLoader* definitions in your web.xml!");
}
Log logger = LogFactory.getLog(ContextLoader.class);
servletContext.log("Initializing Spring root WebApplicationContext");
if (logger.isInfoEnabled()) {
logger.info("Root WebApplicationContext: initialization started");
}
long startTime = System.currentTimeMillis();
try {
// 在创建xml版本的时候context是空的 所以我们在这里需要在这里创建根容器对象
if (this.context == null) {
// 注意:由于xml版本中context为空,所以这个if逻辑是用来创建我们的 根容器对象
this.context = createWebApplicationContext(servletContext);
}
/**
* 不为空,很明显我们,这个是注解版本进来的,在外面就已经传递
* 进来了context对象 AnnotationConfigWebApplicationContext,
* 判断我们的AnnotationConfigWebApplicationContext是不是ConfigurableWebApplicationContext类型的
*/
if (this.context instanceof ConfigurableWebApplicationContext) {
//强制转化成ConfigurableWebApplicationContext
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
//判断ConfigurableWebApplicationContext 配置上下文版本的是不是激活了
if (!cwac.isActive()) {//没有激活
//若此时ConfigurableWebApplicationContext对象的父容器为空
if (cwac.getParent() == null) {
//为我们的Root Context加载我们的父容器
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
//配置和刷新我们的根容器对象
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
/**
* 把我们的spring上下文保存到 应用上下文对象中
* 方便我们在Spring web 上下文对象实例化过程会从servletContext取出来
*/
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context;
}
else if (ccl != null) {
currentContextPerThread.put(ccl, this.context);
}
if (logger.isDebugEnabled()) {
logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
}
if (logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
}
return this.context;
}
catch (RuntimeException ex) {
logger.error("Context initialization failed", ex);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
throw ex;
}
catch (Error err) {
logger.error("Context initialization failed", err);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
throw err;
}
}
listener调用完毕后,容器会回调DispatcherServlet->init->FrameworkServlet.initServletBean->initWebApplicationContext
// 在该方法中,我们创建了Spring web应用的上下文对象
protected WebApplicationContext initWebApplicationContext() {
/**
* 从我们的ServletContext对象中获取到Spring root 上下文对象
* 我们在Spring 根容器上下文创建成功后放入到ServletContext对象中
*/
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
/**
* webApplicationContext对象是在创建DispatcherServlet对象的时候,存放进来的一个
* springmvc web的上下文对象
*/
if (this.webApplicationContext != null) {
wac = this.webApplicationContext;
//判断webApplicationContext不为空
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
//判断是否激活
if (!cwac.isActive()) {
//设置父的上下文对象
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
cwac.setParent(rootContext);
}
/**
* 作为SpringMvc 上下文刷新
*/
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
// No context instance was injected at construction time -> see if one
// has been registered in the servlet context. If one exists, it is assumed
// that the parent context (if any) has already been set and that the
// user has performed any initialization such as setting the context id
wac = findWebApplicationContext();
}
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
synchronized (this.onRefreshMonitor) {
// 这里初始化了springMVC组件
onRefresh(wac);
}
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
"' as ServletContext attribute with name [" + attrName + "]");
}
}
return wac;
}
至此父子容器均启动完毕
onRefresh方法
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
//初始化我们web上下文对象的 用于文件上传下载的解析器对象
initMultipartResolver(context);
//初始化我们web上下文对象用于处理国际化资源的
initLocaleResolver(context);
//主题解析器对象初始化
initThemeResolver(context);
//初始化我们的HandlerMapping
initHandlerMappings(context);
//实例化我们的HandlerAdapters
initHandlerAdapters(context);
//实例化我们处理器异常解析器对象
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
//给DispatcherSerlvet的ViewResolvers处理器
initViewResolvers(context);
initFlashMapManager(context);
}
springMVC流程源码分析
在启动过程中我们的前端控制器DispatcherServlet已经实例化完毕,然后客户端发送一个请求到服务端我们会出现什么情况呢?
一般而言一个servlet请求会根据请求类型走doGet,doPost等方法,
我们发现DispatcherServlet的doPost方法还是doGet方法都会调processRequest然后processRequest会调用doService。所以所有的请求解析的步骤都在doService里面进行,我们看一下doService做了什么
doService方法
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
" processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
}
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
//把Spring上下文对象存放到Request的attribute中
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
//把Spring国际化支持解析器对象存放到Request的attribute中
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
//主题解析器对象
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
//主题对象
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
if (this.flashMapManager != null) {
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
}
try {
//真正的进行处理转发
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
doDispatch方法
这个方法是找到对应Controller去调用
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);
/**
* 判断是不是我们文件上传的请求 若是的话返回的processedRequest是MultipartHttpServletRequest
* 那么显然和原始的request对象 不是同一个对象,那么就multipartRequestParsed为true(表示文件上传下载)
*/
multipartRequestParsed = (processedRequest != request);
/**
* 上面所写的springMVC的【第一步】,构建处理器执行链包含Handler(请求对应Controller)和拦截器
* 从我们当前的请求中推断出我们的HandlerExecuteChain处理器执行链对象
* 这个方法很重要
*/
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
/**
* 上面所写的springMVC的【第二步】,找适配器
* 根据Handler选择我们的HandlerAdpater对象
* 默认是RequestMappingHandlerAdapter对象
*/
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 (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//触发我们的拦截器的pre方法,返回false,就不进行处理了
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
/**
* 上面所写的springMVC的【第三步】,调用目标方法
* 通过我们的适配器真正的调用我们的目标方法
* RequestMappingHandlerAdapter.handle===>
* AbstractHandlerMethodAdapter#handle(HttpServletRequest, HttpServletResponse,Object)
*/
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
/**
* 触发我们拦截器链的post方法
*/
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);
}
/**
* 上面所写的springMVC的【第四步】,找到视图解析器并解析视图并渲染视图
* 处理目标方法返回的结果,主要就是渲染视图
*/
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
/**
* 抛出异常:处理我们拦截器的afterCompletion方法
*/
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
/**
* 抛出异常:处理我们拦截器的afterCompletion方法
*/
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 {
/**
* 清楚文件上传时候生成的临时文件
*/
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
getHandler方法
从请求中推断出处理器执行链对象是什么
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
/**
* 判断我们spring web容器中配置的所有的handlerMapping集合对象
* 在本类中的initHandlerMappings()方法为我们的DispatcherServlet类
* 初始化赋值我们的handlerMappings集合
*/
if (this.handlerMappings != null) {
/**
* 循环遍历我们所有的handlerMappings对象,依次调用我们的handlerMappings的getHandler(request)
* 来获取我们的处理器执行链对象,有且有一个handlerMapping对象返回了我们的HandlerExecutionChain
* 那么就不会走下一个逻辑,而是直接返回。
*/
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
/**
* 依次循环调用我们的HandlerMapping的getHandler方法进行获取HandlerExecutionChain
* 但是会调用我们所有的HandlerMapping的父类的
* AbstractHandlerMapping#getHandler(request)
*/
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
}
/**
*通过所有的handlerMapping 对象 还没有获取到对应的HandlerExecutionChain,
* 那么就认为该次请求无法匹配
*/
return null;
}
AbstractHandlerMapping.getHandler方法
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
/**
* 1:找到我们的处理器对象
* 子类的AbstractHandlerMapping的子类RequestMappingHanlderMapping的生命回调接口InitializingBean中
* 把我们的@RequestMapping注解信息和方法映射对象保存到我们的路径映射注册表中
*/
Object handler = getHandlerInternal(request);
//判断上一步的handler是否为空
if (handler == null) {
//返回默认的handler
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
//若我们的解析出来的handler是String 那么就要通过Web 容器创建我么你的handler对象
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
// 根据我们的处理器来构建我们的处理器执行链对象
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
//处理跨越的 暂时不看
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
getHandlerInternal方法
从请求中拿到路径名称,从映射注册表中匹配到Handler,找到返回
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// 获取UrlPathHelper对象,用于来解析从们的request中解析出请求映射路径
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
if (logger.isDebugEnabled()) {
logger.debug("Looking up handler method for path " + lookupPath);
}
//通过映射注册表获取lock对象
this.mappingRegistry.acquireReadLock();
try {
// 通过我们从Request对象中解析出来的lookupPath 然后通过lookupPath获取HandlerMethod对象
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
if (logger.isDebugEnabled()) {
if (handlerMethod != null) {
logger.debug("Returning handler method [" + handlerMethod + "]");
}
else {
logger.debug("Did not find handler method for [" + lookupPath + "]");
}
}
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
//释放锁对象
this.mappingRegistry.releaseReadLock();
}
}
getHandlerExecutionChain方法
构建处理器执行链
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
//创建我们处理器执行链对象
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
//从我们的请求中获取我们的请求映射路径
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
//循环获取我们的所有的拦截器对象
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
//判断拦截器对象是不是实现 HandlerInterceptor
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
//通过路径匹配 看该拦截器是否会拦截本次请求路径
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
//返回我们的拦截器链执行器对象
return chain;
}
handle方法
通过找到的适配器调用处理器执行链中Hadler(Controller)方法,我这里用的是注解@Controller所以它调用的时候会选择RequestMappingHandlerAdapter适配器。
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
((HttpRequestHandler) handler).handleRequest(request, response);
return null;
}
我们一直跟进去发现到了AbstractHandlerMethodAdapter.handleInternal方法
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
//检查我们的请求对象
checkRequest(request);
/**
* 判断当前是否需要支持在同一个session中只能线性地处理请求
* 因为锁是通过 synchronized 是 JVM 进程级,所以在分布式环境下,
* 无法达到同步相同 Session 的功能。默认情况下,synchronizeOnSession 为 false
*/
if (this.synchronizeOnSession) {
// 获取当前请求的session对象
HttpSession session = request.getSession(false);
if (session != null) {
// 为当前session生成一个唯一的可以用于锁定的key
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
// 对HandlerMethod进行参数等的适配处理,并调用目标handler
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// 如果当前不存在session,则直接对HandlerMethod进行适配
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// 如果当前不需要对session进行同步处理,则直接对HandlerMethod进行适配
mav = invokeHandlerMethod(request, response, handlerMethod);
}
//判断当前请求头中是否包含Cache-Control请求头,如果不包含,则对当前response进行处理
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
// 如果当前SessionAttribute中存在配置的attributes,则为其设置过期时间。
// 这里SessionAttribute主要是通过@SessionAttribute注解生成的
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
// 如果当前不存在SessionAttributes,则判断当前是否存在Cache-Control设置,
// 如果存在,则按照该设置进行response处理,如果不存在,则设置response中的
// Cache的过期时间为-1,即立即失效
prepareResponse(response);
}
}
return mav;
}
processDispatchResult方法
找到视图解析器并解析视图并渲染视图
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) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
//渲染我们的视图
if (mv != null && !mv.wasCleared()) {
// 重点方法-视图解析,渲染视图
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
render方法
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
//获取我们国际化语言解析器对象
Locale locale =
(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);
//获取我们视图名称
View view;
String viewName = mv.getViewName();
if (viewName != null) {
//根据我们的视图名称 解析成为我们真正的物理视图(通过视图解析器对象)
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}
// Delegate to the View object for rendering.
if (logger.isDebugEnabled()) {
logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
}
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
//渲染模型视图
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
getServletName() + "'", ex);
}
throw ex;
}
}
resolveViewName
找视图解析器
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
Locale locale, HttpServletRequest request) throws Exception {
//判断当前的视图解析器集合是否为空
if (this.viewResolvers != null) {
//循环调用我们的视图解析器对象解析视图
for (ViewResolver viewResolver : this.viewResolvers) {
// 一旦有我们的视图解析器能够解析出视图,后面的视图解析器不在参与解析,直接返回
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
}
return null;
}