程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> SpringMVC源碼解析,springmvc源碼

SpringMVC源碼解析,springmvc源碼

編輯:JAVA綜合教程

SpringMVC源碼解析,springmvc源碼


HandlerMethod及子類主要用於封裝方法調用相關信息,子類還提供調用,參數准備和返回值處理的職責.

分析下各個類的職責吧(順便做分析目錄):

HandlerMethod 封裝方法定義相關的信息,如類,方法,參數等.

  使用場景:HandlerMapping時會使用

InvocableHandlerMethod 添加參數准備,方法調用功能

  使用場景:執行使用@ModelAttribute注解會使用

ServletInvocableHandlerMethod 添加返回值處理職責,ResponseStatus處理

  使用場景:執行http相關方法會使用,比如調用處理執行

 

1. HandlerMethod

 HandlerMethod其實可以簡單理解為保持方法信息的pojo.

所以這邊主要就是看下定義的屬性:

 1 package org.springframework.web.method;
 2 public class HandlerMethod {
 3     /** 什麼鬼,給子類提供logger,到現在為止源碼中不多見 */
 4     protected final Log logger = LogFactory.getLog(HandlerMethod.class);
 5     // 方法所在的類,如果是String類型,可以去容器中獲取
 6     private final Object bean;
 7     // 方法
 8     private final Method method;
 9     // 類管理的容器
10     private final BeanFactory beanFactory;
11     // 方法的參數
12     private final MethodParameter[] parameters;
13     // 如果方法是bridged方法,則對應原始方法
14     private final Method bridgedMethod;
15     // ...
16 }


大部分應該是看看注釋就能理解了,我們解釋下下面:

  這邊所有的熟悉都是final類型的,不可修改,所以如果出現修改需要new.

  如果bean是string,是在createWithResolvedBean找容器獲取實例的.

  MethodParameter類封裝了參數相關的信息.

  提供獲取返回值,判斷是否void類型,還有讀取注解

 

createWithResolvedBean邏輯其實很簡單:

確認下如果bean是String類型的,那麼從容器BeanFactory中獲取,並使用private HandlerMethod(HandlerMethod handlerMethod, Object handler) new一個新的.

1 // HandlerMethod
2     public HandlerMethod createWithResolvedBean() {
3         Object handler = this.bean;
4         if (this.bean instanceof String) {
5             String beanName = (String) this.bean;
6             handler = this.beanFactory.getBean(beanName);
7         }
8         return new HandlerMethod(this, handler);
9     }

 

MethodParameter,可以根據method方法和parameterIndex(參數下標)唯一確定,其他屬性都可以根據他們兩獲取.

由於反射中沒有參數名的信息,而這邊需要,所以Spring添加了一個參數名查找器ParameterNameDiscover.

這邊返回值的類型是存儲在parameters屬性中的,下標用-1區分.

MethodParameter在HandlerMethod有兩個內部類的子類.

 1 package org.springframework.core;
 2 public class MethodParameter {
 3     // 參數所在方法
 4     private final Method method;
 5     // 參數的構造方法
 6     private final Constructor constructor;
 7     // 參數下標
 8     private final int parameterIndex;
 9     // 參數類型
10     private Class<?> parameterType;
11     // Type類型的參數類型
12     private Type genericParameterType;
13     // 參數使用的注解
14     private Annotation[] parameterAnnotations;
15     // 參數名查找器
16     private ParameterNameDiscoverer parameterNameDiscoverer;
17     // 參數名
18     private String parameterName;
19     // 參數嵌套級別,如Map<String>中map為1,string為2
20     private int nestingLevel = 1;
21     // 每層的下標
22     /** Map from Integer level to Integer type index */
23     Map<Integer, Integer> typeIndexesPerLevel;
24     // 什麼鬼?就一個構造方法用了,其他都沒有使用
25     Map<TypeVariable, Type> typeVariableMap;
26     // ... 
27 }

 

 

2. InvocableHandlerMethod

習慣性的,看到Invocable,會想到Spring會不會定義一個接口來定義可調用概念,不過沒有.

這邊添加了2個職責:參數准備和方法執行.

參數准備委托HandlerMethodArgumentResolver進行具體的解析.解析的時候需要用到WebDataBinder,所以順便帶上.對參數解析器有興趣可以移步這裡

 

2.1 先來看看參數准備工作部分吧.

查找某個參數值的邏輯:

  a, 先委托參數名查找器獲取參數名

  b,從外部提供的參數清單中查找值(竟然是根據類型判斷的)

  c,如果沒有直接提供,使用參數解析器創建

  d,如果還是沒有獲得,直接報錯

 1 package org.springframework.web.method.support;
 2 
 3 public class InvocableHandlerMethod extends HandlerMethod {
 4     // 參數解析器
 5     private HandlerMethodArgumentResolverComposite argumentResolvers = new HandlerMethodArgumentResolverComposite();
 6     // 參數解析器需要用到
 7     private WebDataBinderFactory dataBinderFactory;
 8     // 參數名查找器
 9     private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
10 
11     private Object[] getMethodArgumentValues(
12             NativeWebRequest request, ModelAndViewContainer mavContainer,
13             Object... providedArgs) throws Exception {
14 
15         MethodParameter[] parameters = getMethodParameters();
16         Object[] args = new Object[parameters.length];
17         for (int i = 0; i < parameters.length; i++) {
18             MethodParameter parameter = parameters[i];
19             parameter.initParameterNameDiscovery(parameterNameDiscoverer);
20             // 查找參數名
21             GenericTypeResolver.resolveParameterType(parameter, getBean().getClass());
22             // 從提供的參數值providedArgs中找值
23             args[i] = resolveProvidedArgument(parameter, providedArgs);
24             if (args[i] != null) {
25                 continue;
26             }
27             // 使用參數解析器解析
28             if (argumentResolvers.supportsParameter(parameter)) {
29                 try {
30                     args[i] = argumentResolvers.resolveArgument(parameter, mavContainer, request, dataBinderFactory);
31                     continue;
32                 } catch (Exception ex) {
33                     throw ex;
34                 }
35             }
36             // 參數獲取不到是需要報錯的
37             if (args[i] == null) {
38                 String msg = getArgumentResolutionErrorMessage("No suitable resolver for argument", i);
39                 throw new IllegalStateException(msg);
40             }
41         }
42         return args;
43     }
44 
45     private Object resolveProvidedArgument(MethodParameter parameter, Object... providedArgs) {
46         if (providedArgs == null) {
47             return null;
48         }
49         for (Object providedArg : providedArgs) {
50             // 竟然是根據類型判斷的
51             if (parameter.getParameterType().isInstance(providedArg)) {
52                 return providedArg;
53             }
54         }
55         return null;
56     }
57 
58     // ...
59 
60 }

 2.2 方法執行

這邊的邏輯其實很簡單:

  委托獲取方法執行需要的參數

  強制將方法變為可用

  處理方法執行過程中的異常

 

 1 package org.springframework.web.method.support;
 2 
 3 public class InvocableHandlerMethod extends HandlerMethod {
 4     // ... 
 5     public final Object invokeForRequest(NativeWebRequest request,
 6                                          ModelAndViewContainer mavContainer,
 7                                          Object... providedArgs) throws Exception {
 8         Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
 9         Object returnValue = invoke(args);
10         return returnValue;
11     }
12 
13     private Object invoke(Object... args) throws Exception {
14         ReflectionUtils.makeAccessible(this.getBridgedMethod());
15         try {
16             return getBridgedMethod().invoke(getBean(), args);
17         }
18         catch (IllegalArgumentException e) {
19             String msg = getInvocationErrorMessage(e.getMessage(), args);
20             throw new IllegalArgumentException(msg, e);
21         }
22         catch (InvocationTargetException e) {
23             // Unwrap for HandlerExceptionResolvers ...
24             Throwable targetException = e.getTargetException();
25             if (targetException instanceof RuntimeException) {
26                 throw (RuntimeException) targetException;
27             }
28             else if (targetException instanceof Error) {
29                 throw (Error) targetException;
30             }
31             else if (targetException instanceof Exception) {
32                 throw (Exception) targetException;
33             }
34             else {
35                 String msg = getInvocationErrorMessage("Failed to invoke controller method", args);
36                 throw new IllegalStateException(msg, targetException);
37             }
38         }
39     }
40 
41 }

 

 

3. ServletInvocableHandlerMethod

  委托HandlerMethodReturnValueHandler添加返回值處理功能

  添加@ResponseStatus注解支持.

 

這邊使用的@ResponseStatus注解兩個屬性:value狀態碼,reason 寫入response的說明文字

 

3.1 設置response status時的邏輯:

  responseStatus沒設置就返回

  responseReason存在則進入error

  把responseStatus設置到request,RedirectView需要使用 

 1 // ServletInvocableHandlerMethod
 2     private void setResponseStatus(ServletWebRequest webRequest) throws IOException {
 3         if (this.responseStatus == null) {
 4             return;
 5         }
 6 
 7         if (StringUtils.hasText(this.responseReason)) {
 8             webRequest.getResponse().sendError(this.responseStatus.value(), this.responseReason);
 9         }
10         else {
11             webRequest.getResponse().setStatus(this.responseStatus.value());
12         }
13 
14         // to be picked up by the RedirectView
15         webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, this.responseStatus);
16     }

3.2 invokeAndHandle

  委托父類執行請求

  添加ResponseStatus支持

  然後判斷怎麼樣才是執行完畢,滿足一下任意一個:

    request的notModified為真,使用@ResponseStatus注解,mavContainer的requestHandled為真

  委托HandlerMethodReturnValueHandler封裝返回值

 1 // ServletInvocableHandlerMethod
 2     public final void invokeAndHandle(ServletWebRequest webRequest,
 3             ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
 4 
 5         Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
 6 
 7         setResponseStatus(webRequest);
 8 
 9         if (returnValue == null) {
10             if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {
11                 mavContainer.setRequestHandled(true);
12                 return;
13             }
14         }
15         else if (StringUtils.hasText(this.responseReason)) {
16             mavContainer.setRequestHandled(true);
17             return;
18         }
19 
20         mavContainer.setRequestHandled(false);
21 
22         try {
23             this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
24         }
25         catch (Exception ex) {
26             if (logger.isTraceEnabled()) {
27                 logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
28             }
29             throw ex;
30         }
31     }

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved