Day60

Day60_SpringMVC03

 

可以在方法处传入Map、或Model或者ModelMap

给这些参数里面保存的所有数据都会放在请求域中,可以在页面获取

 

Map、Model、ModelMap ,最终在都是BindingAwareModelMap在工作。

相当于给BindingAwareModelMap中保存的东西都会放在请求域中。

 

Map(interface(jdk))

Model(interface(Spring))

ModelMap(class) 继承于Map下。

 

数据都放在request域中,既安全又快速。

 

尽量别用@SessionAttributes,用原生SessionAPI放数据。

 

 

@ModelAttribute

参数:取出 刚才的参数

这个方法会提前与目标方法先运行。

参数的Map: BindgAwareModelMap。

 

SpringMVC源码

 

1.前端控制器的架构:DispatcherServlet:

FrameWorkServlet

HttpServletBean

 

DispatcherServletDO

doDispatch()方法的详细源码分析。

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 {
//1.检查当前是否文件上传请求。
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);

// Determine handler for the current request.
               //2.根据当前的请求地址找到哪个类能来处理。
mappedHandler = getHandler(processedRequest);
               
      //3.如果没有找到哪个处理器(控制器)能处理这个请求,就404,或者抛异常。
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}

// Determine handler adapter for the current request.
               //4.拿到能执行这个类的所有方法的适配器。(拿到反射工具)
               //这里的ha = AnntotationMethodHandlerAdpter
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.
               //处理(控制)器的方法被调用
               //控制器(Controller) 或者叫处理器(handler)
               //这里返回一个ModelAndView
               //5.适配器执行目标方法,将目标方法执行完成后的返回值作为视图名,设置保存到ModelAndView中
               //目标方法无论怎么写,最终适配器执行完以后都会将执行后的信息封装为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);
}
           
           //转发到目标页面。
           //6.根据方法最终执行完成后封装的ModelAndView,转发到对呀页面,而且ModelAndView中的数据可以从请求域中获取
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);
}
}
}
}

总结:

1、所有请求过来DispatcherServlet收到请求。

2、调用doDispatch()方法进行处理

1、getHandler():根据当前请求地址找到能处理这个请求的目标处理器类(处理器)

2、getHandlerAdapter():根据当前处理器类获取到能执行这个处理器方法的适配器

根据当前处理器类,找到当前类的HandlerAdapter

3、使用刚才获取到的适配器(AnnotationMethodHandlerAdapter)执行目标方法

4、目标方法执行后会返回一个ModelAndView对象

5、根据ModelAndView的信息转发到具体的页面,并可以在请求域中取出这个ModelAndView中的模型数据。


3、getHandler()细节。

getHandler()会返回目标处理器类的执行链。

HandlerMapping:处理器映射,里面保存了每一个处理器能处理哪些请求的映射信息。

BeanNameUrlHandlerMapping

DefaultAnnotationHandlerMapping -->>能知道哪个类处理哪个请求。

handlerMap:ioc容器启动创建Controller对象的时候扫描每个处理器都能处理什么请求,保存在HandlerMapping的handleMap属性中。下一次请求过来,就来看那个handlerMapping中有这个请求映射信息就行了。


4、如何找到目标处理器类的适配器,要拿适配器才去执行目标方法。

适配器里面有

{RequestMappingHandlerAdapter@4840} ==>拿这个适配器, {HttpRequestHandlerAdapter@4841} {SimpleControllerHandlerAdapter@4842}

注解的适配器:

在 Spring3.1 之前使用 AnnotationMethodHandlerAdapter 注解器适配器(根据 DispatcherServlet.properties 配置文件中的配置) 

3.1之后使用 RequestMappingHandlerAdapter 注解适配器

 

RequestMappingHandlerAdapter是HandlerAdapter的一个具体实现,主要用于将某个请求适配给@RequestMapping类型的Handler处理

 


5、DispatcherServlet中有几个引用类型的属性;SpringMVC的九大组件:

SpringMVC在工作的时候,关键位置都是由这些组件完成的。

共同点:九大组件全都是接口;做成接口的好处:接口就是规范:提供了非常强大的扩展性。

九大组件的工作原理:

/** MultipartResolver used by this servlet. */
@Nullable
//1.文件上传请求,(多部件处理器)
private MultipartResolver multipartResolver;

/** LocaleResolver used by this servlet. */
@Nullable
//2.国际化和(区域信息解析器)有关
private LocaleResolver localeResolver;

/** ThemeResolver used by this servlet. */
@Nullable
//3,主题解析器:强大的主题效果更换(基本不用)
private ThemeResolver themeResolver;

/** List of HandlerMappings used by this servlet. */
@Nullable
//4.Handler映射信息,HandlerMapping
private List<HandlerMapping> handlerMappings;

/** List of HandlerAdapters used by this servlet. */
@Nullable
//5.Handler的适配器
private List<HandlerAdapter> handlerAdapters;

/** List of HandlerExceptionResolvers used by this servlet. */
@Nullable
//6.SpringMVC强大的异常解析功能:异常解析器
private List<HandlerExceptionResolver> handlerExceptionResolvers;

/** RequestToViewNameTranslator used by this servlet. */
@Nullable
//7.没啥用
private RequestToViewNameTranslator viewNameTranslator;

/** FlashMapManager used by this servlet. */
@Nullable
//8.FlashMap+Manager:SringMVC中运行重定向携带数据的功能。
private FlashMapManager flashMapManager;

/** List of ViewResolvers used by this servlet. */
@Nullable
//9.视图解析器。
private List<ViewResolver> viewResolvers;

DispatcherServlet中九大组件初始化的地方:

服务器一启动就运行的。

protected void initStrategies(ApplicationContext context) {
  initMultipartResolver(context);
  initLocaleResolver(context);
  initThemeResolver(context);
  initHandlerMappings(context);
  initHandlerAdapters(context);
  initHandlerExceptionResolvers(context);
  initRequestToViewNameTranslator(context);
  initViewResolvers(context);
  initFlashMapManager(context);
}

可以在web.XML中修改DispatcherServlet初始值。(基本不会修改)

 

 

@ModelAttribute 标注的方法会提前运行并把方法的运行结果放在隐含模型中:

放的时候会使用一个key:

如果@ModelAttribute(“book”)指定了,就用指定的book

如果没指定,就用返回值类型的首字母小写作为key。

 


转发forword:转发到一个页面

/hello.jsp 转发到当前项目下的hello

不会解析拼串。

forward:/hello.jsp

forward:/handle01 --->转到handle01处理,可以转发两次,多次。

 

重定向:redirect:重定向的路径 可以多次重定向。

redirect:hello.jsp 。代表就是从当前项目开始,SpringMVC会为路径自动拼接上项目名。

 

 


SpringMVC视图解析

1.任何方法的返回值,最终都会被包装成ModelAndView对象

2.processDispatchResult(processedRequest,response,mappedHandler,mv,dispatchException);来到页面的方法

视图选人流程;将域中懂得数据在页面展示;页面就是用来渲染模型数据的;

3.调用render(mv,request,response);渲染页面

4.View与ViewResolver:

ViewResovler 的作用根据视图名(方法的返回值)得到View对象

5.怎么能根据方法的返回值(视图名)得到View对象。

 

posted @ 2021-06-14 13:17  独眼龙  阅读(210)  评论(0)    收藏  举报