SpringBoot里的Servlet和實現


Servlet

接口,一個規范,

SpringBoot

Spring Boot 是 Spring 的子項目,正如其名字,提供 Spring 的引導( Boot )的功能。

通過 Spring Boot ,開發者可以快速配置 Spring 項目,引入各種 Spring MVC、Spring Transaction、Spring AOP、等等框架,而無需不斷重復編寫繁重的 Spring 配置,降低了 Spring 的使用成本。

Servlet 和 SpringBoot

搭建一個 SpringBoot 項目:

引入依賴:

<dependency>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-starter-web</artifactid>
</dependency>

添加 Controller

@Controller
public class IndexController {

    @RequestMapping("/hello")
    public String hello(){
        return "hello";
    }
}

添加啟動類

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

運行DemoApplication.main即可啟動一個簡單的 Springboot web 項目。

項目是通過一個內嵌的Tomcat容器來提供 web 服務。Tomcat 是 Servlet 容器,SpringBoot 想要使用 Servlet 容器就需要有 servlet 那么 SpringBoot 中有哪些 Servlet?是怎么通過這些 Servlet 來提供 web 服務的呢?

SpringBoot 中的 Servlet

SpringBoot 中涉及到的 Servlet 及它們的關系

HttpServletBean

主要方法:

設置一些配置信息,並提供子類初始化入口。無具體業務邏輯

@Override
    public final void init() throws ServletException {
        // Set bean properties from init parameters.
        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()));
                initBeanWrapper(bw);
                bw.setPropertyValues(pvs, true);
            }
            catch (BeansException ex) {
                if (logger.isErrorEnabled()) {
                    logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
                }
                throw ex;
            }
        }
        // Let subclasses do whatever initialization they like.
        //增加子類實例化入口
        initServletBean();
    }

FrameworkServlet

FrameworkServlet 會實現

  • #doGet(HttpServletRequest request, HttpServletResponse response)
  • #doPost(HttpServletRequest request, HttpServletResponse response)
  • #doPut(HttpServletRequest request, HttpServletResponse response)
  • #doDelete(HttpServletRequest request, HttpServletResponse response)
  • #doOptions(HttpServletRequest request, HttpServletResponse response)
  • #doTrace(HttpServletRequest request, HttpServletResponse response)
  • #service(HttpServletRequest request, HttpServletResponse response)

等方法。而這些實現,最終會調用 #processRequest(HttpServletRequest request, HttpServletResponse response) 方法,處理請求。

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
        if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
            processRequest(request, response);
        }
        else {
            super.service(request, response);
        }
    }

    @Override
    protected final void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        processRequest(request, response);
    }

    @Override
    protected final void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        processRequest(request, response);
    }


    @Override
    protected final void doPut(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        processRequest(request, response);
    }


    @Override
    protected final void doDelete(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        processRequest(request, response);
    }
...

處理請求:

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    // <1> 記錄當前時間,用於計算 web 請求的處理時間
    long startTime = System.currentTimeMillis();
    // <2> 記錄異常
    Throwable failureCause = null;

    LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
    LocaleContext localeContext = buildLocaleContext(request);

    RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
    ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

    initContextHolders(request, localeContext, requestAttributes);

    try {
        //  執行真正的邏輯
        doService(request, response);
    } catch (ServletException | IOException ex) {
        failureCause = ex; //
        throw ex;
    } catch (Throwable ex) {
        failureCause = ex; //
        throw new NestedServletException("Request processing failed", ex);
    } finally {
        resetContextHolders(request, previousLocaleContext, previousAttributes);
        if (requestAttributes != null) {
            requestAttributes.requestCompleted();
        }
        // 打印請求日志,並且日志級別為 DEBUG
        logResult(request, response, failureCause, asyncManager);
        // 發布 ServletRequestHandledEvent 事件
        publishRequestHandledEvent(request, response, startTime, failureCause);
    }
}
//抽象類給子類實現
protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
      throws Exception;

DispatcherServlet

doService

#doService(HttpServletRequest request, HttpServletResponse response) 方法,DispatcherServlet 的處理請求的入口方法,代碼如下:

// DispatcherServlet.java

@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
  logRequest(request);

    // Keep a snapshot of the request attributes in case of an include,
    // to be able to restore the original attributes after the include.
    Map<string, object=""> 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.
    // 設置 Spring 框架中的常用對象到 request 屬性中
    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());

    ...

    try {
        // 執行請求的分發
        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

#doDispatch(HttpServletRequest request, HttpServletResponse response) 方法,執行請求的分發。

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

            //  <2> 映射處理器
              //獲得請求對應的 HandlerExecutionChain 對象
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null) { // 如果獲取不到,則根據配置拋出異常或返回 404 錯誤
                noHandlerFound(processedRequest, response);
                return;
            }

             //  <3> 處理器適配器
            // 獲得當前 handler 對應的 HandlerAdapter 對象
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

            // last-modified
            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;
            }
            //  <4> 調用處理器方法
            //  真正的調用 handler 方法,並返回視圖
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

            if (asyncManager.isConcurrentHandlingStarted()) {
                return;
            }

            // 視圖
            applyDefaultViewName(processedRequest, mv);
            //  后置處理 攔截器
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        } catch (Exception ex) {
            dispatchException = ex; // 記錄異常
        } catch (Throwable err) {
            dispatchException = new NestedServletException("Handler dispatch failed", err); // <10> 記錄異常
        }

        // 處理正常和異常的請求調用結果。
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    } catch (Exception ex) {
    ...
    }
  ...
}

processDispatchResult

#processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) 方法,處理正常和異常的請求調用結果。代碼如下:

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
        @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
        @Nullable Exception exception) throws Exception {
    // 標記,是否是生成的 ModelAndView 對象
    boolean errorView = false;

    //如果是否異常的結果
    if (exception != null) {
        if (exception instanceof ModelAndViewDefiningException) {
            logger.debug("ModelAndViewDefiningException encountered", exception);
            mv = ((ModelAndViewDefiningException) exception).getModelAndView();
        } else {
            Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
            mv = processHandlerException(request, response, handler, exception);
            errorView = (mv != null);
        }
    }

    if (mv != null && !mv.wasCleared()) {
        //  渲染頁面
        render(mv, request, response);
        if (errorView) {
            WebUtils.clearErrorRequestAttributes(request);
        }
    } else {
        if (logger.isTraceEnabled()) {
            logger.trace("No view rendering, null ModelAndView returned.");
        }
    }

    ...
}

render

#render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
        // Determine locale for request and apply it to the response.
        Locale locale =
                (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
        response.setLocale(locale);

        View view;
        String viewName = mv.getViewName();
        if (viewName != null) {
      //<5> 解析視圖
      // 視圖解析器ViewResolver
            view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
            if (view == null) {
                throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
                        "' in servlet with name '" + getServletName() + "'");
            }
        }
        else {
            // No need to lookup: the ModelAndView object contains the actual View object.
            view = mv.getView();
            if (view == null) {
                throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
                        "View object in servlet with name '" + getServletName() + "'");
            }
        }

        // Delegate to the View object for rendering.
        if (logger.isTraceEnabled()) {
            logger.trace("Rendering view [" + view + "] ");
        }
        try {
            if (mv.getStatus() != null) {
                response.setStatus(mv.getStatus().value());
            }
      //<6> 渲染視圖
      //視圖渲染
            view.render(mv.getModelInternal(), request, response);
        }
        catch (Exception ex) {
            if (logger.isDebugEnabled()) {
                logger.debug("Error rendering view [" + view + "]", ex);
            }
            throw ex;
        }
    }


至此,對照圖中的一個請求的處理流程在 DispatcherServlet 中的流轉結束。

End


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM