1.请求入口 DispatcherServlet
流程:
1、给Web项目中配置一个 DispatcherServlet,拦截 / 请求
2、初始化的时候 DispatcherServlet 会启动IOC容器,而且会 初始化他底层的九大组件
3、请求到来的时候,Tomcat找到 DispatcherServlet 能处理请求,交给他,DispatcherServlet调用底层的九大组件结合容器、自动装配、反射等功能进行请求处理
2.九大组件
protected void initStrategies(ApplicationContext context) {
this.initMultipartResolver(context);
this.initLocaleResolver(context);
this.initThemeResolver(context);
this.initHandlerMappings(context);
this.initHandlerAdapters(context);
this.initHandlerExceptionResolvers(context);
this.initRequestToViewNameTranslator(context);
this.initViewResolvers(context);
this.initFlashMapManager(context);
}
//全是接口
initMultipartResolver(context);
##以下都有默认功能
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
-
MultipartResolver:文件上传解析器。
从容器中获取MultipartResolver类型的,并且名字叫multipartResolver的组件。如果有用容器中的,如果没有就为null -
LocaleResolver:国际化解析器 zh_cn\en_us。来完成页面国际化功能
从容器中获取LocaleResolver类型的组件,并且名为localeResolver。如果没有,加载 DispatcherServlet.properties文件中指定的。org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver -
ThemeResolver:主题解析器(用的比较少)
-
HandlerMapping: 处理器映射(解析了系统中所有Controller组件,缓存他们请求映射信息,。想要知道哪个请求由谁处理,在他里面找即可。 请求–controller–method)
从容器中找 类型为HandlerMapping并且名字为handlerMapping的组件。没有就加载 DispatcherServlet.properties文件中指定默认的
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping(组件名字作为请求映射),\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping(把@RequestMapping标注的路径作为请求的路径进行映射),\
org.springframework.web.servlet.function.support.RouterFunctionMapping(webflux)
-
HandlerAdapter: handler(我们自己的controller)的适配器,未来决定目标方法如何执行的(反射进行请求处理的核心逻辑)。
从容器中获取所有的 HandlerAdapter 组件。没有就加载默认值
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter(反射处理标注@RequestMapping注解的方法),\
org.springframework.web.servlet.function.support.HandlerFunctionAdapter(webflux)
- HandlerExceptionResolver:异常解析器
从容器中获取所有的 HandlerExceptionResolver 组件。没有就加载默认值
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
-
RequestToViewNameTranslator:请求转为要跳转的视图名的转换器
从容器中获取 RequestToViewNameTranslator 并且名为 viewNameTranslator;
#没有就加载默认的
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
-
ViewResolver:视图解析器。决定页面的视图跳转逻辑。
- 容器中找到所有的 ViewResolver,
- 如果没有SpringMVC会给我们自动配置一个 org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
-
FlashMapManager:闪存管理器。(重定向携带数据)【利用session进行闪存】
- 从容器中获取类型为FlashMapManager 并且名字为 flashMapManager的组件
- 如果没有,SpringMVC将加载一个默认的 org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
3.九大组件作用
1.HandlerMapping(保存全系统哪些请求,谁来处理的)
@Nullable
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;
}
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
Iterator var2 = this.handlerMappings.iterator();
while(var2.hasNext()) {
HandlerMapping mapping = (HandlerMapping)var2.next();
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
HandlerMapping SpringBoot底层默认配置了。SpringBoot自己配置了
1-1、RequestMappingHandlerMapping:最强大,自定义Controller+ @RequestMapping
利用InitializingBean机制,在创建完对象以后是分析全系统的所有组件,分析每个方法 @RequestMapping 注解信息,注册到请求映射里面
RequestMappingHandlerMapping 原理
1、什么时候被创建的?
- 1、SpringBoot自动配置了SpringMVC功能,给容器中放了一个 RequestMappingHandlerMapping
- 2、初始化所有非懒加载单实例Bean的时候会创建这个对象
2、创建这个Bean的时候 后置处理器 以及生命函数有没有对其进行功能增强?
1、后置处理器没干活
2、生命周期做事情、
- Bean创建完对象,调用 InitializingBean.afterPropertiesSet】
- 拿到容器中所有的组件名字
- 获取当前名字的组件类型
- 只要发现这个类上标注了 @RequestMapping 或者 @Controller注解,就认为是处理器
- 利用反射扫描这个类的所有方法,把他们标注的@RequestMapping信息和method封装到HandlerMapping的mappingRegistry中。
3、怎么样的增强?
挨个方法扫描。请求映射信息缓存起来。以后所有的请求,上来就能找到当前请求使用哪个Handler进行处理
1-2、WelcomePageHandlerMapping: SpringBoot独家提供的 提供了 /** 路径下的所有资源在。
"classpath:/META-INF/resources/",
"classpath:/resources/",
"classpath:/static/",
"classpath:/public/" 的 index.html 下面找
以上是SpringBoot底层的静态资源路径
/**
会有底层的 ParameterizableViewController 东西来处理请求
1-3、BeanNameUrlHandlerMapping
@Component("/abc") //要想启用他,controller可以哦这么写
public class RequestController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView modelAndView = new ModelAndView();
response.getWriter().write("icoding 11111 ");
return null;
}
}
1-4、SimpleUrlHandlerMapping
SpringBoot自己用来适配静态资源的
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
if (!registry.hasMappingForPattern("/webjars/**")) {
customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/")
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)
.setUseLastModified(this.resourceProperties.getCache().isUseLastModified()));
}
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)
.setUseLastModified(this.resourceProperties.getCache().isUseLastModified()));
}
}
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;
}
//所有的 HandlerMapping 挨个进行映射处理,如果能处理就返回对象,否则返回null,轮到下一个处理
1-5 自定义HandlerMapping
/api/v1/xxxxx;全部是v1的,全部去v1包下找映射
/api/v2/xxxxx;全部是v1的,全部去v2包下找映射
@Order(1)
@Component
public class BaiDuHandlerMapping /*extends AbstractUrlHandlerMapping*/ implements HandlerMapping {
@Override
public boolean usesPathPatterns() {
return true;
}
@Override
public HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
String requestURI = request.getRequestURI();
if(requestURI.startsWith("/baidu")){
return new HandlerExecutionChain(new Object());
}
return null;
}
}
2、HandlerAdapter
HandlerMapping找到请求映射以后会给我们返回 HandlerExcutionChain
处理器的执行链
2-1、 HandlerExcutionChain
- 如果我们方法标注了 @RequestMapping 注解会直接返回这个方法的详细信息。包装成 HandlerMethod
- 如果我们写的简单处理器(不是用@RequestMapping做的)返回如下。原生的Handler和拦截 包装成 HandlerExcutionChain
2-2、根据HandlerExcutionChain返回的handler需要找到适配器
底层4个适配器
- RequestMappingHandlerAdapter:判断当前handler是不是 HandlerMethod,也就是由@RequestMapping解析到的
- HandlerFunctionAdapter: 适配Webflux的
- HttpRequestHandlerAdapter:看当前handler 是不是 HttpRequestHandler
- SimpleControllerHandlerAdapter: 看当前handler 是不是 Controller
场景: 原来的SpringMVC不兼容直接写Servlet进行请求处理,自定义适配器+beanNameUrlHandlerMapping实现这个功能
/**
* 执行实现了 Servlet 接口的类
*/
@Component
public class ServletHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof Servlet);
}
@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Servlet baiduHandler = (Servlet) handler;
baiduHandler.service(request,response);
return null;
}
@Override
public long getLastModified(HttpServletRequest request, Object handler) {
return 0;
}
}
3.参数解析流程
4. 返回值解析流程
返回值处理器的总接口:HandlerMethodReturnValueHandler
mavContainer:方法执行期间的一些关键数据,defaultModel一般保存方法运行期间给Model里面保存的数据
1、任何返回值的目标方法,处理结束后都会返回ModelAndView(以JSON返回mav为null,以页面返回才会有数据)
1、先确定目标方法的每一个参数的值(参数处理流程)
2、利用反射执行目标方法,并获取到目标方法执行后返回值 User@6557
3、利用返回值处理器处理返回值
1、遍历所有的返回值处理器,看谁能处理这个返回值,返回这个处理器
2、使用这个返回值处理器进行返回值的处理工作
1、标记当前请求已经被处理 mavContainer.setRequestHandled(true);
2、writeWithMessageConverters【json】,使用MessageConverter把数据写出去
3、决定媒体类型(json、html、xml、image?)【内容协商】
1、使用contentNegotiationManager(内容协商管理器)判断浏览器能接受的内容类型;默认使用HeaderContentNegotiationStrategy(基于请求头的内容协商)String[] headerValueArray = request.getHeaderValues("Accept");
2、获取服务器能生产的内容类型。SpringBoot底层内置了很多的消息转换器HttpMessageConverter(代表了服务器的底层能力)。
3、最终消息转换器会把对象以json的方式写出去。ModelAndView会封装为null
1、返回对象,作为数据直接以json响应()
RequestResponseBodyMethodProcessor处理@ResonpseBody方式的返回值
总结:
1、所有的返回值处理器进行处理器,最终会使用RequestResponseBodyMethodProcessor来处理器
2、RequestResponseBodyMethodProcessor是利用底层的 HttpMessageConverter 将消息进行转换。
3、消息转换期间需要进行内容协商(多端适配)。
导入jacksonxml 包底层
2、页面渲染流程
返回的普通对象,String、Model、View、ModelAndView:就会跳转页面
String-》ViewNameMethodReturnValueHandler
1、ViewNameMethodReturnValueHandler处理字符串返回值,把字符串值设置到mavContainer中的viewName字段
2、mavContainer的model以及view的内容重新被封装成一个ModelAndView进行返回
3、如果方法返回void那么请求的路径就会被当成视图名(要跳转的页面地址)
4、进入视图解析流程
5、页面渲染流程
ViewResolver?View?
ViewResolver解析viewName得到View;View.render 才是真正页面逻辑的展示
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
拿到mv以及dispatchException开始进行处理
1、判断是否有异常。有异常处理异常。
2、没有异常进入页面渲染流程 render(mv, request, response);
1、根据方法的返回值viewName得到要去的页面的View对象
2、所有视图解析器看谁能知道当前viewName该去哪里
3、内容协商功能会把所有视图解析器都拿来组合起来,得到最终所有能解析的结果,最终根据内容协商原理选择一个最合适的View
4、得到View对象,调用View的render(渲染)
1、beanNameViewResolver: context.getBean(viewName, View.class);从容器中得到名为目标方法返回值的View组件
2、ThymeleafViewResolver: 直接用缓存中的,或者创建出视图;
返回值以redirect:开始;就会创建一个 RedirectView
返回值以forward:开始;就会创建一个 InternalResourceView
不以上面的开始就loadView;
1、看容器中有没有 viewName的View对象
2、没有1就创建一个 ThymeleafView对象;名字就叫viewName。并且放在容器中(利用了缓存)。
6、异常解析流程(HandlerExceptionResolver)
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
//处理完异常继续返回modelAndView
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
//渲染modelAndView
render(mv, request, response);
1、SpringBoot中对异常处理自动配置
分析 ErrorMvcAutoConfiguration;
- 1、绑定了两个配置类 ServerProperties.class, WebMvcProperties.class ,异常配置可以参照
- 2、给容器中放了如下组件
- DefaultErrorAttributes: ErrorAttributes, HandlerExceptionResolver(处理器异常解析器)
- HandlerExceptionResolver最终解析异常返回ModelAndView对象。最终这个mav对象会被渲染
- 第一次目标方法出现异常,先把异常信息存起来,第二次/error进来把上次请求发送的所有错误信息转为map
- DefaultErrorAttributes: ErrorAttributes, HandlerExceptionResolver(处理器异常解析器)
- BasicErrorController:一个处理器,处理 /error请求。基于内容协商可以适配。如果是html就返回ModelAndView,如果是json就返回 ResponseEntity<Map<String, Object>>
- ErrorPageCustomizer: 错误页的定制化器,系统发生错误,自动发送 /error 请求将错误传递下去
- DefaultErrorViewResolver: ErrorViewResolver,默认的错误视图解析器
- View:名字叫error的错误视图。
- BeanNameViewResolver:beanName的视图解析器,按照bean的名字在容器中找View组件
2、ExceptionHandlerExceptionResolver
1、使用@ExceptionHandler标准的异常处理方法能写以下参数
2、使用@ExceptionHandler标准的异常处理方法能写以下返回
3、执行使用 @ExceptionHandler 标注的异常处理方法。共享适配器执行目标方法的流程
- 使用参数解析器确定参数值,
- 目标方法执行
- 使用返回值处理器处理返回值
3、ResponseStatusExceptionResolver
处理自定义异常标注 @ResponseStatus 注解。
如果自定异常标注了注解
- 1、如果 @ResponseStatus 没有指定reason,就会 response.sendError(statusCode);
- 2、如果 @ResponseStatus 指定reason,reason可国际化解析,然后 response.sendError(statusCode, resolvedReason);
response.sendError(statusCode); 默认是
实际上是:
4、DefaultHandlerExceptionResolver
底层的异常会触发这个异常解析器。效果是: