1. 程式人生 > 其它 >SpringBoot裡的Servlet和實現

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