从上一篇 SpringMVC源码分析(1) 中我们了解到在DispatcherServlet.doDispatch方法中会通过 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()) 这样的方式来执行request的handler方法。

  先来分析一下ha.handle方法的调用过程:HandlerAdapter接口有一个抽象实现类AbstractHandlerMethodAdapter,在该抽象类中通过具体方法handle调用抽象方法handleInternal:

 1 public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {
2
3 private int order = Ordered.LOWEST_PRECEDENCE;
4 @Override
5 public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
6 throws Exception {
7 return handleInternal(request, response, (HandlerMethod) handler);
8 }
9 //抽象方法,由具体的Adapter实现
10 protected abstract ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception;
11
12 }

   RequestMappingHandlerAdapter类继承了抽象类AbstractHandlerMethodAdapter,实现了抽象方法 handleInternal,下面看看handleInternal方法的具体实现(需要注意,handler方法在synchronizeOnSession为true的情况下会放在同步代码块中进行执行):

 1 public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean {
2
3 //省略若干代码...
4
5 @Override
6 protected ModelAndView handleInternal(HttpServletRequest request,
7 HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
8
9 if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
10 // Always prevent caching in case of session attribute management.
11 checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
12 }
13 else {
14 // Uses configured default cacheSeconds setting.
15 checkAndPrepare(request, response, true);
16 }
17
18 // Execute invokeHandlerMethod in synchronized block if required.
19 /*
20 * synchronizeOnSession默认为false,如果其为true,那么request对于的handler将会被放置在同步代码块
21 * 中进行执行。问题:什么时候???通过怎样的方式将synchronizeOnSession设置为true???
22 */
23 if (this.synchronizeOnSession) {
24 HttpSession session = request.getSession(false);
25 if (session != null) {
26 Object mutex = WebUtils.getSessionMutex(session);
27 synchronized (mutex) {
28 return invokeHandleMethod(request, response, handlerMethod);
29 }
30 }
31 }
32 // 不在同步块中执行handler方法
33 return invokeHandleMethod(request, response, handlerMethod);
34 }
35 }

   现在就分析上面代码块中的 invokeHandleMethod(request, response, handlerMethod) 方法的执行流程,看看在调用handler前后又完成了什么工作,同时分析出@ModelAttribute的作用。先来总体看看,然后再各个部分分别做 分析,一共分为6个步骤(step1 ~ step6):

 1 /**
2 * Invoke the @RequestMapping handler method preparing a @ModelAndView
3 * if view resolution is required.
4 */
5 private ModelAndView invokeHandleMethod(HttpServletRequest request,
6 HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
7
8 ServletWebRequest webRequest = new ServletWebRequest(request, response);
9
10 WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
11 ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
12 ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod, binderFactory);
13
14 //step 1
15 //新建一个mavContainer,用于存放所有可能会用到的ModelAndView
16 ModelAndViewContainer mavContainer = new ModelAndViewContainer();
17
18 //step 2
19 /*
20 * Attributes can be set two ways. The servlet container may set attributes
21 * to make available custom information about a request. For example, for
22 * requests made using HTTPS, the attribute
23 * <code>javax.servlet.request.X509Certificate</code> can be used to
24 * retrieve information on the certificate of the client. Attributes can
25 * also be set programatically using {@link ServletRequest#setAttribute}.
26 * This allows information to be embedded into a request before a
27 * {@link RequestDispatcher} call.
28 *
29 * RequestContextUtils.getInputFlashMap(request)可以获取到request中的attribute,
30 * 并且将所有的request中的attribute放置在mavContainer中
31 */
32 mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
33
34 //step 3
35 /*
36 * 会在这个方法里将所有标注了@ModelAttribute的方法调用一遍,并且将该方法相关的ModelAndView放入到mavContainer中:
37 * 1、最常见的就是@ModelAttribute标注的方法入参中有Map,Model
38 * 2、如果方法有返回值,那么也会结果处理后放入到mavContainer中(是否只有ModelAndView类型的返回值才能放,有待考察??)
39 */
40 modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);
41 mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
42
43 //step 4
44 //许多和 asyncManager 相关的东西,这个貌似和拦截器有关。
45 AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
46 asyncWebRequest.setTimeout(this.asyncRequestTimeout);
47
48 final WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
49 asyncManager.setTaskExecutor(this.taskExecutor);
50 asyncManager.setAsyncWebRequest(asyncWebRequest);
51 asyncManager.registerCallableInterceptors(this.callableInterceptors);
52 asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
53
54 if (asyncManager.hasConcurrentResult()) {
55 Object result = asyncManager.getConcurrentResult();
56 mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
57 asyncManager.clearConcurrentResult();
58
59 if (logger.isDebugEnabled()) {
60 logger.debug("Found concurrent result value [" + result + "]");
61 }
62 requestMappingMethod = requestMappingMethod.wrapConcurrentResult(result);
63 }
64
65 //step 5
66 /*
67 * Invokes the method and handles the return value through one of the configured {@link HandlerMethodReturnValueHandler}s.
68 * 在这里调用handler方法
69 */
70 requestMappingMethod.invokeAndHandle(webRequest, mavContainer);
71
72 //step 6
73 //要么返回ModelAndView,要么返回null
74 if (asyncManager.isConcurrentHandlingStarted()) {
75 return null;
76 }
77 return getModelAndView(mavContainer, modelFactory, webRequest);
78 }

  

  下面从step1~step6逐一的进行分析。

  step1:ModelAndViewContainer mavContainer = new ModelAndViewContainer();

  首先看一下ModelMap是如何定义的,这对理解ModelAndViewContainer以及后面的代码有帮助,主要是理解:ModelMap实际上就是一个LinkedHashMap,而且这个Map的”值“是Object类型,能够放置所有类型的Java对象

