對於一個請求來說,若是隻是須要一個靜態頁面,能夠直接在服務器上根據路徑訪問獲得,可是若是請求的數據是一個動態頁面,即只有在運行時從後臺數據庫獲取,再拼裝東西返回,而後生成一個對應的html文件。在Java中爲了實現這個功能,使用的就是Servlet規範。html
Servlet:server component,運行在服務器上的java代碼java
Servlet並不處理任何的協議和鏈接等等動做,它只是約定了一個種處理request-response的模式。每一個功能實現Servlet接口的類均可以用來處理請求,好比加法用1個servlet,減法用一個Servlet,這樣一但多起來,就須要知道,那些請求用那個Servlet處理,對應的配置產生物也就是web.xml,另外Servlet對象的構建、鏈接端口的請求,處理好對應的映射關係等等都須要有一個程序來負責,這個程序稱做Servlet容器,好比Jetty,從Jetty的整體架構也就能夠看出來它很好的實踐了這些web
Connector負責鏈接,Handler則處理對應的請求,交給Servlet來處理spring
Servlet的生命週期是由發佈它的容器控制的,好比Jetty,當要把請求映射到一個Servlet上時,容器通常會作以下的事情:數據庫
javax.servlet
和javax.servlet.http
提供了要實現Servlet的全部接口和相關類,每個處理Servlet的類必須實現 Servlet.java
接口,它的基本實現爲GenericServlet,是與協議無關的一個實現,若是要實現本身的Servlet接口,能夠繼承它,僅須要實現對應的Service方法。對於處理HTTP請求則可使用HttpServlet更方便,它根據Http請求的類型,對service方法進行了細分,這樣能夠更好的去處理想要處理的請求bash
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason // to go through further expensive logic doGet(req, resp); } else { long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE); if (ifModifiedSince < lastModified) { // If the servlet mod time is later, call doGet() // Round down to the nearest second for a proper compare // A ifModifiedSince of -1 will always be less maybeSetLastModified(resp, lastModified); doGet(req, resp); } else { resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED); } } } else if (method.equals(METHOD_HEAD)) { long lastModified = getLastModified(req); maybeSetLastModified(resp, lastModified); doHead(req, resp); } else if (method.equals(METHOD_POST)) { doPost(req, resp); } else if (method.equals(METHOD_PUT)) { doPut(req, resp); } else if (method.equals(METHOD_DELETE)) { doDelete(req, resp); } else if (method.equals(METHOD_OPTIONS)) { doOptions(req,resp); } else if (method.equals(METHOD_TRACE)) { doTrace(req,resp); } else { // // Note that this means NO servlet supports whatever // method was requested, anywhere on this server. // String errMsg = lStrings.getString("http.method_not_implemented"); Object[] errArgs = new Object[1]; errArgs[0] = method; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg); } } 複製代碼
每次請求總會攜帶一些東西,返回也是,Servlet針對請求抽象了ServletRequest接口和ServletResponse接口,而特定於Http協議,則分別設計了HttpServletRequest接口和HttpServletResponse,每次請求Servlet容器負責實現對應的類提交給service方法服務器
web.xml常見配置以下網絡
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application-context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:web-mvc-dispatcher.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>mvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
複製代碼
從Jetty啓動web項目中的分析可知,會依次的去執行ContextLoaderListener的contextInitialized和DispatcherServlet的init方法,這裏就是jetty容器、servlet和spring的銜接架構
從Jetty啓動web項目分析可獲得,網絡請求分紅兩部分mvc
Jetty中的ServerConnector接收到請求以後調用accepted
private void accepted(SocketChannel channel) throws IOException
{
channel.configureBlocking(false); //將新的鏈接設置成非阻塞的
Socket socket = channel.socket();
configure(socket);
_manager.accept(channel);
}
複製代碼
_manager接收到請求以後,獲取一個Selector,進而提交一個Accept,放到Deque隊列中,Accept實現了SelectorUpdate
public void accept(SelectableChannel channel, Object attachment)
{
final ManagedSelector selector = chooseSelector();
selector.submit(selector.new Accept(channel, attachment));
}
複製代碼
在從Deque中取值時,會執行它的 update方法
public void update(Selector selector)
{
...
//執行註冊
key = channel.register(selector, 0, attachment);
//執行run方法
execute(this);
...
}
public void run()
{
...
createEndPoint(channel, key);
_selectorManager.onAccepted(channel);
...
}
複製代碼
建立的createEndPoint過程以下
private void createEndPoint(SelectableChannel channel, SelectionKey selectionKey) throws IOException
{
//注意_selectorManager自己在構造的時候是一個ServerConnectorManager
//好比這裏是一個SocketChannelEndPoint
EndPoint endPoint = _selectorManager.newEndPoint(channel, this, selectionKey);
//建立鏈接,好比默認的Http了鏈接
Connection connection = _selectorManager.newConnection(channel, endPoint, selectionKey.attachment());
endPoint.setConnection(connection);
//創建鏈接的key是一個SocketChannelEndPoint
selectionKey.attach(endPoint);
endPoint.onOpen();
_selectorManager.endPointOpened(endPoint);
//實際做用是調用了httpConnection的onOpen方法
_selectorManager.connectionOpened(connection);
if (LOG.isDebugEnabled())
LOG.debug("Created {}", endPoint);
}
複製代碼
ServerConnectorManager建立的newEndPoint爲SocketChannelEndPoint
protected ChannelEndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey key) throws IOException
{
SocketChannelEndPoint endpoint = new SocketChannelEndPoint(channel, selectSet, key, getScheduler());
endpoint.setIdleTimeout(getIdleTimeout());
return endpoint;
}
複製代碼
建立的鏈接則是根據鏈接工廠創建的,而默認的則是調用的HttpConnectionFactory
public Connection newConnection(SelectableChannel channel, EndPoint endpoint, Object attachment) throws IOException
{
return getDefaultConnectionFactory().newConnection(ServerConnector.this, endpoint);
}
複製代碼
建立一個HttpConnection對象
public HttpConnection(HttpConfiguration config, Connector connector, EndPoint endPoint, HttpCompliance compliance, boolean recordComplianceViolations)
{
//1. 父類構建了一個_readCallback回調,當成功的時候就會去執行子類的onFillable方法
super(endPoint, connector.getExecutor());
_config = config;
_connector = connector;
_bufferPool = _connector.getByteBufferPool();
//用來構造HTTP的信息
_generator = newHttpGenerator();
//裏頭構建了一個Request對象,它實現了HttpServletRequest,同時構建了Response對象,實現了HttpServletRequest
_channel = newHttpChannel();
_input = _channel.getRequest().getHttpInput();
_parser = newHttpParser(compliance);
_recordHttpComplianceViolations = recordComplianceViolations;
if (LOG.isDebugEnabled())
LOG.debug("New HTTP Connection {}", this);
}
複製代碼
能夠看到Request和Response是和channel綁定的,同一個TCP鏈接用的就是同一個Reqeust和Response,他們會循環的用
鏈接建立完成,調用open方法,它實際在將回調的_readCallback寫入SocketChannelEndPoint的_fillInterest中
Jetty中的EatWhatYouKill的produce方法,即用來處理請求,它核心是隻要獲取task並運行它
Runnable task = produceTask();
複製代碼
這裏的produceTask實際就是初始化的時候傳入的SelectorProducer
的方法
public Runnable produce()
{
while (true)
{
//若是_cursor中有值,也就是有鏈接過來了進行處理
Runnable task = processSelected();
if (task != null)
return task;
//遍歷Deque,好比鏈接創建的時候會放入一個selector.new Accept,並執行update方法,具體分析見面下的Accept鏈接創建
processUpdates();
updateKeys();
//執行Selector對應的select()方法,將放回的keys放入_cursor中存儲
if (!select())
return null;
}
}
複製代碼
當有請求過來的時候,也就是執行processSelected
方法
//SelectorProducer
private Runnable processSelected()
{
...
if (attachment instanceof Selectable)
{
// 當鏈接創建後會附上一個SocketChannelEndPoint,它實現了Selectable
Runnable task = ((Selectable)attachment).onSelected();
if (task != null)
return task;
}
...
}
複製代碼
對應的onSelect實如今ChannelEndPoint中
//ChannelEndPoint
public Runnable onSelected()
{
int readyOps = _key.readyOps();
...
//可讀
boolean fillable = (readyOps & SelectionKey.OP_READ) != 0;
//可寫
boolean flushable = (readyOps & SelectionKey.OP_WRITE) != 0;
...
//根據是可讀仍是可寫返回對應的任務
Runnable task= fillable
? (flushable
? _runCompleteWriteFillable
: _runFillable)
: (flushable
? _runCompleteWrite
: null);
...
return task;
}
複製代碼
到這裏能夠看到,EatWhatYouKill執行的實際上就是可讀或者可寫的一個channel任務。 以可讀的爲例,_runFillable的實現就是從getFillInterest獲取的或吊執行它的fillable方法
public void run()
{
getFillInterest().fillable();
}
複製代碼
這對應到創建的HttpConnection,執行它的fillable方法,即調用這個鏈接的HttpChannel來處理
public void onFillable()
{
....
boolean suspended = !_channel.handle();
....
}
複製代碼
而處理的詳情關鍵
public boolean handle()
{
...
getServer().handle(this);
...
}
複製代碼
這裏就對應處理到Server的handle實現
public void handle(HttpChannel channel) throws IOException, ServletException
{
final String target=channel.getRequest().getPathInfo();
//獲取channel上的Request
final Request request=channel.getRequest();
//獲取channel上的Response
final Response response=channel.getResponse();
...
if (HttpMethod.OPTIONS.is(request.getMethod()) || "*".equals(target))
{
if (!HttpMethod.OPTIONS.is(request.getMethod()))
response.sendError(HttpStatus.BAD_REQUEST_400);
handleOptions(request,response);
if (!request.isHandled())
handle(target, request, request, response);
}
else
handle(target, request, request, response);
...
}
複製代碼
對於web項目會有ServletHanlder,對應實現爲
@Override
public void doHandle(String target, Request baseRequest,HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
....
if (chain!=null)
chain.doFilter(req, res);
else
servlet_holder.handle(baseRequest,req,res);
...
}
複製代碼
這裏對應找到ServletHolder的handler方法
public void handle(Request baseRequest,
ServletRequest request,
ServletResponse response)
throws ServletException,
UnavailableException,
IOException
{
//獲取Servlet
Servlet servlet = getServlet();
if (baseRequest.isAsyncSupported() && !isAsyncSupported())
{
try
{
baseRequest.setAsyncSupported(false,this.toString());
servlet.service(request,response);
}
finally
{
baseRequest.setAsyncSupported(true,null);
}
}
else
//執行Servlet的service方法,並傳入request和response
servlet.service(request,response);
}
複製代碼
至此對於Spring來講已經連上了DispatcherServlet父類FrameWorkService的service方法,而後轉置DispatcherServlet的doService方法。
Jetty自己去鏈接了客戶端,自身去實現了Servlet的規範,在每一個創建的channel上,本身實現了請求request和response,經由handler,對獲取的web.xml配置中的servlet,關聯上Spring的對應servlet的init和service方法來處理請求