SpringMVC作為一個MVC框架,有控制層,當(dāng)我們在瀏覽器發(fā)出了一個請求,SpringMVC是怎么處理請求,而且通過請求找到對應(yīng)的類的方法?我們今天帶著這么問題來解析SpringMVC源代碼處理過程。
我們在實(shí)現(xiàn)SpringMVC控制層時,標(biāo)示了請求路徑,并標(biāo)示請求地址對應(yīng)的哪個方法,源代碼如下:
- @Controller
- @RequestMapping(value="/test")
- public class TestController2 {
- @Autowired
- private TestService testService;
-
- @RequestMapping(value="/index")
- public ModelAndView getIndex(Model model){
- ModelAndView mv = new ModelAndView();
- return mv;
- }
- }
注解@RequestMapping是處理方法的映射。我們在類上面注解和方法上注解這樣會更加的清晰,我們在類上標(biāo)示更能清晰的知道這個路徑是請求這個類,并在方法上注解比較清楚的是請求哪個方法。例如:http://127.0.0.1:8080/test/index.jhtml。如圖所示:
我們先介紹兩個比較重要的組件HandlerMapping和HandlerAdapter是@Contoller和@RequestMapping注解的處理器, HandlerMapping是處理請求映射的處理器;HandlerAdapter適配器處理器(動態(tài)調(diào)用方法和處理參數(shù))。我們在XML配置文件中進(jìn)行配置這兩種處理器。代碼如下:
- <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
- <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
一:我們通過解析SpringMVC處理請求深度解析,并介紹HandlerMapping映射處理器
我們講到這個這個XML配置,找到@RequestMapping和@Controller并封裝成RequestMappingInfo,為后面我們解析處理請求會比較清晰,我在這在補(bǔ)充一下,我們在初始化Bean時我們在上一篇有介紹過,對@RequestMapping注解處理這部分我們沒介紹,所以我在這里在補(bǔ)充一下,RequestMappingHandlerMapping間接實(shí)現(xiàn)了InitializingBean接口,如圖所示:
RequestMappingHandlerMapping間接實(shí)現(xiàn)了InitializingBean接口重寫了afterPropertiesSet方法,初始化RequestMappingHandlerMapping時,會調(diào)用afterPropertiesSet方法,跟 <bean class="" init-method=""/>屬性init-method處理一樣。afterPropertiesSet調(diào)用了RequestMappingHandlerMapping的initHandlerMethods實(shí)現(xiàn)的。處理@RequestMapping的,我們這邊來分析一下它是怎么實(shí)現(xiàn)的。源代碼:
- protected void initHandlerMethods() {
- String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
- BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
- getApplicationContext().getBeanNamesForType(Object.class));
- for (String beanName : beanNames) {
- if (isHandler(getApplicationContext().getType(beanName))){
- <span style="color:#cc0000;">detectHandlerMethods(beanName);</span>
- }
- }
- handlerMethodsInitialized(getHandlerMethods());
- }
-
- @Override
- protected boolean isHandler(Class<?> beanType) {
- return ((AnnotationUtils.findAnnotation(beanType, <span style="color:#cc0000;">Controller.class</span>) != null) ||
- (AnnotationUtils.findAnnotation(beanType, <span style="color:#cc0000;">RequestMapping.class</span>) != null));
- }
說明:
(1)isHandler這個方法是判斷是否被@Controller和@RequestMapping標(biāo)記
(2)如果有被@Controller和@RequestMapping標(biāo)記,然后生成RequestMappingInfo實(shí)例注冊到緩存中,供我們在請求時通過URL能匹配找到。
我們來看怎么生成RequestMappingInfo實(shí)例注冊到緩存,由detectHandlerMethods這個方法實(shí)現(xiàn)的。源代碼如下:
- protected void detectHandlerMethods(final Object handler) {
- Class<?> handlerType =
- (handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());
-
- final Map<Method, T> mappings = new IdentityHashMap<Method, T>();
- final Class<?> userType = ClassUtils.getUserClass(handlerType);
- Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
- public boolean matches(Method method) {
- T mapping = <span style="color:#cc0000;">getMappingForMethod(method, userType);</span>
- if (mapping != null) {
- mappings.put(method, mapping);
- return true;
- }
- else {
- return false;
- }
- }
- });
-
- for (Method method : methods) {
- //注冊到緩存中
- <span style="color:#cc0000;">registerHandlerMethod(handler, method, mappings.get(method))</span>;
- }
- }
-
-
- @Override
- protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
- RequestMappingInfo info = null;
- //查找該類下注解的@RequestMapping的所有方法
- RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);
- if (methodAnnotation != null) {
- RequestCondition<?> methodCondition = getCustomMethodCondition(method);
- //創(chuàng)建RequestMappingInfo
- info = createRequestMappingInfo(methodAnnotation, methodCondition);
- //對類進(jìn)行查找有沒有標(biāo)示@RequestMapping注解
- RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
- if (typeAnnotation != null) {
- RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType);
- //成成RequestMappingInfo。類別和方法級別的RequestMapping注解進(jìn)行組合
- info = <span style="color:#ff0000;">createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);</span>
- }
- }
- return info;
- }
- //設(shè)置RequestMappingInfo的屬性然后創(chuàng)建<span style="font-family: 宋體;">RequestMappingInfo</span>
- protected RequestMappingInfo createRequestMappingInfo(RequestMapping annotation, RequestCondition<?> customCondition) {
- String[] patterns = resolveEmbeddedValuesInPatterns(annotation.value());
- return new RequestMappingInfo(
- new PatternsRequestCondition(patterns, getUrlPathHelper(), getPathMatcher(),
- this.useSuffixPatternMatch, this.useTrailingSlashMatch, this.fileExtensions),
- new RequestMethodsRequestCondition(annotation.method()),
- new ParamsRequestCondition(annotation.params()),
- new HeadersRequestCondition(annotation.headers()),
- new ConsumesRequestCondition(annotation.consumes(), annotation.headers()),
- new ProducesRequestCondition(annotation.produces(), annotation.headers(), getContentNegotiationManager()),
- customCondition);
- }
當(dāng)我們在瀏覽器發(fā)送了http://127.0.0.1:8080/test/index.jhtml這樣的請求,SpringMVC注冊在web.xml中的前端轉(zhuǎn)發(fā)器DispatcherServlet接收,在這個之前,我們對initHandlerMappings和initHandlerAdapters初始化,這個在上一篇有介紹過。接下來我們開始分析DispatcherServlet處理請求。
DispatcherServlet是間接的繼承了HttpSevlet,由父類FrameworkServlet實(shí)現(xiàn)了doPost和doGet方法,然后在調(diào)用子類,DispatcherServlet的doDispatch方法處理請求,實(shí)現(xiàn)了設(shè)計(jì)原則接口隔離原則。請求的包含了一些頭部的信息等,如圖所示:
doDispatch方法的源代碼如下:
- protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
- HttpServletRequest processedRequest = request;
- //判斷是否是文件流請求
- processedRequest = checkMultipart(request);
- //獲取了映射處理器,里面是通過請求的URL獲取對應(yīng)的類并獲取實(shí)例化的Bean,包裝成HandlerMethod
- mappedHandler = <span style="color:#990000;">getHandler(processedRequest, false);</span>
- if (mappedHandler == null || mappedHandler.getHandler() == null) {
- noHandlerFound(processedRequest, response);
- return;
- }
- //獲取HandlerAdapter
- HandlerAdapter ha = 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 (logger.isDebugEnabled()) {
- String requestUri = urlPathHelper.getRequestUri(request);
- logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
- }
- if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
- return;
- }
- }
- try {
- mv = <span style="color:#cc0000;">ha.handle(processedRequest, response, mappedHandler.getHandler());</span>
- }
- applyDefaultViewName(request, mv);
- mappedHandler.applyPostHandle(processedRequest, response, mv);
- processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
- }
- }
-
-
- protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
- for (HandlerMapping hm : this.handlerMappings) {
- HandlerExecutionChain handler = <span style="color:#990000;">hm.getHandler(request);</span>
- if (handler != null) {
- return handler;
- }
- }
- return null;
- }
說明:
(1)Spring3.1開始的版本,建議使用RequestMappingHandlerMapping和RequestMappingHandlerAdapter,所以我們在XML配置了這個Bean組件。 List<HandlerMapping> handlerMappings里面存放的是映射處理器,Spring內(nèi)置了很多映射處理器,例如SimpleUrlHandlerMapping、BeanNameUrlHandlerMapping等,如圖所示:
(2)HandlerExecutionChain包含了處理該請求的處理器,還包含一系列可以攔截請求的攔截器。
RequestMappingHandlerMapping也是繼承了AbstractHandlerMapping,getHandler具體實(shí)現(xiàn)是由AbstractHandlerMapping來實(shí)現(xiàn)的,源代碼如下:
- public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
- Object handler = getHandlerInternal(request);
- if (handler == null) {
- handler = getDefaultHandler();
- }
- if (handler == null) {
- return null;
- }
- // Bean name or resolved handler?
- if (handler instanceof String) {
- String handlerName = (String) handler;
- handler = getApplicationContext().getBean(handlerName);
- }
- return <span style="color:#990000;">getHandlerExecutionChain(handler, request);</span>
- }
說明:
(1)getHandlerInternal方法是處理映射的,獲取request獲取了請求路徑,然后找到對應(yīng)的RequestMappingInfo獲取了Controller類,并找到了對應(yīng)的方法。
(2)HandlerExecutionChain帶了一系列的interceptors
第一:getHandlerInternal方法是通過URL找到對應(yīng)的處理映射的,并找到對應(yīng)的Bean實(shí)例,我們通過源代碼分析是怎么處理的?
getHandlerInternal方法源代碼如下:
- protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
- //通過UrlPathHelper獲取request獲取了請求路徑 例如:test/index.jhtml
- String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
- if (logger.isDebugEnabled()) {
- logger.debug("Looking up handler method for path " + lookupPath);
- }
- //
- HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
- if (logger.isDebugEnabled()) {
- if (handlerMethod != null) {
- logger.debug("Returning handler method [" + handlerMethod + "]");
- }
- else {
- logger.debug("Did not find handler method for [" + lookupPath + "]");
- }
- }
- //返回對handlerMethod 進(jìn)行設(shè)置已經(jīng)初始化Bean并設(shè)置屬性的handlerMethod
- return (handlerMethod != null ? handlerMethod.<span style="color:#990000;">createWithResolvedBean</span>() : null);
- }
- //對bean進(jìn)行初始化
- public HandlerMethod createWithResolvedBean() {
- Object handler = this.bean;
- if (this.bean instanceof String) {
- String beanName = (String) this.bean;
- //獲取對應(yīng)的Bean
- handler = this.beanFactory.getBean(beanName);
- }
- return new HandlerMethod(this, handler);
- }
- //設(shè)置bean、還有beanFactory 、method、parameters 等屬性
- private HandlerMethod(HandlerMethod handlerMethod, Object handler) {
- Assert.notNull(handlerMethod, "HandlerMethod is required");
- Assert.notNull(handler, "Handler object is required");
- this.bean = handler;
- this.beanFactory = handlerMethod.beanFactory;
- this.method = handlerMethod.method;
- this.bridgedMethod = handlerMethod.bridgedMethod;
- this.parameters = handlerMethod.parameters;
- }
說明:
(1)UrlPathHelper是分析請求的URL,LookupPathForRequest(request)這個方法中有alwaysUseFullPath默認(rèn)是false使用相對路徑。
(2)lookupHandlerMethod通過URL查看映射到哪個方法和類,MultiValueMap<String, T> urlMap 存放的key是url,value是RequestMappingInfo信息(params等),通過lookupPath查找對應(yīng)的RequestMappingInfo,然后通過RequestMappingInfo到Map<T, HandlerMethod> handlerMethods查找對應(yīng)的HandlerMethod,并返回。MultiValueMap<String, T> urlMap這個緩存中是我們在最開始時有介紹,處理@RequestMapping和@Controll 并封裝成RequestMappingInfo并放到緩存,如圖所示:
如果查找對應(yīng)的方法時,放到Match,里面有包含的HandlerMethod,如圖所示:
然后通過HandlerMethod的createWithResolvedBean方法實(shí)現(xiàn)了通過beanName獲取已經(jīng)初始化的 Bean。然后重新初始化HandlerMethod對象,并設(shè)置bean、還有beanFactory 、method、parameters 等屬性。
第二:HandlerExecutionChain 包含了一系列攔截器。會在調(diào)用Controller類對應(yīng)方法之前、處理完方法還沒返回視圖、返回視圖之后,這些動態(tài)加以攔截。
HandlerExecutionChain這個類屬性很很多添加一系列的攔截器,源代碼如下:
- public class HandlerExecutionChain {
- private HandlerInterceptor[] interceptors;
- private List<HandlerInterceptor> interceptorList;
- }
getHandler具體實(shí)現(xiàn)是由AbstractHandlerMapping中,在這個 方法中實(shí)現(xiàn)了加入了攔截器,我們在看一下我們怎么加入攔截器,源代碼如下:
- protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
- HandlerExecutionChain chain =
- (handler instanceof HandlerExecutionChain) ?
- (HandlerExecutionChain) handler : new HandlerExecutionChain(handler);
- //添加攔截器
- <span style="color:#990000;">chain.addInterceptors(getAdaptedInterceptors());</span>
- String lookupPath = urlPathHelper.getLookupPathForRequest(request);
- for (MappedInterceptor mappedInterceptor : mappedInterceptors) {
- if (mappedInterceptor.matches(lookupPath, pathMatcher)) {
- chain.addInterceptor(mappedInterceptor.getInterceptor());
- }
- }
-
- return chain;
- }
- }
說明:
我們在XML里沒配置自己的攔截器,所以這邊都是為空的。
HandlerInterceptor攔截器接口,里面有三個方法:
(1)preHandle方法:請求處理之前執(zhí)行的這個方法,在Controller方法調(diào)用之前調(diào)用。例如:調(diào)用之前判斷是否有登陸。
(2)postHandle方法: 請求進(jìn)行處理之后,在Controller 方法調(diào)用之后執(zhí)行,會在DispatcherServlet 調(diào)用ModelView視圖之前調(diào)用。
(3)afterCompletion方法:是在DispatcherServlet 調(diào)用ModelView視圖之后調(diào)用。
既然HandlerInterceptor是接口,我們可以自己實(shí)現(xiàn)一個類實(shí)現(xiàn)這個接口,這樣我們就自己定義自己的攔截器,然后加到SpringMVC攔截中?當(dāng)然可以。
我們自己定義了一個類實(shí)現(xiàn)了HandlerInterceptor 接口,例如:public class myInterceptor implements HandlerInterceptor 然后重寫了這個3個方法。我們在XML配置這個類,把自己定義的攔截器加到SpringMVC攔截中。在配置文件加入了
- <span style="color:#990000;"><beans xmlns:mvc="http://www.springframework.org/schema/mvc"
- xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"></span>
- <mvc:interceptors>
- <mvc:interceptor>
- <!--攔截哪個包下的類例如:-->
- <mvc:mapping path="/test/*"/>
- <bean class="test.myInterceptor "></bean>
- </mvc:interceptor>
- </mvc:interceptors>
這樣就把我們定義好的攔截器加到SpringMVC的攔截器中。
到這里,我們對
HandlerMapping映射處理器介紹完了,
二:我們通過解析SpringMVC處理請求深度解析,并介紹HandlerAdapter適配器處理器(動態(tài)調(diào)用方法和處理參數(shù))
HandlerAdapter處理HandlerMethod映射并返回了視圖和數(shù)據(jù)的對象。getHandlerAdapter獲取了我們在配置文件的如圖所示:
父類AbstractHandlerMethodAdapter實(shí)現(xiàn)的,我們先看一下 繼承關(guān)系,這種開封閉原則。如圖所示:
我們來看一下這個handle(processedRequest, response, mappedHandler.getHandler());動態(tài)的調(diào)用方法和處理參數(shù)的具體實(shí)現(xiàn)。源代碼如下:
- public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
- throws Exception {
- return handleInternal(request, response, (HandlerMethod) handler);
- }
-
- @Override
- protected final ModelAndView handleInternal(HttpServletRequest request,
- HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
-
- if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
- // Always prevent caching in case of session attribute management.
- checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
- }
- else {
- // Uses configured default cacheSeconds setting.
- checkAndPrepare(request, response, true);
- }
-
- // Execute invokeHandlerMethod in synchronized block if required.
- if (this.synchronizeOnSession) {
- HttpSession session = request.getSession(false);
- if (session != null) {
- Object mutex = WebUtils.getSessionMutex(session);、
- synchronized (mutex) {
- return invokeHandleMethod(request, response, handlerMethod);
- }
- }
- }
- //動態(tài)的調(diào)用方法和處理參數(shù)
- return invokeHandleMethod(request, response, handlerMethod);
- }
說明:
通過HandlerAdapter動態(tài)的調(diào)用方法和處理參數(shù),調(diào)用方法。我們這邊具體怎么動態(tài)調(diào)用方法和處理參數(shù),并返回視圖,等下一章在具體的介紹,這里涉及也比較多。
總結(jié):
(1) 當(dāng)我們在瀏覽器發(fā)送了http://127.0.0.1:8080/test/index.jhtml這樣的請求,SpringMVC注冊在web.xml中的前端轉(zhuǎn)發(fā)器DispatcherServlet接收時。
(2)通過URL查看映射到哪個方法和類,MultiValueMap<String, T> urlMap 存放的key是url,value是RequestMappingInfo信息(params等),RequestMappingInfo獲取了Controller類,并找到了對應(yīng)的方法。并包裝返回了HandlerMethod。
(3)通過BeanName,到工廠獲取已經(jīng)初始化的Bean,然后重新初始化HandlerMethod對象,并設(shè)置bean、還有beanFactory 、method、parameters 等屬性。
(4)對HandlerExecutionChain添加攔截器和handler然后返回HandlerExecutionChain
(5)HandlerAdapter對HandlerExecutionChain進(jìn)行動態(tài)的調(diào)用方法會返回ModelAndView。
瀏覽器請求已經(jīng)獲取到了,也找到了對應(yīng)的類和方法,那怎么動態(tài)的請求方法和處理參數(shù)等,執(zhí)行完方法并返回ModelAndView等?,帶著這些問題我們繼續(xù)前進(jìn)。