JAVA WEB的前世今生
1990年伯納斯·李發明了WWW,從此完全改變了數字世界,
早期的網站功能都是很簡單的,那時的網站還是靜態頁面,所謂的資源就是實實在在的檔案,URL里的路徑也是實實在在的檔案路徑,很明顯,這樣的缺點就是不能動態展示資料,如果需要發布一篇文章,就需要將檔案上傳到服務器,再編輯主頁,將文章鏈接進去,如果找不到檔案,那就回傳404,

隨著人民日益增長的美好生活需要和發展不平衡不充分之間的矛盾網路規模的發展,人們對動態網頁的訴求和早期的靜態網頁技術的矛盾成為主要矛盾后,就需要一種技術能夠動態生成網頁,這個時候的解決方案就是CGI(Common Gateway Interface),譯作“通用網關介面”,
起:CGI
談到動態網頁開發,一個繞不開的技術就是CGI,正如其名,Common Gateway Interface,通用是他的第一個特性,無論是C、C++、Java還是Python、Perl都是支持CGI的,理論上任何一種具有標準輸入輸出和環境變數的語言都可以用來撰寫CGI程式,
我們可以寫一個簡單的腳本hello放到服務器的cgi-bin目錄下,然后訪問http://example.com/cgi-bin/hello就可以看到腳本生效了,當然,這需要你的服務器支持CGI,
#!/usr/bin/python
# -*- coding: UTF-8 -*-
print('Content-type:text/html')
print('') # blank line, end of headers
print('<html>')
print('<head>')
print('<meta charset="utf-8">')
print('<title>Hello Word!</title>')
print('</head>')
print('<body>')
print('<h2>Hello Word!</h2>')
print('</body>')
print('</html>')
雖然繁瑣,但是屏蔽了底層的TCP連接,HTTP協議決議,開發的作業就只需要拼接字串,做做輸入輸入就可以了,實在是一個飛躍,
CGI的第二個單詞是Gateway,網關這個詞在不同的語境里面有不同的含義,例如微服務、網路傳輸層,但是意思其實都差不多,可以理解為協議轉換器,負責將網關兩側的協議轉換,
Interface翻譯成介面,所謂介面,就是需要遵守的規則,比如如何從環境變數獲取資料輸入,如何從標準輸入stdin獲取資料請求,
CGI的弊端也很明顯,那就是他使用的是fork/exec模式,每當發送一次CGI請求都會fork一個行程,這在并發請求下會造成不小的壓力,為了解決這個問題,后面誕生了FastCGI,也就是使用了常駐記憶體的行程池技術,由調度器將請求發送給相應的handler行程處理,請求結束后繼續等待下一個請求,
時至今日,CGI早已淡出人們的視線,仍然在大規模使用的恐怕只有在C/C++了,這里面需要點名的就是鵝廠:
https://mp.weixin.qq.com/cgi-bin/readtemplate?t=business/faq_operation_tmpl&type=info&lang=zh_CN&token=
承:Servlet
JAVA最早是面向嵌入式設備的語言,在CGI蓬勃發展的年代,作為后來者自然而然的站在了前人的肩上,針對CGI的一些缺點,提出了Servlet,
Servlet很簡單,只是一個介面,幾行代碼,5個方法,只要實作了Servlet介面就是Servlet,
public interface Servlet {
void init(ServletConfig var1) throws ServletException;
ServletConfig getServletConfig();
void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
String getServletInfo();
void destroy();
}
其中最重要的就是三件事情了:
- init,初始化
- service,接受到請求
- destroy,銷毀
加上servlet的加載和創建,他的什么周期共包含5個程序,
以下就是一個很簡單的Servlet了
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class HelloServlet extends HttpServlet {
private static final long serialVersionUID = -4159478011804388333L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("Hello World!");
}
}
web.xml檔案用來描述servlet的映射關系
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>net.sk32.demo.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
Apache Tomcat
前面說了,Servlet只是一個協議,他并不直接與客戶端打交道,我們寫的這個servlet需要一個容器來執行,稱做Servlet容器,Tomcat就是其中最著名的一個,

