国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

看透 Spring MVC 源代碼分析與實踐 —— 俯視 Spring MVC

fsmStudy / 2190人閱讀

摘要:通過上面的圖,可以看到這三個類直接實現三個接口。如果配置不合法或者需要的參數丟失或者子類初始化發生錯誤,那么就會拋出異常日志代碼刪除了從參數設置屬性。參與了創建工作,并沒有涉及請求的處理。小結本章分析了的請求處理的過程。

Spring MVC


Spring MVC 之初體驗 環境搭建

在 IDEA 中新建一個 web 項目,用 Maven 管理項目的話,在 pom.xml 中加入 Spring MVC 和 Servlet 依賴即可。



    org.springframework
    spring-webmvc
    4.3.9.RELEASE



    javax.servlet
    javax.servlet-api
    3.1.0
    provided
Spring MVC 簡單配置

在 web.xml 中配置 Servlet

創建 Spring MVC 的 xml 配置文件

創建 Controller 和 View

1、web.xml




    spring
    org.springframework.web.servlet.DispatcherServlet
    
    1



    spring
    *.do






   
     org.springframework.web.context.ContextLoaderListener
   





    contextConfigLocation
    classpath:config/applicationContext.xml

2、spring-servlet.xml




    
    
  
    
    

3、Controller

package controller;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import entity.User;

@Controller  //類似Struts的Action
public class TestController {

    @RequestMapping("/test/login.do")  // 請求url地址映射,類似Struts的action-mapping
    public String testLogin(@RequestParam(value="username")String username, String password, HttpServletRequest request) {
        // @RequestParam是指請求url地址映射中必須含有的參數(除非屬性 required=false, 默認為 true)
        // @RequestParam可簡寫為:@RequestParam("username")

        if (!"admin".equals(username) || !"admin".equals(password)) {
            return "loginError"; // 跳轉頁面路徑(默認為轉發),該路徑不需要包含spring-servlet配置文件中配置的前綴和后綴
        }
        return "loginSuccess";
    }

    @RequestMapping("/test/login2.do")
    public ModelAndView testLogin2(String username, String password, int age){
        // request和response不必非要出現在方法中,如果用不上的話可以去掉
        // 參數的名稱是與頁面控件的name相匹配,參數類型會自動被轉換

        if (!"admin".equals(username) || !"admin".equals(password) || age < 5) {
            return new ModelAndView("loginError"); // 手動實例化ModelAndView完成跳轉頁面(轉發),效果等同于上面的方法返回字符串
        }
        return new ModelAndView(new RedirectView("../index.jsp"));  // 采用重定向方式跳轉頁面
        // 重定向還有一種簡單寫法
        // return new ModelAndView("redirect:../index.jsp");
    }

    @RequestMapping("/test/login3.do")
    public ModelAndView testLogin3(User user) {
        // 同樣支持參數為表單對象,類似于Struts的ActionForm,User不需要任何配置,直接寫即可
        String username = user.getUsername();
        String password = user.getPassword();
        int age = user.getAge();

        if (!"admin".equals(username) || !"admin".equals(password) || age < 5) {
            return new ModelAndView("loginError");
        }
        return new ModelAndView("loginSuccess");
    }

    @Resource(name = "loginService")  // 獲取applicationContext.xml中bean的id為loginService的,并注入
    private LoginService loginService;  //等價于spring傳統注入方式寫get和set方法,這樣的好處是簡潔工整,省去了不必要得代碼

    @RequestMapping("/test/login4.do")
    public String testLogin4(User user) {
        if (loginService.login(user) == false) {
            return "loginError";
        }
        return "loginSuccess";
    }
}

@RequestMapping 可以寫在方法上,也可以寫在類上,上面代碼方法上的 RequestMapping 都含有 /test , 那么我們就可以將其抽出直接寫在類上,那么方法里面就不需要寫 /test 了。

如下即可:

@Controller
@RequestMapping("/test")
public class TestController {

    @RequestMapping("/login.do")  // 請求url地址映射,類似Struts的action-mapping
    public String testLogin(@RequestParam(value="username")String username, String password, HttpServletRequest request) {
        // @RequestParam是指請求url地址映射中必須含有的參數(除非屬性 required=false, 默認為 true)
        // @RequestParam可簡寫為:@RequestParam("username")

        if (!"admin".equals(username) || !"admin".equals(password)) {
            return "loginError"; // 跳轉頁面路徑(默認為轉發),該路徑不需要包含spring-servlet配置文件中配置的前綴和后綴
        }
        return "loginSuccess";
    }
    //省略其他的
}

上面的代碼方法的參數中可以看到有一個 @RequestParam 注解,其實還有 @PathVariable 。這兩個的區別是啥呢?

@PathVariable 標記在方法的參數上,利用它標記的參數可以利用請求路徑傳值。

@RequestParam是指請求url地址映射中必須含有的參數(除非屬性 required=false, 默認為 true)

看如下例子:

@RequestMapping("/user/{userId}")  // 請求url地址映射
public String userinfo(Model model, @PathVariable("userId") int userId,  HttpSession session) {
         System.out.println("進入  userinfo  頁面");
        //判斷是否有用戶登錄
        User user1 = (User) session.getAttribute("user");
        if (user1 == null) {
            return "login";
        }
        User user = userService.selectUserById(userId);
        model.addAttribute("user", user);
        return "userinfo";
    }

上面例子中如果瀏覽器請求的是 /user/1 的時候,就表示此時的用戶 id 為 1,此時就會先從 session 中查找是否有 “user” 屬性,如果有的話,就代表用戶此時處于登錄的狀態,如果沒有的話,就會讓用戶返回到登錄頁面,這種機制在各種網站經常會使用的,然后根據這個 id = 1 ,去查找用戶的信息,然后把查找的 “user” 放在 model 中,然后返回用戶詳情頁面,最后在頁面中用 $!{user.name} 獲取用戶的名字,同樣的方式可以獲取用戶的其他信息,把所有的用戶詳情信息展示出來。

創建 Spring MVC 之器

Spring MVC 核心 Servlet 架構圖如下:

Java 中常用的 Servlet 我在另外一篇文章寫的很清楚了,有興趣的請看:通過源碼詳解 Servlet ,這里我就不再解釋了。

這里主要講 Spring 中的 HttpServletBean、FrameworkServlet、DispatcherServlet 這三個類的創建過程。

通過上面的圖,可以看到這三個類直接實現三個接口:EnvironmentCapable、EnvironmentAware、ApplicationContextAware。下面我們直接看下這三個接口的內部是怎樣寫的。

EnvironmentCapable.java

public interface EnvironmentCapable {
    //返回組件的環境,可能返回 null 或者默認環境
    @Nullable
    Environment getEnvironment();
}

EnvironmentAware.java

public interface EnvironmentAware extends Aware {
    //設置組件的運行環境
    void setEnvironment(Environment environment);
}

ApplicationContextAware.java

public interface ApplicationContextAware extends Aware {
    //設置運行對象的應用上下文
    //當類實現這個接口后,這個類可以獲取ApplicationContext中所有的bean,也就是說這個類可以直接獲取Spring配置文件中所有有引用到的bean對象
    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}

怎么使用這個這個接口呢?

參考文章:org.springframework.context.ApplicationContextAware使用理解

HttpServletBean

這里就直接看其中最重要的 init() 方法的代碼了:

/**
 * 將配置參數映射到此servlet的bean屬性,并調用子類初始化。
 *  如果 bean 配置不合法(或者需要的參數丟失)或者子類初始化發生錯誤,那么就會拋出 ServletException 異常
 */
@Override
public final void init() throws ServletException {
   //日志代碼刪除了

   // 從init參數設置bean屬性。
   //獲得web.xml中的contextConfigLocation配置屬性,就是spring MVC的配置文件
   PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
   if (!pvs.isEmpty()) {
      try {
         BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
         //獲取服務器的各種信息
         ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
         bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
         //模板方法,可以在子類中調用,做一些初始化工作,bw代表DispatcherServelt
         initBeanWrapper(bw);
         //將配置的初始化值設置到DispatcherServlet中
         bw.setPropertyValues(pvs, true);
      }
      catch (BeansException ex) {
         //日志代碼
         throw ex;
      }
   }

   // Let subclasses do whatever initialization they like.
   //模板方法,子類初始化的入口方法
   initServletBean();

   //日志代碼刪除了
}

FrameworkServlet

其中重要方法如下:里面也就兩句關鍵代碼,日志代碼我直接刪掉了

protected final void initServletBean() throws ServletException {
        //日志代碼刪除了
        long startTime = System.currentTimeMillis();

        //就是 try 語句里面有兩句關鍵代碼
        try {
            //初始化 webApplicationContext
            this.webApplicationContext = initWebApplicationContext();
            //模板方法,
            initFrameworkServlet();
        }
        catch (ServletException ex) {
            this.logger.error("Context initialization failed", ex);
            throw ex;
        }
        catch (RuntimeException ex) {
            this.logger.error("Context initialization failed", ex);
            throw ex;
        }

        //日志代碼刪除了
    }

再來看看上面代碼中調用的 initWebApplicationContext() 方法

protected WebApplicationContext initWebApplicationContext() {
        //獲取 rootContext
        WebApplicationContext rootContext =
                WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        WebApplicationContext wac = null;

        if (this.webApplicationContext != null) {
            // 上下文實例在構造時注入 - >使用它
            wac = this.webApplicationContext;
            if (wac instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
                if (!cwac.isActive()) {
                // 如果上下文尚未刷新 -> 提供諸如設置父上下文,設置應用程序上下文ID等服務
                    if (cwac.getParent() == null) {
            // 上下文實例被注入沒有顯式的父類 -> 將根應用程序上下文(如果有的話可能為null)設置為父級
                        cwac.setParent(rootContext);
                    }
                    configureAndRefreshWebApplicationContext(cwac);
                }
            }
        }
        if (wac == null) {
    // 當 WebApplicationContext 已經存在 ServletContext 中時,通過配置在 servlet 中的 ContextAttribute 參數獲取
            wac = findWebApplicationContext();
        }
        if (wac == null) {
            // 如果 WebApplicationContext 還沒有創建,則創建一個
            wac = createWebApplicationContext(rootContext);
        }
        if (!this.refreshEventReceived) {
            // 當 ContextRefreshedEvent 事件沒有觸發時調用此方法,模板方法,可以在子類重寫
            onRefresh(wac);
        }
        if (this.publishContext) {
            // 將 ApplicationContext 保存到 ServletContext 中去
            String attrName = getServletContextAttributeName();
            getServletContext().setAttribute(attrName, wac);
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Published WebApplicationContext of servlet "" + getServletName() +
                        "" as ServletContext attribute with name [" + attrName + "]");
            }
        }
        return wac;
    }

initWebApplicationContext 方法做了三件事:

獲取 Spring 的根容器 rootContext

設置 webApplicationContext 并根據情況調用 onRefresh 方法

將 webApplicationContext 設置到 ServletContext 中

這里在講講上面代碼中的 wac == null 的幾種情況:

1)、當 WebApplicationContext 已經存在 ServletContext 中時,通過配置在 servlet 中的 ContextAttribute 參數獲取,調用的是 findWebApplicationContext() 方法

protected WebApplicationContext findWebApplicationContext() {
        String attrName = getContextAttribute();
        if (attrName == null) {
            return null;
        }
        WebApplicationContext wac =
                WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);
        if (wac == null) {
            throw new IllegalStateException("No WebApplicationContext found: initializer not registered?");
        }
        return wac;
    }

2)、如果 WebApplicationContext 還沒有創建,調用的是 createWebApplicationContext 方法

protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
        //獲取創建類型
        Class contextClass = getContextClass();
        //刪除了打印日志代碼

        //檢查創建類型
        if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
            throw new ApplicationContextException(
                    "Fatal initialization error in servlet with name "" + getServletName() +
                    "": custom WebApplicationContext class [" + contextClass.getName() +
                    "] is not of type ConfigurableWebApplicationContext");
        }
        //具體創建
        ConfigurableWebApplicationContext wac =
                (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

        wac.setEnvironment(getEnvironment());
        wac.setParent(parent);
  //并設置的 contextConfigLocation 參數傳給 wac,默認是 WEB-INFO/[ServletName]-Servlet.xml
        wac.setConfigLocation(getContextConfigLocation());

        //調用的是下面的方法
        configureAndRefreshWebApplicationContext(wac);

        return wac;
    }

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
        if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
            // The application context id is still set to its original default value
            // -> assign a more useful id based on available information
            if (this.contextId != null) {
                wac.setId(this.contextId);
            }
            else {
                // Generate default id...
                wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                        ObjectUtils.getDisplayString(getServletContext().getContextPath()) + "/" + getServletName());
            }
        }

        wac.setServletContext(getServletContext());
        wac.setServletConfig(getServletConfig());
        wac.setNamespace(getNamespace());
        wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

        // The wac environment"s #initPropertySources will be called in any case when the context
        // is refreshed; do it eagerly here to ensure servlet property sources are in place for
        // use in any post-processing or initialization that occurs below prior to #refresh
        ConfigurableEnvironment env = wac.getEnvironment();
        if (env instanceof ConfigurableWebEnvironment) {
            ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
        }

        postProcessWebApplicationContext(wac);
        applyInitializers(wac);
        wac.refresh();
    }

里面還有 doXXX() 方法,大家感興趣的可以去看看。

DispatcherServlet

DispatcherServlet 繼承自 FrameworkServlet,onRefresh 方法是 DispatcherServlet 的入口方法,在 initStrategies 方法中調用了 9 個初始化的方法。

這里分析其中一個初始化方法:initLocaleResolver() 方法

private void initLocaleResolver(ApplicationContext context) {
        try {
            //在 context 中獲取
            this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
            //刪除了打印日志的代碼
        }
        catch (NoSuchBeanDefinitionException ex) {
            //使用默認的策略
            this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
            //刪除了打印日志的代碼
        }
    }

查看默認策略代碼:

protected  T getDefaultStrategy(ApplicationContext context, Class strategyInterface) {
        //調用 getDefaultStrategies 方法
        List strategies = getDefaultStrategies(context, strategyInterface);
        if (strategies.size() != 1) {
            throw new BeanInitializationException(
                    "DispatcherServlet needs exactly 1 strategy for interface [" + strategyInterface.getName() + "]");
        }
        return strategies.get(0);
    }

    /**
     * Create a List of default strategy objects for the given strategy interface.
     * 

The default implementation uses the "DispatcherServlet.properties" file (in the same * package as the DispatcherServlet class) to determine the class names. It instantiates * the strategy objects through the context"s BeanFactory. */ @SuppressWarnings("unchecked") protected List getDefaultStrategies(ApplicationContext context, Class strategyInterface) { String key = strategyInterface.getName(); //根據策略接口的名字從 defaultStrategies 獲取所需策略的類型 String value = defaultStrategies.getProperty(key); if (value != null) { //如果有多個默認值的話,就以逗號分隔為數組 String[] classNames = StringUtils.commaDelimitedListToStringArray(value); List strategies = new ArrayList<>(classNames.length); //按獲取到的類型初始化策略 for (String className : classNames) { try { Class clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader()); Object strategy = createDefaultStrategy(context, clazz); strategies.add((T) strategy); } catch (ClassNotFoundException ex) { throw new BeanInitializationException( "Could not find DispatcherServlet"s default strategy class [" + className + "] for interface [" + key + "]", ex); } catch (LinkageError err) { throw new BeanInitializationException( "Error loading DispatcherServlet"s default strategy class [" + className + "] for interface [" + key + "]: problem with class file or dependent class", err); } } return strategies; } else { return new LinkedList<>(); } }

其他幾個方法大概也類似,我就不再寫了。

小結

主要講了 Spring MVC 自身創建過程,分析了 Spring MVC 中 Servlet 的三個層次:HttpServletBean、FrameworkServlet 和 DispatcherServlet。HttpServletBean 繼承自 Java 的 HttpServlet,其作用是將配置的參數設置到相應的屬性上;FrameworkServlet 初始化了 WebApplicationContext;DispatcherServlet 初始化了自身的 9 個組件。

Spring MVC 之用

分析 Spring MVC 是怎么處理請求的。首先分析 HttpServletBean、FrameworkServlet 和 DispatcherServlet 這三個 Servlet 的處理過程,最后分析 doDispatcher 的結構。

HttpServletBean

參與了創建工作,并沒有涉及請求的處理。

FrameworkServlet

在類中的 service() 、doGet()、doPost()、doPut()、doDelete()、doOptions()、doTrace() 這些方法中可以看到都調用了一個共同的方法 processRequest() ,它是類在處理請求中最核心的方法。

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        long startTime = System.currentTimeMillis();
        Throwable failureCause = null;
        //獲取 LocaleContextHolder 中原來保存的 LocaleContext
        LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
        //獲取當前請求的 LocaleContext
        LocaleContext localeContext = buildLocaleContext(request);
        //獲取 RequestContextHolder 中原來保存的 RequestAttributes
        RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
        //獲取當前請求的 ServletRequestAttributes
        ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
//將當前請求的 LocaleContext 和 ServletRequestAttributes 設置到 LocaleContextHolder 和 RequestContextHolder
        initContextHolders(request, localeContext, requestAttributes);

        try {
            //實際處理請求的入口,這是一個模板方法,在 Dispatcher 類中才有具體實現
            doService(request, response);
        }catch (ServletException ex) {
            failureCause = ex;
            throw ex;
        }catch (IOException ex) {
            failureCause = ex;
            throw ex;
        }catch (Throwable ex) {
            failureCause = ex;
            throw new NestedServletException("Request processing failed", ex);
        }finally {
            //將 previousLocaleContext,previousAttributes 恢復到 LocaleContextHolder 和 RequestContextHolder 中
            resetContextHolders(request, previousLocaleContext, previousAttributes);
            if (requestAttributes != null) {
                requestAttributes.requestCompleted();
            }
            //刪除了日志打印代碼
            //發布了一個 ServletRequestHandledEvent 類型的消息
            publishRequestHandledEvent(request, response, startTime, failureCause);
        }
    }

DispatcherServlet

上一章中其實還沒把該類講清楚,在這個類中,里面的智行處理的入口方法應該是 doService 方法,方法里面調用了 doDispatch 進行具體的處理,在調用 doDispatch 方法之前 doService 做了一些事情:首先判斷是不是 include 請求,如果是則對 request 的 Attribute 做個快照備份,等 doDispatcher 處理完之后(如果不是異步調用且未完成)進行還原 ,在做完快照后又對 request 設置了一些屬性。

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // Keep a snapshot of the request attributes in case of an include,
        // to be able to restore the original attributes after the include.
        Map attributesSnapshot = null;
        if (WebUtils.isIncludeRequest(request)) {
            attributesSnapshot = new HashMap<>();
            Enumeration attrNames = request.getAttributeNames();
            while (attrNames.hasMoreElements()) {
                String attrName = (String) attrNames.nextElement();
                if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)){
                    attributesSnapshot.put(attrName, request.getAttribute(attrName));
                }
            }
        }
        // Make framework objects available to handlers and view objects.
        request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
        request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
        request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
        request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

        FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
        if (inputFlashMap != null) {
            request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
        }
        request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
        request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);

        try {
            //調用 doDispatch 方法
            doDispatch(request, response);
        }finally {
            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
                // Restore the original attribute snapshot, in case of an include.
                if (attributesSnapshot != null) {
                    restoreAttributesAfterInclude(request, attributesSnapshot);
                }
            }
        }
    }

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);

                // Determine handler for the current request.  根據 request 找到 Handler
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null || mappedHandler.getHandler() == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

    // Determine handler adapter for the current request.根據 Handler 找到對應的 HandlerAdapter
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
                // Process last-modified header, if supported by the handler.
                //處理 GET 、 HEAD 請求的 LastModified
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (logger.isDebugEnabled()) {
                        logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                    }
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }
                //執行相應的 Interceptor 的 preHandle
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }
                // Actually invoke the handler. HandlerAdapter 使用 Handler 處理請求
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                //如果需要異步處理,直接返回
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }
                //當 view 為空時,根據 request 設置默認 view
                applyDefaultViewName(processedRequest, mv);
                //執行相應 Interceptor 的 postHandler
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }catch (Exception ex) {
                dispatchException = ex;
            }catch (Throwable err) {
                // As of 4.3, we"re processing Errors thrown from handler methods as well,
                // making them available for @ExceptionHandler methods and other scenarios.
                dispatchException = new NestedServletException("Handler dispatch failed", err);
            }
            //調用 processDispatchResult 方法處理上面處理之后的結果(包括處理異常,渲染頁面,發出完成通知觸發 Interceptor 的 afterCompletion)
            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);
                }
            }
        }
    }

