1.@RequestMapping
- @RequestMapping注解来映射URl,返回值会通过视图解析器解析为实际的物理试图,对于org.springframework.web.servlet.view.InternalResourceViewResolver试图通过prefix+returnVal+后缀这样的方式得到实际的物理试图,然后做转发操作.
<bean class=*"org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name=*prefix* *value=**/WEB-INF/view/"></property>
<property name=*"suffix" value=".jsp"></property>
</bean>
2.@ModelAttribute
被@ModelAttribute标记的方法,会在每一个目标方法执行之前被springmvc调用
@ModelAttribute
public void getUserInfo(@RequestParam(value = "id",required = false) Integer id, Map<String,Object> map){
System.out.println("xxxxxx");
if(id!=null){
Person person=new Person("x","1");
System.out.println(person);
map.put("person",person);
}
}
@RequestMapping("/demo/xx")
public ModelAndView demo(HttpServletRequest request, HttpServletResponse response,Person person ){
System.out.println("x");
ModelAndView modelAndView=new ModelAndView();
modelAndView.addObject(person);
return modelAndView;
}
运行流程:
- 执行@ModelAttribute注解修饰的方法,从数据库中取出对象,把对象放到map中,
- springmvc从map取出User对象,并把表单的请求参数赋值到给该对象的对象属性
- springmvc吧上述对象传入目标方法参数
源码分析
- 首先会调用@ModelAttribute 注解修饰的方法, 实际上把@ModelAttribute方法中的map 的数据保存到implicitModel中
- 解析请求参数中的目标参数,实际上该目标参数 来自于WebDateBinder对象的target属性
WebDateBinder=bingObject(target对象)+attrName- 创建WebDateBinder对象
- 确定objectName属性:若传入的attrNa me属性值为"",则objectName为类型首字母小写,如果目标方法的pojo属性使用了@ModelAttribute来修饰,则attrName值为@ModelAttribute的value属性值
- 确定target属性
- 在implicitModel中查找attrName对应的属性值,若存在,OK
- 若不存在则验证当前handler是否使用了@SessionAttributes进行修饰,若使用了,则尝试从session中获取attrName所对应的属性值,若session中没有对应的属性值,则抛出异常
- 若Handler没有使用@SessionAttributes修饰,或@SessionAttributes中没有使用value值指定的key和attrName相匹配,则通过反射创建POJO对象
- springmvc把表单的请求参数赋给了WebDateBinder的target对象
- springmvc会把WebDateBinder的attrName和target给到implicitModel,传到request域中
- 把WebDateBinder的target作为参数传递给目标方法入参
- 创建WebDateBinder对象
3.springmvc是怎么确定目标对象方法POJO类型入参的过程的?
- 确定一个key
- 若目标方法的pojo类型的参数没有@ModelAttribute作为修饰,则key为POJO类型的首字母小写
- 若使用了@ModelAttribute来修饰,则key为@ModelAttribute注解的value属性值
- 在implicitModel中查找key对象的对象,若存在,则作为入参传入
- 若在@ModelAttribute标记的方法中在map中保存在,且key和之前确定的key一致,则会获取到
- 若implicitModel获取不到key对应的对象,则会检查当前的handler是否使用@SessionAttributes注解修饰,若使用了该注解,且@SessionAttributes注解的value包含了key,则会从HttpSession获取到key所对应的value的值,若存在则直接传入到目标对象的入参中,若不存在则将抛出异常
- 若handler没有标识@SessionAttributes注解或@SessionAttributes注解的@Value不包含key ,则通过反射创建POJO类型的参数,传入目标方法的参数
- springmvc会把key和POJO类型的对象保存到implicitModel中,进而保存到request中
4.springmvc数据绑定流程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iLt64TnN-1575211185165)(/Users/laiyanxin/Library/Application Support/typora-user-images/image-20191201140712842.png)]
流程
-
Springmvc通过反射机制对目标处理方法进行解析,并将请求消息绑定到处理方法入参中.
-
Springmvc会将ServletRequest对象及目标处理方法的参数对象实例传递到DataBinder,DataBinder调用装配在Spring web上下文的ConversionService组件进行数据类型转换,数据格式化工作,并将ServletRequest中的消息填充到参数对象中,然后再调用Vaildator组件已经绑定了请求消息数据的参数对象进行数据合法性校验,并最终生成数据绑定结果BindingResult对象.
-
BindingResult对象包含了已完成数据绑定参数对象,还包含了相对应的校验错误对象.
5.@RequestBody @ResponseBody原理
工作原理图[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TPrzMpcm-1575211185166)(/Users/laiyanxin/Library/Application Support/typora-user-images/image-20191201153135250.png)]
- 从请求报文先转为HttpInputMessage再由HttpMessgetConverter转为我们需要的java对象
- spingmvc返回一个对象先转为 HttpMessgetConverter再转HttpOutPutMessage再响应信息
public interface HttpInputMessage extends HttpMessage {
//把请求信息转为输入流
InputStream getBody() throws IOException;
}
public interface HttpOutputMessage extends HttpMessage {
//把请求信息转为输出流
OutputStream getBody() throws IOException;
}
核心在于HttpMessgetConverter,SpringMVC处理请求和响应时,支持多种类型的请求参数和返回类型,而此种功能的实现就需要对HTTP消息体和参数及返回值进行转换,为此SpringMVC提供了大量的转换类,所有转换类都实现了HttpMessageConverter接口。
6拦截器执行流程
拦截器有三个方法
- preHandle 在目标方法之前调用
- postHandle 调用目标方法后,渲染试图之前
- afterCompletion 渲染试图后
- 拦截器可以通过配置作用的路径
<mvc:mapping ><mvc:exclude-mapping>
配置包含关系
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;
----省略代码-----
//在目标方法之前调用
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 调用目标方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
//调用目标方法后,渲染试图之前调用
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
//渲染试图
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
}
//渲染视图
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 (mappedHandler != null) {
// 渲染视图完之后调用
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
7拦截器执行顺序
- 正常流程
第一个拦截器preHandle—》第二个拦截器preHandle–》目标方法–》第二个拦截器postHandle–》第一个拦截器postHandle --》第二个拦截器afterCompletion–》第一个拦截器afterCompletion
- 第二个拦截器preHandle返回false
第一个拦截器preHandle—》第二个拦截器preHandle–》第一个拦截器afterCompletion
- preHandle正序,afterCompletion和postHandle倒序
8. springmvc执行流程图
9.springmvc源码解析
核心方法在于DispatcherServlet中的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 {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 获取当前请求的HandlerExecutionChain,根据handleMapping获取
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
//获取.HandlerAdapter
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;
}
// 调用目标方法
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) {
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);
}
}
}
}
-
先获取一个mappingHandler,类型就是HandleExecutionChain,处理器调用链(包含了拦截器,目标方法和handler ),通过handlerMapping获取
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; }
-
获取到hadlerAdapter,( 包含messageConver,数据转换,数据格式化,校验等等)
-
调用目标方法返回ModelAndView
-
调用拦截的applyPostHandle
-
处理视图processDispatchResult
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
//如果有异常就通过handlerExceptionResolvers来处理
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 (mappedHandler != null) {
// 渲染完调用 拦截器afterCompletion
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
-
会先判断是否有异常,如果有异常就通过handlerExceptionResolvers来处理
-
如果没异常就进行渲染试图
- 会先通过ViewResolvers得到View
- 下一步渲染视图
-
渲染完调用 拦截器afterCompletion
-
会先判断是否有异常,如果有异常就通过handlerExceptionResolvers来处理
-
如果没异常就进行渲染试图
- 会先通过ViewResolvers得到View
- 下一步渲染视图
-
渲染完调用 拦截器afterCompletion