Spring MVCにおけるHandler、HandlerMappingとHandlerAdapterの関係

47766 ワード

Handler、HandlerMapping、HandlerAdapterはSpring MVCの重要な構成部分です.
ハードラー
Handlerは要求を処理する最小単位であり、最も典型的なHandlerすなわちControllerであり、HandlerはRequestを処理するために用いられ、最終的な処理結果をResonseに書き込む.ControllerにはhandleRequestという方法があります.ここでRequestを処理してModelAndViewに戻ります.
package hello.test;


import org.springframework.ui.ModelMap;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyController implements Controller {

    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ModelMap modelMap = new ModelMap();
        modelMap.put("key", "value");
        return new ModelAndView(new MyView(), modelMap);
    }
}

HandlerMapping
HandlerMappingには要求のURLとhandlerとの関係が保存されていますが、urlとhandlerのキーペアを追加する方法やurlの名前から該当するhandlerを検索する方法が多く含まれています.
BenNameUrlhandlerMappingを例にとると、初期化段階ではSpringは、スキャンされた経路とその対応する処理というURLを格納するbeanを有する.
package org.springframework.web.servlet.handler;

import java.util.ArrayList;
import java.util.List;

import org.springframework.util.StringUtils;

public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {

    /**
     * Checks name and aliases of the given bean for URLs, starting with "/".
     */
    @Override
    protected String[] determineUrlsForHandler(String beanName) {
        List<String> urls = new ArrayList<>();
        //     bean    "/"  ,        URL bean,    
        if (beanName.startsWith("/")) {
            urls.add(beanName);
        }
        String[] aliases = obtainApplicationContext().getAliases(beanName);
        for (String alias : aliases) {
            if (alias.startsWith("/")) {
                urls.add(alias);
            }
        }
        return StringUtils.toStringArray(urls);
    }

}

