什么是Spring MVC ?
Spring MVC就是Spring+MVC。
Spring就不介绍了,什么是MVC?
- M:模型,javabean
- V:视图,如jsp
- C:控制层,如Controller、Servlet
SpringMVC请求处理流程
- 用户发送请求至前端控制器DispatcherServlet;
- DispatcherServlet收到请求调用HandlerMapping处理器映射器;
- 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)HandlerExecutionChain一并返回给DispatcherServlet;
- DispatcherServlet通过HandlerAdapter处理器适配器调用处理器,执行处理器(Controller,也叫后端控制器);
- Controller执行完成返回ModelAndView,并返回给HandlerAdapter,HandlerAdapter将结果返回给DispatcherServlet;
- DispatcherServlet将ModelAndView传给ViewReslover视图解析器,ViewReslover解析后返回具体View给DispatcherServlet;
- DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)后返回给给客户;
源码分析
代码调试准备
新建项目
新建Springboot项目,依赖如下:
java
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
打断点
断点打在下面的位置:
org.springframework.web.servlet.DispatcherServlet#initStrategies:初始化阶段
org.springframework.web.servlet.DispatcherServlet#doDispatch:运行调用阶段。
项目调试启动时未进入上面的两个方法,当第一次调用Controller的请求时,会执行初始化阶段代码,每次调用Controller都会调用运行调用阶段代码。
初始化阶段
initStrategies方法初始化
第一次调用Controller时,会调用org.springframework.web.servlet.DispatcherServlet#initStrategies方法初始化SpringMVC的九大组件
java
protected void initStrategies(ApplicationContext context) {
//多文件上传组件
initMultipartResolver(context);
//初始化本地语言环境
initLocaleResolver(context);
//初始化模版处理器
initThemeResolver(context);
//初始化处理器映射器
initHandlerMappings(context);
//初始化处理器适配器
initHandlerAdapters(context);
//初始化异常拦截器
initHandlerExceptionResolvers(context);
//初始化视图预处理器
initRequestToViewNameTranslator(context);
//初始化视图解析器
initViewResolvers(context);
//初始化FlashMap管理器
initFlashMapManager(context);
}
org.springframework.web.servlet.DispatcherServlet#initStrategies方法的调用链路如下:
HandlerMapping初始化
HandlerMapping是处理器映射器,简单讲是Controller中的方法与请求地址的映射关系,通过它可以找到Controller中的方法来处理请求。
什么是Handler?
Handler是controller中带请求路径的方法,最常用的是Controller中@RequestMapping注解标注的方法。如下面getProduct方法是Handler,loginPage方法不是Handler。
java
@Controller
@Slf4j
public class ClassifyController {
@Autowired
IClassifyService classifyService;
@RequestMapping("/getClassify")
@ResponseBody
Object getProduct(){
log.error("你妹的");
return classifyService.selectByTenant("0000000001");
}
// @GetMapping("/index")
public String loginPage(Model model){
model.addAttribute("name","登科");
return "welcom";
}
}
Spring中共四种Handler
- 加RequestMapping注解的方法(常用的)
- 实现Controller接口的Bean对象(古老的方法)
- 实现HttpRequestHandler接口的Bean对象(古老的方法)
- HandlerFunction对象(新的)
什么是HandlerMapping?
HandlerMapping是处理器映射器,简单讲是Controller中的方法与请求地址的映射关系,通过它可以找到Controller中的方法来处理请求。
三种常用的HandlerMapping
- BeanNameUrlHandlerMapping:处理实现Controller、HttpRequestHandler接口的Bean对象对应的Handler
- RequestMappingHandlerMapping:处理加RequestMapping注解的方法对应的Handler,也是最常用的。
- RouterFunctionMapping:处理HandlerFunction对象对应的Handler
HandlerMapping初始化源码分析
java
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
//获取所有的HandlerMappings,其中包括RequestMappingHandlerMapping
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
//将获取到HandlerMapping赋值给DispatcherServlet.handlerMappings
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
下面的代码获取了所有的HandlerMapping,其中包括RequestMappingHandlerMapping
java
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
以RequestMappingHandlerMapping为例,HandlerMapping中是如何为何请求地址与Handler之间的映射呢?
上面的方法通过BeanFactory获取RequestMappingHandlerMapping对应的实例,因为RequestMappingHandlerMapping是单例的,容器启动的时候会创建单例对象。查看RequestMappingHandlerMapping的父类,实现了InitializingBean接口,因此创建实例的时候会执行初始化方法,而绑定请求地址与Handler的逻辑就在初始化方法中。
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#afterPropertiesSet
java
public void afterPropertiesSet() {
this.config = new RequestMappingInfo.BuilderConfiguration();
this.config.setUrlPathHelper(getUrlPathHelper());
this.config.setPathMatcher(getPathMatcher());
this.config.setSuffixPatternMatch(useSuffixPatternMatch());
this.config.setTrailingSlashMatch(useTrailingSlashMatch());
this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
this.config.setContentNegotiationManager(getContentNegotiationManager());
//跳转到父类的初始化方法
super.afterPropertiesSet();
}
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#afterPropertiesSet
java
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#initHandlerMethods
java
protected void initHandlerMethods() {
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
//绑定url与Handler的逻辑
processCandidateBean(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#processCandidateBean
java
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isTraceEnabled()) {
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
}
}
//isHandler方法就是判断这个bean是否是Controller(有@Controller或者@RequestMapping注解)
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
}
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#detectHandlerMethods
根据给定的Controller,找出这个Controller中的所有Handler,也就是带@RequestMapping注解的方法
java
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<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));
}
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
最后通过org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#registerHandlerMethod方法将映射关系维护到org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#registry对应的Map中。key是mapping对象,其实就是@RequestMapping注解中对应的信息,value是MappingRegistration对象。
java
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
MappingRegistration中
-
mapping是@RequestMapping注解中对应的信息
-
handlerMethod是Handler,也就是@RequestMapping注解的方法
-
directUrl是请求地址,这里观察是/getClassify
-
mappingName是映射的名称,这里观察室CC#getProduct
java
private static class MappingRegistration<T> {
private final T mapping;
private final HandlerMethod handlerMethod;
private final List<String> directUrls;
@Nullable
private final String mappingName;
............
}
HandlerMapping初始化总结
- org.springframework.web.servlet.DispatcherServlet#initStrategies:初始化SpringMVC的九大组件。第一次调用请求Controller时,会初始化DispatcherServlet,因此会调用该方法
- org.springframework.web.servlet.DispatcherServlet#initHandlerMappings:初始化处理器映射器
- 获取所有的处理器映射器实例,并放入到org.springframework.web.servlet.DispatcherServlet#handlerMappings中,其实是List。以RequestMappingHandlerMapping这个处理器映射器为例子,因为RequestMappingHandlerMapping是单例、非延时加载,因此启动项目时会实例化该对象,handlerMappings是直接从缓存中获取的对象。
- 实例化RequestMappingHandlerMapping对象时会调用初始化方法org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#afterPropertiesSet
- 初始化方法会调用父类初始化方法org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#afterPropertiesSet
- 父类初始化方法中会调用org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#initHandlerMethods
- initHandlerMethods方法中会调用org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#processCandidateBean
- processCandidateBean方法中,会判断是否为Controller,如果是则调用org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#detectHandlerMethods,该方法会查找Controller中所有的Handler,也就是该Controller所有标注@RequestMapping注解的方法,并解析注册到org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#registry中 。
- 最终请求相关的信息和Handler就会绑定在一起,存储在HandlerMapping中。
运行调用阶段
随便调用一个Controller中的请求,会访问到org.springframework.web.servlet.DispatcherServlet#doDispatch方法中,调用链路如下:
java
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);
// Determine handler for the current request.
//根据请求获取handler,获取到的是HandlerExecutionChain,HandlerExecutionChain中包括handler和interceptors
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
//走没有Handler的逻辑,404
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
//获取处理器适配器
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;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
//调用handler,也就是Controller中对应的@RequestMapping注解的方法,并返回ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//结果视图对象处理
applyDefaultViewName(processedRequest, mv);
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);
}
}
}
}
根据请求获取Handler
下面的代码是根据请求获取handler,获取到的是HandlerExecutionChain,HandlerExecutionChain中包括handler和interceptors。
java
mappedHandler = getHandler(processedRequest);
org.springframework.web.servlet.DispatcherServlet#getHandler方法,遍历所有的handlerMapping,其中包括RequestMappingHandlerMapping,根据Request通过HandlerMapping获取Handler。
java
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler方法,根据请求获取HandlerExecutionChain。HandlerExecutionChain中包含handler和interceptors。因为初始化阶段初始化HandlerMapping时,已经将请求信息与Handler绑定到一起,所以这里可以通过绑定信息获取,不继续往下跟了。
根据请求获取HandlerAdapter
java
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
之前初始化阶段也初始化了HandlerAdapter,因此有下面的几个:
org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter方法中,循环遍历HandlerAdapter,匹配到则返回,这里匹配到了RequestMappingHandlerAdapter。
java
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {//初始化阶段获取的实例
for (HandlerAdapter adapter : this.handlerAdapters) {//遍历所有的HandlerAdapter ,匹配到则返回
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");
}
调用通过HandlerAdapter调用handler
这里执行了Controller中@RequestMapping注解的方法,走到了请求真正想处理的逻辑,并返回ModelAndView
java
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle方法:
java
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
调用org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal方法
调用org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod
上面的方法中通过下面代码,最终通过反射调用方法。
java
invocableMethod.invokeAndHandle(webRequest, mavContainer);
视图解析器返回View,并渲染页面
org.springframework.web.servlet.DispatcherServlet#processDispatchResult
java
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);
}
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
//获取视图并进行渲染
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("No view rendering, null ModelAndView returned.");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {
// Exception (if any) is already handled..
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
获取视图并进行页面渲染
java
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
org.springframework.web.servlet.DispatcherServlet#render
方法中
java
//根据视图解析器获取视图,这里的视图解析器是初始化的时候通过initViewResolvers方法获取的
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
java
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;
}
org.springframework.web.servlet.DispatcherServlet#render方法中的下面代码,对视图进行渲染。
java
view.render(mv.getModelInternal(), request, response);
SpringMVC运行调用阶段源码总结
- org.springframework.web.servlet.DispatcherServlet#getHandler:根据请求通过HandlerMapping获取Handler
- org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter:根据Handler获取HandlerAdapter
- org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle:调用HandlerAdap的handle方法,执行Handler(Controller中的方法)中的逻辑,返回ModelAndView。
- org.springframework.web.servlet.DispatcherServlet#processDispatchResult:处理视图相关逻辑
- org.springframework.web.servlet.DispatcherServlet#resolveViewName:根据视图解析器获取视图View
- 调用View的render方法渲染jsp页面