/**
* 可以看出ModelMap实际上就是一个LinkedHashMap,且“值”为超类Object类型
* 能够放置所有的Java对象
*/
public class ModelMap extends LinkedHashMap<String, Object> {
public ModelMap() {
} // 调用这个构造函数之前会先调用其父类构造函数,得到一个Map对象
public ModelMap(String attributeName, Object attributeValue) {
addAttribute(attributeName, attributeValue);
} // 就是将attributeValue对象放置到Map末尾,同时指定键值为attributeName
public ModelMap addAttribute(String attributeName, Object attributeValue) {
put(attributeName, attributeValue);
return this;
} // attributes是一个Map集合;所谓merge无非是将attributes这个集合放置到现有集合的末尾
public ModelMap mergeAttributes(Map<String, ?> attributes) {
if (attributes != null) {
for (Map.Entry<String, ?> entry : attributes.entrySet()) {
String key = entry.getKey();
if (!containsKey(key)) {
put(key, entry.getValue());
}
}
}
return this;
} //省略一些方法的定义...
}

  再来看ModelAndViewContainer到底是个什么东西,从下面的ModelAndViewContainer定义中不难理解其含有两个ModelMap对象defaultModel和redirectModel默认情况下使用defaultModel,也可以通过其方法设置使用redirectModel。

 1 /**
2 * 定义了两个ModelMap对象:defaultModel和redirectModel,实际上也就是两个Map<String, Object>
3 * 其中defaultModel已经完成了初始化。默认使用defaultModel。
4 * 也可以通过其中的方法来设置使用redirectModel,这个是方便移植和使用其它框架而设定的。
5 */
6 public class ModelAndViewContainer {
7
8 private boolean ignoreDefaultModelOnRedirect = false;
9   // view的用法值得去探究
10 private Object view;
11
12 /* BindingAwareModelMap实际上也就是一个Map,
13 * 看看定义 public class BindingAwareModelMap extends ModelMap implements Model
14 *
15 * 从这里可以看出,ModelAndViewContainer对象都有一个默认的ModelMap
16 */
17 private final ModelMap defaultModel = new BindingAwareModelMap();
18
19 // 这个为方便移植其它框架的Model而设置的,SpringMVC本身使用的是defaultModel
20 private ModelMap redirectModel;
21
22 private boolean redirectModelScenario = false;
23
24 private final SessionStatus sessionStatus = new SimpleSessionStatus();
25
26 private boolean requestHandled = false;
27
28 /**
29 * Set a view name to be resolved by the DispatcherServlet via a ViewResolver.
30 * Will override any pre-existing view name or View.
31 */
32 public void setViewName(String viewName) {
33 this.view = viewName;
34 }
35
36 /**
37 * 返回"default" 或者是 "redirect" 模型,具体根据redirectModelScenario等
38 * 属性的值来确定(具体用法参看javadoc)
39 */
40 public ModelMap getModel() {
41 if (useDefaultModel()) {
42 return this.defaultModel;
43 }
44 else {
45 return (this.redirectModel != null) ? this.redirectModel : new ModelMap();
46 }
47 }
48
49 // 返回默认的Model,而不考虑其它的属性值如何
50 public ModelMap getDefaultModel() {
51 return this.defaultModel;
52 }
53
54 }

  到现在为止,step1完成的工作已经分析完全。

  step2:  mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request))

