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 中的流轉結束。