Tomcat 的主要目錄結構
? tomcat tree -L 1 apache-tomcat-9.0.38
├── bin // 存放可執行的檔案,如 startup 和 shutdown
├── conf // 存放組態檔,如核心組態檔 server.xml 和應用默認的部署描述檔案 web.xml
├── lib // 存放 Tomcat 運行需要的jar包
├── logs // 存放運行的日志檔案
├── webapps // 存放默認的 web 應用部署目錄
└── work // 存放 web 應用代碼生成和編譯檔案的臨時目錄
將前面的HelloServlet.java編譯成class檔案,再加上web.xml檔案,拷貝到webapps目錄,然后打開瀏覽器訪問http://localhost:8080/helloWorld/hello就可以看到回傳的頁面了,
? webapps tree helloWorld
helloWorld
└── WEB-INF
├── HelloServlet.class
└── web.xml
Tomcat的核心原理
這部分先占個坑,講起來還需要一篇文章,
JSP(Java Server Pages)
同一時期PHP憑借良好的用戶體驗(可以在HTML中插入代碼)大行其道,而這時的JAVA程式員畫風還是這樣的:
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
out.println("<HTML>");
out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>");
out.println(" <BODY>");
out.print(" This is ");
out.print(this.getClass());
out.println(", using the GET method");
out.println(" </BODY>");
out.println("</HTML>");
out.flush();
out.close();
}
一個頁面幾千行println簡直蔚為壯觀,
這個時候很多人紛紛轉向了最好的語言PHP,SUN公司坐不住了,趕緊推出了JSP,畫風就變成這樣了:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
$END$
</body>
</html>
看起來正常多了,是不是和HTML很像,那他是怎么運作的呢,實際上JSP本質上還是servlet,例如上面這個index.jsp最侄訓生成index.jsp.java檔案:
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports {
...
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException {
...
try {
response.setContentType("text/html;charset=UTF-8");
pageContext = _jspxFactory.getPageContext(this, request, response,
null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;
out.write("\n");
out.write("\n");
out.write("<html>\n");
out.write(" <head>\n");
out.write(" <title>$Title$</title>\n");
out.write(" </head>\n");
out.write(" <body>\n");
out.write(" $END$\n");
out.write(" </body>\n");
out.write("</html>\n");
} catch (java.lang.Throwable t) {
...
} finally {
_jspxFactory.releasePageContext(_jspx_page_context);
}
}
}
轉:Spring
嚴格來說,Spring并不僅僅是解決Web開發而產生的,他更多的是一套框架,為了解決企業應用程式開發的復雜性而創建的,這篇文章更多的還是講SpringMVC,
MVC并不是JAVA獨有的,Model-View-Controller是一種經典的三層架構,例如PHP、Python都有相應的框架,
M(Model):模型,處理業務邏輯,封裝物體
V(View):視圖,展示內容
C(Controller):控制器,調度分發(接受請求-呼叫模型-轉發視圖)
太陽底下沒有新鮮事,SpringMVC也是基于servlet的,想要使用SpringMVC,我們需要在前面的web.xml中配置DispatcherServlet,將所有請求映射到org.springframework.web.servlet.DispatcherServlet,
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
SpringMVC請求流程
默認情況下,SpringMVC會去WEB-INF目錄下尋找[servlet名稱]-servlet.xml檔案,在那里定義相關的Beans,大致的流程如下:
- 啟動Servlet容器,例如
Tomcat,對DispatcherServlet進行實體化,然后呼叫它的init()方法進行初始化,這個程序完成:加載web.xml初始化引數;建立WebApplicationContext(SpringMVC的IOC容器);進行組件的初始化; - 客戶端請求由
Servlet容器接收并匹配映射路徑,按照我們在web.xml里的配置,轉交給DispatcherServlet; DispatcherServlet從容器中查找能夠處理Request請求的HandlerMapping實體,回傳HandlerExecutionChain;DispatcherServlet查找能夠處理該Handler的HandlerAdapter組件;- 執行
HandlerExecutionChain中所有攔截器的preHandler()方法,然后再利用HandlerAdapter執行Handler,執行完成得到ModelAndView,再依次呼叫攔截器的postHandler()方法; - 利用
ViewResolver將ModelAndView或是Exception(可決議成 ModelAndView)決議成View,然后 View 會呼叫render()方法再根據 ModelAndView 中的資料渲染出頁面; - 最后再依次呼叫攔截器的
afterCompletion()方法,這一次請求就結束了,
SpringMVC原始碼分析
我們根據這個思路來看代碼:
我們先從核心的DispatcherServlet入手,先全域了解一下繼承關系,

DispatcherServlet初始化
根據前面的描述,servlet在初始化階段會呼叫init()方法,DispatcherServlet的init()是繼承自HttpServletBean#init
/**
* Map config parameters onto bean properties of this servlet, and
* invoke subclass initialization.
* @throws ServletException if bean properties are invalid (or required
* properties are missing), or if subclass initialization fails.
*/
@Override
public final void init() throws ServletException {
// 處理init-param引數
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;
}
}
// 初始化servlet
initServletBean();
}
initServletBean這個方法在FrameworkServlet#initServletBean中實作了:
/**
* Overridden method of {@link HttpServletBean}, invoked after any bean properties
* have been set. Creates this servlet's WebApplicationContext.
*/
@Override
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
if (logger.isInfoEnabled()) {
logger.info("Initializing Servlet '" + getServletName() + "'");
}
long startTime = System.currentTimeMillis();
try {
// 初始化WebApplicationContext容器
this.webApplicationContext = initWebApplicationContext();
// 掛載點,默認沒有任何實作
initFrameworkServlet();
}
catch (ServletException | RuntimeException ex) {
logger.error("Context initialization failed", ex);
throw ex;
}
if (logger.isDebugEnabled()) {
String value = https://www.cnblogs.com/edifyX/p/this.enableLoggingRequestDetails ?
"shown which may lead to unsafe logging of potentially sensitive data" :
"masked to prevent unsafe logging of potentially sensitive data";
logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
"': request parameters and headers will be " + value);
}
if (logger.isInfoEnabled()) {
logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
}
}
initWebApplicationContext方法初始化并回傳容器,在FrameworkServlet#initWebApplicationContext實作:
/**
* Initialize and publish the WebApplicationContext for this servlet.
* <p>Delegates to {@link #createWebApplicationContext} for actual creation
* of the context. Can be overridden in subclasses.
* @return the WebApplicationContext instance
* @see #FrameworkServlet(WebApplicationContext)
* @see #setContextClass
* @see #setContextConfigLocation
*/
protected WebApplicationContext initWebApplicationContext() {
// ROOT context
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
// 在創建該Servlet注入的背景關系
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// 注入到 ROOT 背景關系
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
// 查找已經系結的背景關系
wac = findWebApplicationContext();
}
if (wac == null) {
// 如果沒有找到相應的背景關系,并指定父親為ROOT
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// 重繪背景關系(執行一些初始化),這里加了同步代碼塊,我是5.2.6版本
synchronized (this.onRefreshMonitor) {
onRefresh(wac);
}
}
// 推送事件通知
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
DispatcherServlet#onRefresh實作了onRefresh方法,基本是一些常用組件的初始化,我們可以詳細的看一下initHandlerMappings和initHandlerAdapters:
/**
* This implementation calls {@link #initStrategies}.
*/
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
/**
* Initialize the HandlerMappings used by this class.
* <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace,
* we default to BeanNameUrlHandlerMapping.
*/
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// 從ApplicationContext容器中獲取所有的HandlerMapping實體
// 包括祖先contexts,就是一個遞回實作的
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// 排序,是OrderComparator的子類,可以支持@Order和@Priority注解
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
} else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// 如果沒有HandlerMapping,加載默認的
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
/**
* Initialize the HandlerAdapters used by this class.
* <p>If no HandlerAdapter beans are defined in the BeanFactory for this namespace,
* we default to SimpleControllerHandlerAdapter.
*/
private void initHandlerAdapters(ApplicationContext context) {
this.handlerAdapters = null;
if (this.detectAllHandlerAdapters) {
// HandlerAdapters也是一樣的,就不詳細說明了
Map<String, HandlerAdapter> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerAdapters = new ArrayList<>(matchingBeans.values());
// We keep HandlerAdapters in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerAdapters);
}
} else {
try {
HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
this.handlerAdapters = Collections.singletonList(ha);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerAdapter later.
}
}
// Ensure we have at least some HandlerAdapters, by registering
// default HandlerAdapters if no other adapters are found.
if (this.handlerAdapters == null) {
this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerAdapters declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
HandlerMapping和HandlerAdapters
這里需要插入一點,HandlerMapping和HandlerAdapters是什么?
HandlerMapping介面主要有兩類,分別繼承自AbstractUrlHandlerMapping和AbstractHandlerMethodMapping,都繼承自抽象類AbstractHandlerMapping,HandlerMapping介面只有一個方法getHandler,接受request,根據URL和method回傳HandlerExecutionChain也就是handler和一堆HandlerInterceptor攔截器:

HandlerAdaptor也是一個介面,3個方法,supports,handle,getLastModified,字面上的意思就是處理配接器,作用就是呼叫具體的方法對用戶發來的請求來進行處理,HandlerMapping獲取到執行請求的controller時,DispatcherServlte會根據controller對應的controller型別來呼叫相應的HandlerAdapter來進行處理,
- SimpleControllerHandlerAdapter支持
org.springframework.web.servlet.mvc.Controller型別的handler - SimpleServletHandlerAdapter支持
javax.servlet.Servlet - HttpRequestHandlerAdapter支持
org.springframework.web.HttpRequestHandler

processRequest
我們繼續回到DispatcherServlet,org.springframework.web.servlet.FrameworkServlet繼承了doGet,doPost,doPut,doDelete,doOptions,doTrace等喜聞樂見的CRUD方法:
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
// 其他幾個方法也是呼叫了processRequest
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
// 回傳與當前執行緒相關聯的 LocaleContext
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
// 根據請求構建 LocaleContext,公開請求的語言環境為當前語言環境
LocaleContext localeContext = buildLocaleContext(request);
// 回傳當前系結到執行緒的 RequestAttributes
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
// 獲取當前請求的 WebAsyncManager,如果沒有找到則創建
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
// 使 LocaleContext 和 requestAttributes 關聯
initContextHolders(request, localeContext, requestAttributes);
try {
// DispatcherServlet實作
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();
}
logResult(request, response, failureCause, asyncManager);
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
接下來doService呼叫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 {
// Multipart涉及到檔案IO需要單獨處理
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 這就是前面提到的HandlerMapping了
// 遍歷所有的 HandlerMapping 找到與請求對應的 Handler
// 并將其與一堆攔截器封裝到 HandlerExecution 物件中
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 遍歷所有的 HandlerAdapter,找到可以處理該 Handler 的 HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Last-Modified是一個回應首部,其中包含源頭服務器認定的資源做出修改的日期及時間,
// 它通常被用作一個驗證器來判斷接收到的或者存盤的資源是否彼此一致,如果一致直接回傳
// https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/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;
}
}
// 遍歷攔截器,執行它們的 preHandle() 方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 執行實際的處理程式
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 遍歷攔截器,執行它們的 postHandle() 方法
applyDefaultViewName(processedRequest, mv);
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);
}
// 處理執行結果,是一個 ModelAndView 或 Exception,然后進行渲染
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) {
// 遍歷攔截器,執行它們的 afterCompletion() 方法
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
processDispatchResult用來渲染mv,或者封裝exception成mv渲染:
/**
* Handle the result of handler selection and handler invocation, which is
* either a ModelAndView or an Exception to be resolved to a ModelAndView.
*/
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
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);
}
}
// Did the handler return a view to render?
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.");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {
// Exception (if any) is already handled..
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
這一階段的SSM、SSH以及眾多的開源框架奠定了JAVA在WEB開發界的領先地位,
合:Microservice
通過整體式架構,所有行程緊密耦合,并可作為單項服務運行,這意味著,如果應用程式的一個行程遇到需求峰值,則必須擴展整個架構,隨著代碼庫的增長,添加或改進整體式應用程式的功能變得更加復雜,這種復雜性限制了試驗的可行性,并使實施新概念變得困難,整體式架構增加了應用程式可用性的風險,因為許多依賴且緊密耦合的行程會擴大單個行程故障的影響,
使用微服務架構,將應用程式構建為獨立的組件,并將每個應用程式行程作為一項服務運行,這些服務使用輕量級 API 通過明確定義的介面進行通信,這些服務是圍繞業務功能構建的,每項服務執行一項功能,由于它們是獨立運行的,因此可以針對各項服務進行更新、部署和擴展,以滿足對應用程式特定功能的需求,

Spring Cloud 模塊的相關介紹如下:
-
Eureka/Nacos:服務注冊中心,用于服務管理
-
Ribbon:基于客戶端的負載均衡組件
-
Hystrix/Sentinel:容錯框架,能夠防止服務的雪崩效應
-
Feign:Web 服務客戶端,能夠簡化 HTTP 介面的呼叫
-
Zuul:API 網關,提供路由轉發、請求過濾等功能
-
Config:分布式配置管理
-
Sleuth:服務跟蹤
-
Stream:構建訊息驅動的微服務應用程式的框架
-
Bus:訊息代理的集群訊息總線
借用一張圖來描述Spring Cloud架構,時間關系先寫這么多,有時間再寫,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/68300.html
標籤:Java
上一篇:C語言02