/* * 
* 检索request域中的attribute,并将其放置在mavContainer末尾
*
* RequestContextUtils.getInputFlashMap(request)会调用HttpServletRequest.getAttribute方法。
* 可以获取到request中的attribute属性。这个attribute就是我们属性的attribute,它可以通过两
* 种方式来设置:①servlet容器为request设置的;②通过ServletRequest.setAttribute方法来设置。
*
*/
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));

  step3:modelFactory.initModel(webRequest, mavContainer, requestMappingMethod)

  这个方法的作用在javadoc中描述得很清楚:

  Populate the model in the following order:

  1. Retrieve "known" session attributes listed as @SessionAttributes.
  2. Invoke @ModelAttribute methods
  3. Find @ModelAttribute method arguments also listed as @SessionAttributes and ensure they're present in the model raising an exception if necessary.

  也就是说初始化模型的时候会按顺序完成三件事情

  ①、检索现有的session域中的attributes,并将其放置于mavContainer末尾;

  ②、调用所有@ModelAttribute注解标注的方法;

  ③、找出handler方法中使用@ModelAttribute注解修饰的入参(主要是@ModelAttribute指定的value属性值,如果没有指定则是类名第一个字母小写得到,我们假定它为V),如果V同时被@SessionAttributes的value属性值指定,那么就必须保证在此时的mavContainer中必须含有”key“为V的对象。如果没有,则会抛出一个异常

  

  带着这个印象我们分析代码就会容易很多:

 1 /*
2 * 会在这个方法里将所有标注了@ModelAttribute的方法调用一遍,并且将该方法相关的ModelAndView放入到mavContainer中:
3 * 1、最常见的就是@ModelAttribute标注的方法入参中有Map,Model
4 * 2、如果方法有返回值,那么也会结果处理后放入到mavContainer中(是否只有ModelAndView类型的返回值才能放,有待考察??)
5 */
6 modelFactory.initModel(webRequest, mavContainer, requestMappingMethod){
7 //完成①:检索现有的session域中的attributes,并将其放置于mavContainer末尾
8 Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
9 mavContainer.mergeAttributes(sessionAttributes);
10
11 //完成②:将所有的@ModelAttribute标注的方法都调用一遍。调用完了以后,将调用方法的结果放置到mavContainer中
12 invokeModelAttributeMethods(request, mavContainer){
13 //modelMethods中包含了所有@ModelAttribute标注的方法,在这个while循环中将会把所有@ModelAttribute标注
14 //的方法都调用一遍
15 while (!this.modelMethods.isEmpty()) {
16 InvocableHandlerMethod attrMethod = getNextModelMethod(mavContainer).getHandlerMethod();
17 String modelName = attrMethod.getMethodAnnotation(ModelAttribute.class).value();
18 //如果,mavContainer中已经包含有modelName名的attribute,那么,将不会调用@ModelAttribute标注的方法
19 if (mavContainer.containsAttribute(modelName)) {
20 continue;
21 }
22
23 //真正的调用@ModelAttribute标注的方法
24 Object returnValue = attrMethod.invokeForRequest(request, mavContainer);
25
26 //如果@ModelAttribute标注的方法不是void类型,则将其返回结果转换成returnValueName;如果mavContainer中
27 //没有包含returnValueName,则将方法返回的结果放置到mavContainer中。
28 if (!attrMethod.isVoid()){
29 String returnValueName = getNameForReturnValue(returnValue, attrMethod.getReturnType());
30 if (!mavContainer.containsAttribute(returnValueName)) {
31 mavContainer.addAttribute(returnValueName, returnValue);
32 }
33 }
34 }
35 };
36
37
38 // 完成③:
39
40 /*
41 * 找出handler方法中使用@ModelAttribute注解修饰的入参(主要是@ModelAttribute指定的value属性值,
42 * 如果没有指定则是类名第一个字母小写得到,我们假定它为V),如果V同时被@SessionAttributes的value
43 * 属性值指定,则将这样的V放入到nameList中。
44 *
45 */
46 List<String> nameList = findSessionAttributeArguments(handlerMethod){
47 List<String> result = new ArrayList<String>();
48 //遍历处理方法的所有参数
49 for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
50 //如果处理方法有@ModelAttribute标注
51 if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
52 //得到参数的String型的名字
53 String name = getNameForParameter(parameter);
54 //如果在处理方法所在的类定义处使用了@SessionAttributes注解,那么就将该参数放入到List中
55 if (this.sessionAttributesHandler.isHandlerSessionAttribute(name, parameter.getParameterType())) {
56 result.add(name);
57 }
58 }
59 }
60 return result;
61 };
62
63 // 保证nameList中记录的V必须在mavContainer中存在,如果不存在则会抛出异常
64 for (String name : nameList) {
65 //如果mavContainer中没有包含有name名字的attribute,那么再一次检查request中是否包含了name名字的attribute
66 if (!mavContainer.containsAttribute(name)) {
67 //再一次检查request中是否包含了name名字的attribute
68 Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
69 //如果没有检测到,则会抛出一个异常
70 if (value == null) {
71 throw new HttpSessionRequiredException("Expected session attribute '" + name + "'");
72 }
73 //如果检测到了,那么会将这个attribute放入到mavContainer中
74 mavContainer.addAttribute(name, value);
75 }
76 }
77 };

  step4: 暂时不做分析...

  step5: 调用handler方法同时处理返回结果

  1 requestMappingMethod.invokeAndHandle(webRequest, mavContainer){
2 //一、 调用处理方法,并的到返回结果
3 Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs){
4 //为调用处理方法准备参数
5 Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs){
6
7 //...
8
9 //从这里可以看出@ModelAttribute能够修饰handler的入参
10 String name = ModelFactory.getNameForParameter(parameter){
11 ModelAttribute annot = parameter.getParameterAnnotation(ModelAttribute.class);
12 //如果当前参数使用了@ModelAttribute标注,则获取到该标签的value属性值
13 String attrName = (annot != null) ? annot.value() : null;
14 //如果attrName有text,则返回该attrName值,也就是@ModelAttribute的value属性值。
15 //反之,则返回参数类型第一个字母小写后得到的字符串
16 return StringUtils.hasText(attrName) ? attrName : Conventions.getVariableNameForParameter(parameter);
17 };
18
19 //检测mavContainer中是否包含有name关键字的ModelAndView,如果包含有,则获取它并返回。如果没有,则创建一个attribute
20 Object attribute = (mavContainer.containsAttribute(name) ?
21 mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, webRequest));
22
23 //对于WebDataBinder而言,最重要的参数就是name和attribute
24 //将请求域中表单数据绑定到上面的到的attribute中:request域中有的就重新赋值,没有的就保持原有的属性值不变。
25 /*
26 * 结合前面name和attribute的获取过程可以分析出request表单数据绑定的过程:
27 * ①、如果handler的入参使用了@ModelAttribute,同时还指定了其value属性值,那么attrName就是其value属性值;
28 * ②、如果handler的入参处没有指定@ModelAttribute的value属性值,或者是根本就没有使用该注解,那么其
29 * attrName就是参数类名第一个字母小写的到;
30 * ③、搜索mavContainer中是否有键值为attrName的attribute对象,如果有,则将这个对象作为表单数据绑定的对象;
31 * 如果没有,则新创建一个作为数据绑定的对象;
32 */
33 WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
34 if (binder.getTarget() != null) {
35 //绑定参数
36 bindRequestParameters(binder, webRequest);
37 validateIfApplicable(binder, parameter);
38 if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
39 throw new BindException(binder.getBindingResult());
40 }
41 }
42
43 // Add resolved attribute and BindingResult at the end of the model
44 Map<String, Object> bindingResultModel = binder.getBindingResult().getModel();
45 //删除原有的attribute
46 mavContainer.removeAttributes(bindingResultModel);
47 //将处理过的attribute添加到末尾,这样mavContainer中相对应的attribute就是更新以后的attribute
48 mavContainer.addAllAttributes(bindingResultModel);
49
50
51 //返回需要格式的args
52 return binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
53 };
54
55 //**真正的调用处理方法,此时的args是依据request表单数据更新过的args
56 Object returnValue = doInvoke(args);
57
58 //返回处理方法返回的结果,这个结果将会进一步处理
59 return returnValue;
60 };
61
62 //二、处理目标方法的返回结果
63 //设置应答状态
64 setResponseStatus(webRequest);
65
66 if (returnValue == null) {
67 if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {
68 mavContainer.setRequestHandled(true);
69 return;
70 }
71 }
72 else if (StringUtils.hasText(this.responseReason)) {
73 mavContainer.setRequestHandled(true);
74 return;
75 }
76
77 mavContainer.setRequestHandled(false);
78 try {
79 //处理返回结果
80 this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest){
81 //获取返回结果对应的处理方法
82 HandlerMethodReturnValueHandler handler = getReturnValueHandler(returnType);
83 Assert.notNull(handler, "Unknown return value type [" + returnType.getParameterType().getName() + "]");
84 //利用获取到的结果处理方法来处理结果
85 handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest){
86 if (returnValue == null) {
87 return;
88 }
89 else if (returnValue instanceof String) {
90 String viewName = (String) returnValue;
91 mavContainer.setViewName(viewName);
92 if (isRedirectViewName(viewName)) {
93 mavContainer.setRedirectModelScenario(true);
94 }
95 }
96 else {
97 // should not happen
98 throw new UnsupportedOperationException("Unexpected return type: " +
99 returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
100 }
101 };
102 };
103 }
104
105 };

  从上面的代码分析可以得出表单数据绑定的流程:

  1、request的表单数据绑定首先需要创建一个WebDataBinder对象: binder = binderFactory.createBinder(webRequest, attribute, name)。表单数据在webRequest中,还要确定两个关键的参数:attribute【Object类型】, name【String类型】。

  2、确定name(也就是attrName):

    ①、如果handler的入参处使用了@ModelAttribute注解,同时该注解还制定了value属性值,那么name就是value的属性值;

    ②、如果handler的入参数使用了@ModelAttribute注解,但是没有指定value属性值;或者是,入参处根本就没有使用@ModelAttribute注解;那么这2种情况下其name值就是handler入参类名第一个字母小写得到的String;

  3、确定attribute:

    ①、查看mavContainer中是否包含有key=name的attribute对象(mavContainer.getModel()实际上得到的是一个Map<String, Object>)。如果有,则attribute就是该对象;如果没有,则新创建一个对象赋给attribute。其代码如下:

    Object attribute = (mavContainer.containsAttribute(name) ? mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, webRequest));

  4、通过已经确定好的attribute和name就能够完成数据的绑定了。

  step6: 返回ModelAndView

 if (asyncManager.isConcurrentHandlingStarted()) {
return null;
} return getModelAndView(mavContainer, modelFactory, webRequest);

  返回有两种情况,第一个貌似和同步机制有关,asyncManager的工作机制后续继续分析。这里主要是分析getModelAndView(mavContainer, modelFactory, webRequest)方法的源码。

 private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception { modelFactory.updateModel(webRequest, mavContainer);
if (mavContainer.isRequestHandled()) {
return null;
}
ModelMap model = mavContainer.getModel();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model);
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
return mav;
}

  视图模型是一个大的主题,后面再仔细分析。

  