Handler,HandlerMapping,HandlerAdapter 三個區別:

Handler:處理器,對應 MVC 的 C層,也就是 Controller 層,具體表現形式有很多種,可以是類,方法,它的類型是 Object,只要可以處理實際請求就可以是 Handler。

HandlerMapping:用來查找 Handler 的。

HandlerAdapter :Handler 適配器,

另外 View 和 ViewResolver 的原理與 Handler 和 HandlerMapping 的原理類似。

小結

本章分析了 Spring MVC 的請求處理的過程。

注:本文首發于 zhisheng 的博客,可轉載但務必請注明原創地址為:http://www.54tianzhisheng.cn/2017/07/14/Spring-MVC02/

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/67391.html

相關文章

  • 寫這么多系列博客,怪不得找不到女朋友

    摘要:前提好幾周沒更新博客了,對不斷支持我博客的童鞋們說聲抱歉了。熟悉我的人都知道我寫博客的時間比較早,而且堅持的時間也比較久,一直到現在也是一直保持著更新狀態。 showImg(https://segmentfault.com/img/remote/1460000014076586?w=1920&h=1080); 前提 好幾周沒更新博客了,對不斷支持我博客的童鞋們說聲:抱歉了!。自己這段時...

    JerryWangSAP 評論0 收藏0
  • 那些年我看過的書 —— 致敬我的大學生活 —— Say Good Bye !

    摘要:開頭正式開啟我入職的里程,現在已是工作了一個星期了,這個星期算是我入職的過渡期,算是知道了學校生活和工作的差距了,總之,盡快習慣這種生活吧。當時是看的廖雪峰的博客自己也用做爬蟲寫過幾篇博客,不過有些是在前人的基礎上寫的。 showImg(https://segmentfault.com/img/remote/1460000010867984); 開頭 2017.08.21 正式開啟我...

    xiaoqibTn 評論0 收藏0
  • 看透 Spring MVC 代碼分析實踐 —— 網站基礎知識

    摘要:兩個序號,三個標志位含義表示所傳數據的序號。正常通信時為,第一次發起請求時因為沒有需要確認接收的數據所以為。終止位,用來在數據傳輸完畢后釋放連接。手機網站如,填寫。中的用法普通的用法分為和兩大類。 網站架構及其演變過程 基礎結構 網絡傳輸分解方式: 標準的 OSI 參考模型 TCP/IP 參考模型 showImg(https://segmentfault.com/img/remot...

    mingde 評論0 收藏0
  • Spring Boot & Spring MVC 異常處理的N種方法

    摘要:定制特定異常返回結果根據官方文檔的例子,可以使用和對特定異常返回特定的結果。下面是用瀏覽器和訪問的結果無輸出注意上方表格的錯誤,產生這個的原因前面已經講過。不過需要注意的是,無法通過設定,由或者容器決定里一律是。 github:https://github.com/chanjarste... 參考文檔: Spring Boot 1.5.4.RELEASE Documentation ...

    h9911 評論0 收藏0

發表評論

0條評論

fsmStudy

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<