1、前端控制器的架构:DispatcherServlet
2、DispatcherServlet() 细节:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
try {
ModelAndView mv = null;
Object dispatchException = null;
try {
//*1、检查是否文件上传请求
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
//2、根据当前的请求地址找到哪个类能来处理
mappedHandler = this.getHandler(processedRequest);
//3、如果没有找到相应的控制器来处理这个请求,就报404或者抛异常
if (mappedHandler == null) {
this.noHandlerFound(processedRequest, response);
return;
}
//4、拿到能执行这个类的所有方法的适配器(反射工具)
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
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;
}
//处理(控制)器的方法被调用了
//控制器(Controller),处理器(Handler)
//5、适配器执行目标方法,将目标方法执行完成后的返回值作为视图名,设置保存到
//ModelAndView中
//目标方法无论怎么写,最终适配器执行完成以后都会将执行后的信息封装成ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//如果没有视图名,设置一个默认的视图名
this.applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new NestedServletException("Handler dispatch failed", var21);
}
//转发到目标页面的
//6、根据方法最终执行完成封装的ModelAndView,转发到对应页面,
//而且ModelAndView中的数据可以从请求域中获取
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
} catch (Exception var22) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
} catch (Throwable var23) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
}
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else if (multipartRequestParsed) {
this.cleanupMultipart(processedRequest);
}
}
}
流程总结:
1、所有的请求过来,DispatcherServlet收到请求,
2、调用doDispathc()方法方法进行处理:
1)、getHandler():根据当前请求地址找到能处理这个请求的目标处理类(处理器)
根据当前请求在HandlerMapping中找到能处理这个请求的映射信息,获取到目标处理器类
2)、getHandlerAdapter():根据当前处理类获取到能执行这个处理器方法的适配器
根据当前处理器类,找到当前类的HandlerAdapter(适配器)
3)、使用刚刚获取到的适配器(AnnotationMethodHandlerAdapter)执行目标方法
4)、目标方法执行后会返回一个ModelAndView对象
5)、根据ModelAndView的信息发送到具体的页面,并可以在请求域中取出ModelAndView中的模型数据
2.1、 getHandler() 细节:怎么根据当前请求就能找到哪个类能来处理
getHandler() 会返回目标处理器的执行链(HandlerExecutionChain)
handlerMapping的handlerMap为什么会有值?:IOC容器一启动,就会创建标有注解@Controller的组件对象,然后扫描 @RequsetMapping能处理什么请求,只要扫描到了就会保存handlerMap中,下一次请求过来,就看哪个HandlerMapping中有这个请求映射信息就行了
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//遍历handlerMappings中的handlerMapping
//handlerMappings是一个ArrayList
//handlerMappings中有两个handlerMapping
//一个是:BeanNameUrlHandlerMapping
//一个是:DefaultAnnotationHandlerMapping
//handlerMapping中的handlerMap中有值
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;
}
2.2、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();
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");
}
@Nullable
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) throws Exception {
request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
ModelAndView exMv = null;
if (this.handlerExceptionResolvers != null) {
Iterator var6 = this.handlerExceptionResolvers.iterator();
while(var6.hasNext()) {
HandlerExceptionResolver resolver = (HandlerExceptionResolver)var6.next();
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
if (exMv != null) {
if (exMv.isEmpty()) {
request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
return null;
} else {
if (!exMv.hasView()) {
String defaultViewName = this.getDefaultViewName(request);
if (defaultViewName != null) {
exMv.setViewName(defaultViewName);
}
}
if (this.logger.isTraceEnabled()) {
this.logger.trace("Using resolved error view: " + exMv, ex);
} else if (this.logger.isDebugEnabled()) {
this.logger.debug("Using resolved error view: " + exMv);
}
WebUtils.exposeErrorRequestAttributes(request, ex, this.getServletName());
return exMv;
}
} else {
throw ex;
}
}
2.3 DispatcherServlet九大组件
SpringMVC在工作的时候,关键位置都是由这些组件完成的;
共同点:全部都是接口,接口就是规范,提供了强大的扩展性
@Nullable
//文件上传解析器
private MultipartResolver multipartResolver;
@Nullable
//区域信息解析器:和国际化有关
private LocaleResolver localeResolver;
@Nullable
//主题解析器
private ThemeResolver themeResolver;
@Nullable
//Handler映射信息
private List<HandlerMapping> handlerMappings;
@Nullable
//Handler适配器
private List<HandlerAdapter> handlerAdapters;
@Nullable
//SpringMVC异常解析功能:异常解析器
private List<HandlerExceptionResolver> handlerExceptionResolvers;
@Nullable
private RequestToViewNameTranslator viewNameTranslator;
@Nullable
//SpringMVC中允许重定向携带数据的功能
private FlashMapManager flashMapManager;
@Nullable
//视图解析器
private List<ViewResolver> viewResolvers;
DispatcherServlet中九大组件初始化的地方:服务器一启动就要初始化,初始的时候去容器中找,找不到就使用默认配置
可以在web.xml中修改DispatcherServlet的某些默认配置
protected void onRefresh(ApplicationContext context) {
this.initStrategies(context);
}
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);
}
例如初始化HandlerMappings
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
} else {
try {
HandlerMapping hm = (HandlerMapping)context.getBean("handlerMapping", HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
} catch (NoSuchBeanDefinitionException var3) {
}
}
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");
}
}
}
2.4 mv = ha.handle 执行目标方法的细节
@RequestMapping("/user")
public String user(User user,
Map<String,Object> map,
Model model,
@RequestParam(value = "name")String name)
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return this.handleInternal(request, response, (HandlerMethod)handler);
}
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;
}
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
Object result;
try {
WebDataBinderFactory binderFactory = this.getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = this.getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod invocableMethod = this.createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer)asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
LogFormatUtils.traceDebug(this.logger, (traceOn) -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]);
if (!asyncManager.isConcurrentHandlingStarted()) {
ModelAndView var15 = this.getModelAndView(mavContainer, modelFactory, webRequest);
return var15;
}
result = null;
} finally {
webRequest.requestCompleted();
}
return (ModelAndView)result;
}
两件事:
1、运行流程简单版
2、确定方法每个参数的值:
1、标注解:保存注解的信息,最终得到这个注解应该对应解析的值
2、没标注解:
1)、看是否是原生API;
2)、看是否是Model或者Map.xxx
3)、都不是,看是否是简单类型:paramName
4)、getattrName(参数标了@ModelAttribure("")就是指定的,没有标题的就是"")
确定自定义类型参数:
1)、attrName使用参数的类型首字母小写;或者使用之前@ModelAttribute("")的值
2)、先看隐含模型中有没有这个attrName作为key对应的值,如果有就从隐含模型中获取并赋值
3)、看是否是@SessionAtrribute(value=""haha),标注的属性,如果是从session中拿,如果拿不到就抛异常
4)、不是@SessionAtrribute标注的,利用反射创建一个对象
5)、拿到之前创建好的对象,使用数据绑定器(WebDataBinder)将请求中的每个数据绑定到这个对象中;