7、SpringMVC源码分析(2):分析HandlerAdapter.handle方法,了解handler方法的调用细节以及@ModelAttribute注解的更多相关文章

  1. springMvc源码学习之:spirngMVC获取请求参数的方法2

    @RequestParam,你一定见过:@PathVariable,你肯定也知道:@QueryParam,你怎么会不晓得?!还有你熟悉的他 (@CookieValue)!她(@ModelAndView ...

  2. springMvc源码学习之:spirngMvc获取请求参数的方法

    一.      通过@PathVariabl获取路径中的参数 @RequestMapping(value="user/{id}/{name}",method=RequestMeth ...

  3. springMVC源码学习之addFlashAttribute源码分析

    本文主要从falshMap初始化,存,取,消毁来进行源码分析,springmvc版本4.3.18.关于使用及验证请参考另一篇jsp取addFlashAttribute值深入理解即springMVC发r ...

  4. springMVC源码学习地址

    springmvc工作原理以及源码分析(基于spring3.1.0) 感谢作者  宏愿, 在此记录下,以便学习 SpringMVC源码分析(1):分析DispatcherServlet.doDispa ...

  5. SpringMVC源码阅读系列汇总

    1.前言 1.1 导入 SpringMVC是基于Servlet和Spring框架设计的Web框架,做JavaWeb的同学应该都知道 本文基于Spring4.3.7源码分析,(不要被图片欺骗了,手动滑稽 ...

  6. springMVC源码分析--HandlerAdapter(一)

    HandlerAdapter的功能实际就是执行我们的具体的Controller.Servlet或者HttpRequestHandler中的方法. 类结构如下:

  7. 8、SpringMVC源码分析(3):分析ModelAndView的形成过程

    首先,我们还是从DispatcherServlet.doDispatch(HttpServletRequest request, HttpServletResponse response) throw ...

  8. springMVC源码分析--RequestMappingHandlerAdapter(五)

    上一篇博客springMVC源码分析--HandlerAdapter(一)中我们主要介绍了一下HandlerAdapter接口相关的内容,实现类及其在DispatcherServlet中执行的顺序,接 ...

  9. springMVC源码分析--HttpRequestHandlerAdapter(四)

    上一篇博客springMVC源码分析--HandlerAdapter(一)中我们主要介绍了一下HandlerAdapter接口相关的内容,实现类及其在DispatcherServlet中执行的顺序,接 ...

随机推荐

  1. [C/C++] 各种C/C++编译器对UTF-8源码文件的兼容性测试(VC、GCC、BCB)

    在不同平台上开发C/C++程序时,为了避免源码文件乱码,得采用UTF-8编码来存储源码文件.但是很多编译器对UTF-8源码文件兼容性不佳,于是我做了一些测试,分析了最佳保存方案. 一.测试程序 为了测 ...

  2. 开源免费的C/C++网络库(c/c++ sockets library)

    (1)ACE 庞大.复杂,适合大型项目.开源.免费,不依赖第三方库,支持跨平台. http://www.cs.wustl.edu/~schmidt/ACE.html (2)Asio Asio基于Boo ...

  3. Android Studio笔记(2)——快捷键

    在朋友推荐下,上个星期黄老师我用上了Google的新黑暗工具,基于Intellij idea的新Android开发集成开发环境 ——Android Studio,用下来感觉还算不错,但作为一个ADT ...

  4. java之classpath到底是什么

    如果你输入一个命令,比如java那么系统是如何找到这个命令的呢?按照顺序,系统先在当前目录搜索是否有java.exe, java.bat 等. 如果没有,就得到系统的PATH(不区分大小写)里面查找. ...

  5. range,shuffle,str_shuffle

    print_r(range(1,20)); 输出,range产生 Array( [0] => 1 [1] => 2 [2] => 3 [3] => 4 [4] => 5 ...

  6. 还原没有日志文件的SQL数据库

    1.执行命令示例: EXEC sys.sp_attach_db @dbname = 'AdventrueWorks2012_Data',     @filename1 = N'C:\Program F ...

  7. 【软件工程-Teamwork 3】团队角色分配和团队贡献分分配规则

    Part 1 团队角色分配 1.人员分配概要: Project Manager:1名 / Developer:4名 / Test: 1名 2.具体人员分配及职责: Project Manager(PM ...

  8. How to use Android Activity's finish(), onDestory() and System.exit(0) methods

    Activity.finish() Calling this method will let the system know that the programmer wants the current ...

  9. 【转】解析Java finally

    下文写的关于Java中的finally语句块什么时候执行的问题.什么时候执行呢?和return.continue.break.exit都有关系,尤其return语句非常有意思,于是分享给大家.谢谢Sm ...

  10. HDU 4162 Shape Number

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4162 题意: 求给定字符的一阶差分链的最小表示. 题解: 先求一阶差分链,再求一阶差分链的最小表示法 ...