運転段階では、HandlerMappingは、Dispactch Servletの処理ロジックに、URLから該当する処理URLを検索するbeanの機能を提供し、これが検出されたbeanはこのURLに対応するhandlerである.Displatch Servletこのクラスにはhandle Mappingsという配列変数があります.handle MappingsにはすべてのHandlerMappingが保存されています.その値はSpring MVCで初期化スキャンを行う時に充填されます.以下の種類があります.
  • SimpleUrlHandlerMapping
  • Request MappingHandlerMapping
  • BenNameUrlhandlerMapping
  • SimpleUrlHandlerMapping
  • Welcome PageHandlerMapping
  • このうち、各HandlerMappingには、HandlerMapオブジェクトがそれぞれのタイプのURLとHandlerの対応関係を記録しています.
    Displatch Servlet.javaでhandler関連コードを取得します.
        @Nullable
        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;
        }
    
    以下のコードはAbstractUrlHandlerMapping.javaのURLからHandlerを取得する具体的なロジックです.
    @Nullable
        protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
            // Direct match?
            Object handler = this.handlerMap.get(urlPath);
            if (handler != null) {
                // Bean name or resolved handler?
                if (handler instanceof String) {
                    String handlerName = (String) handler;
                    handler = obtainApplicationContext().getBean(handlerName);
                }
                validateHandler(handler, request);
                return buildPathExposingHandler(handler, urlPath, urlPath, null);
            }
    
            // Pattern match?
            List<String> matchingPatterns = new ArrayList<>();
            for (String registeredPattern : this.handlerMap.keySet()) {
                if (getPathMatcher().match(registeredPattern, urlPath)) {
                    matchingPatterns.add(registeredPattern);
                }
                else if (useTrailingSlashMatch()) {
                    if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
                        matchingPatterns.add(registeredPattern +"/");
                    }
                }
            }
    
            String bestMatch = null;
            Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
            if (!matchingPatterns.isEmpty()) {
                matchingPatterns.sort(patternComparator);
                if (logger.isTraceEnabled() && matchingPatterns.size() > 1) {
                    logger.trace("Matching patterns " + matchingPatterns);
                }
                bestMatch = matchingPatterns.get(0);
            }
            if (bestMatch != null) {
                handler = this.handlerMap.get(bestMatch);
                if (handler == null) {
                    if (bestMatch.endsWith("/")) {
                        handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
                    }
                    if (handler == null) {
                        throw new IllegalStateException(
                                "Could not find handler for best pattern match [" + bestMatch + "]");
                    }
                }
                // Bean name or resolved handler?
                if (handler instanceof String) {
                    String handlerName = (String) handler;
                    handler = obtainApplicationContext().getBean(handlerName);
                }
                validateHandler(handler, request);
                String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);
    
                // There might be multiple 'best patterns', let's make sure we have the correct URI template variables
                // for all of them
                Map<String, String> uriTemplateVariables = new LinkedHashMap<>();
                for (String matchingPattern : matchingPatterns) {
                    if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
                        Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
                        Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
                        uriTemplateVariables.putAll(decodedVars);
                    }
                }
                if (logger.isTraceEnabled() && uriTemplateVariables.size() > 0) {
                    logger.trace("URI variables " + uriTemplateVariables);
                }
                return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
            }
    
            // No handler found...
            return null;
        }
    
    HandlerAdapter
    HandlerAdapterは主にhandlerを呼び出して要求を処理し、modelAndViewに戻ります.実際に使用する中にカスタムHandlerAdapterが指定されていない場合はSpringフレームはデフォルトでSimpleController Handler Adapterを提供します.それは、着信したhandlerを利用して、他の2つのパラメータRequestとReponseを処理し、ModelAndViewに戻りました.このModelAndViewは、次のフレームのView Resoloverによって処理されます.
    package org.springframework.web.servlet.mvc;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.springframework.lang.Nullable;
    import org.springframework.web.servlet.HandlerAdapter;
    import org.springframework.web.servlet.ModelAndView;
    
    public class SimpleControllerHandlerAdapter implements HandlerAdapter {
    
        @Override
        // support         HandlerAdapter      Handler
        public boolean supports(Object handler) {
            return (handler instanceof Controller);
        }
    
        @Override
        @Nullable
        public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
                throws Exception {
    
            return ((Controller) handler).handleRequest(request, response);
        }
    
        @Override
        public long getLastModified(HttpServletRequest request, Object handler) {
            if (handler instanceof LastModified) {
                return ((LastModified) handler).getLastModified(request);
            }
            return -1L;
        }
    
    }
    
    
    HandlerAdapterを使用するロジックは、Dispactch Servletクラスと同じであり、このクラスには同じ配列のhandler Adaptersがあります.すべてのシステムのデフォルトまたはプログラミング担当者がカスタマイズしたHandlerAdapterを記録します.doDisplatch()方法では、コードに中国語の注釈を付けました.次に、Handlerを巡回して、すべてのHandlerAdapterの中から処理に適したHandlerAdapterを見つけて、次にHandler Adapterを呼び出して、Handlerを実際的に処理します.
    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);
    
                    // Determine handler for the current request.
                    mappedHandler = getHandler(processedRequest);
                    if (mappedHandler == null) {
                        noHandlerFound(processedRequest, response);
                        return;
                    }
    
                    // Determine handler adapter for the current request.
                    //        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;
                    }
    
                    // Actually invoke the handler.
                    //       ,getHandler()              Controller  
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }
    
                    applyDefaultViewName(processedRequest, mv);
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                }
                ...
            }
            ...
        }
        
    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    if (this.handlerAdapters != null) {
    	for (HandlerAdapter ha : this.handlerAdapters) {
    		if (logger.isTraceEnabled()) {
    			logger.trace("Testing handler adapter [" + ha + "]");
    		}
    		if (ha.supports(handler)) {
    			return ha;
    		}
    	}
    }
    throw new ServletException("No adapter for handler [" + handler +
    		"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
    }