Servlets是怎麼工做的?

ServletContext

When the servletcontainer (like Apache Tomcat) starts up, it will deploy and load all webapplications. When a webapplication get loaded, the servletcontainer will create the ServletContext once and keep in server's memory. The webapp's web.xml will be parsed and every <servlet>, <filter> and <listener> found in web.xml, or annotated with respectively @WebServlet, @WebFilter and @WebListener, will be created once and kept in server's memory as well. For all filters, the init() method will also be invoked immediately. When the servletcontainer shuts down, it will unload all webapplications, invoke the destroy() of all initialized servlets and filters, and finally the ServletContext and all Servlet, Filter and Listener instances will be trashed.java

servlet容器(如tomcat)啓動時,會部署和加載全部的web應用。web應用加載完成,servlet容器將會建立一次ServletContext並保存在server的內存。web應用的web.xml被解析而且定義在這個xml中的全部的servlet,filter,listener被找到,且也會建立一次並將其保存在server的內存。對於全部的filter,init()方法被直接調用。當Servlet容器關閉時,會卸載全部的web應用,並調用全部初始化了得servlets和filters的destory()方法,最終ServletContext,Servlet,Filter ,Listener 將被銷燬。web

When the Servlet in question has a <servlet><load-on-startup> or @WebServlet(loadOnStartup) value greater than 0, then its init() method will also immediately be invoked during startup. Those servlets are initialized in the same order as "load-on-startup" value represents, or if they are the same, then the order in the web.xml or @WebServlet classloading. Or, if the "load-on-startup" value is absent, then the init() method will only be invoked on very first HTTP request hitting the servlet in question.shell

當Servlet配置了<servlet><load-on-startup> 或@WebServlet(loadOnStartup) 的值大於0,它的init()方法會在servlet容器啓動時直接被調用。這些servlets的初始化順序是根據load-on-startup的值來體現的(注:值小的在值大的以前初始化,值越小月先初始化),若是他們相同,則按在web.xml或 @WebServlet 中定義的順序(越前的越先初始化)。還有,若是load-on-startup的值是缺失(注:何爲缺失,就是沒有填<load-on-startup></load-on-startup>或者沒有定義該標籤)的,那麼init()將會在第一個命中該Servlet的Http請求時被調用。windows

HttpServletRequest and HttpServletResponse

The servletcontainer is attached to a webserver which listens on HTTP requests on a certain port number, which is usually 8080 in development and 80 in production. When a client (user with a webbrowser) sends a HTTP request, the servletcontainer will create new HttpServletRequest and HttpServletResponse objects and pass it through the methods of the already-created Filter and Servlet instances whose url-pattern matches the request URL, all in the same thread.瀏覽器

servletcontainer是附屬於一個監聽開發環境端口爲8080、生產環境端口是80的Http請求的服務器。當一個客戶端(經過瀏覽器訪問的用戶)發送一個請求到服務器,servletcontainer 會建立一個HttpServletRequest和HttpServletResponse 對象,並在同一線程中將他們傳遞到已經建立而且符合他們路徑的的Filter 和Servlet。tomcat

In case of filters, the doFilter() method will be invoked. When its code calls chain.doFilter(request, response), then the request and response will continue to the next filter, or if there is none, hit the servlet. In case of servlets, the service() method will be invoked, which by default determines based on request.getMethod() which one of the doXxx() methods to invoke. If such method is absent on the actual servlet, then it will return HTTP 405 error.安全

在filters狀況下,doFilter()方法被調用。若是它的代碼是chain.doFilter(request, response),request和response 傳遞到下一個filter,若是沒有了,這傳遞到servlet。在servlets狀況下,service() 方法被調用,他將會根據request.getMethod()的值決定doXxx()被調用,若是這些方法在這個servlet不存在,他將會返回 HTTP 405 錯誤。服務器

The request object provides access to all information of the HTTP request, such as the request headers and the request body. The response object provides facility to control and send the HTTP response the way you want, such as setting headers and the body (usually with HTML content from a JSP file). When the HTTP response is committed and finished, then both the request and response objects will be trashed (actually, most containers will cleanup the state and recycle the instance for reuse).cookie

request 對象提供了訪問http 請求全部信息(如,請求頭 ,請求體.)的方法。response 對象提供根據你想要方式的靈活控制和發送http response,如設置響應頭和響應體(一般是以js形式呈現Html內容)。當HTTP response提交和完成,request和response都將被銷燬。session

HttpSession

When a client visits the webapp for the first time and/or the HttpSession is to be obtained for the first time by request.getSession(), then the servletcontainer will create a new HttpSession object, generate a long and unique ID (which you can get by session.getId()), and store it in server's memory. The servletcontainer will also set a Cookie in the Set-Cookie header of the HTTP response with JSESSIONID as cookie name and the unique session ID as cookie value.

當一個客戶端第一次訪問web應用的或者HttpSession對象已經在第一次訪問時經過request.getSession()獲取了,servletcontainer 將會建立一個先得HttpSession對象,生成一個很長且惟一的ID(你能夠經過session.getId())來得到),而且保存在服務器內存。servletcontainer也會經過在HTTP response的Set-Cookie header來設置一個cookie名爲JSESSIONID,值爲惟一的session id的cookie。

As per the HTTP cookie specification (a contract a decent webbrowser and webserver has to adhere), the client (the webbrowser) is required to send this cookie back in the subsequent requests in the Cookie header as long as the cookie is valid (i.e. is not expired and has the right domain and path). Using browser builtin HTTP traffic monitor you can check them (press F12 in Chrome / Firefox23+ / IE9+ and check Net/Network tab). The servletcontainer will determine the Cookie header of every incoming HTTP request for the presence of the cookie with the name JSESSIONID and use its value (the session ID) to get the associated HttpSession from server's memory.

根據HTTP cookie specification(一個正式的瀏覽器和web服務必須遵循的合同/協議),客戶端在cookie是有效(如:未過時而且有正確的domain和path)時,必需要將該cookie設置到隨後請求的Cookie header中發回給服務器,經過瀏覽器的內置的Http流量監控器你能夠查看他們(按F12在Chrome / Firefox23+ / IE9+ ,選擇Net/Network選項卡)。servletcontainer 將肯定每個進來的http請求是否存在Cookie 名是JSESSIONID的cookie,並根據該cookie的值從服務器的內存中取得關聯的HttpSession。(注:若是是要實現Session共享,須要作什麼?)

The HttpSession lives until it has not been used for more than the <session-timeout> time, a setting you can specify in web.xml, which defaults to 30 minutes. So when the client doesn't visit the webapp anymore for over 30 minutes, then the servletcontainer will trash the session. Every subsequent request, even though with the cookie specified, will not have access to the same session anymore. The servletcontainer will create a new one.

HttpSession存活到超出在web.xml中<session-timeout>設置的值沒有使用爲止,<session-timeout>默認值是30分鐘。所以,客戶端在超過三分鐘沒用訪問web 應用,servletcontainer將會銷燬這個session。每一個後續的請求,即便是有指定的cookie,也不能再訪問相同的session。servletcontainer 將會建立一個新的。

On the other hand, the session cookie on the client side has a default lifetime which is as long as the browser instance is running. So when the client closes the browser instance (all tabs/windows), then the session will be trashed at the client side. In a new browser instance the cookie associated with the session won't be sent anymore. A new request.getSession() would return a brand new HttpSession and set a cookie with a brand new session ID.

另外一方後面,session cookie在客戶端也有一個與瀏覽器進程運行( 瀏覽器進程關閉了運行就結束了)同樣長的默認的生命時間。所以客戶端關閉了瀏覽器進程(全部的標籤和窗口),客戶端的session也將被銷燬。在一個新的瀏覽器進程中session關聯的cookie也再也不會被髮送給到服務器。一個新的request.getSession()將會返回一個全新的HttpSession而且設置一個全新的session ID。

In a nutshell 簡而言之

  • The ServletContext lives as long as the webapp lives. It's been shared among all requests in all sessions.
  • The HttpSession lives as long as the client is interacting with the webapp with the same browser instance and the session hasn't timed out at the server side yet. It's been shared among all requests in the same session.

  • The HttpServletRequest and HttpServletResponse lives as long as the client has sent it until the complete response (the webpage) is arrived. It is not being shared elsewhere.

  • Any Servlet, Filter and Listener lives as long as the webapp lives. They are being shared among all requests in all sessions.

  • Any attribute which you set in ServletContext, HttpServletRequest and HttpSession will live as long as the object in question lives. The object itself represents the "scope" in bean management frameworks such as JSF, CDI, Spring, etc. Those frameworks store their scoped beans as an attribute of closest matching scope.

  • ServletContext 生命週期是webapp的生命週期。他在全部回話的全部請求中共享。

  • HttpSession 生命週期是客戶端與webapp 交互的瀏覽器進程存活時間而且服務端的session沒有超時。他在同一個回話中的全部請求中共享。

  • HttpServletRequest和HttpServletResponse 生命週期是發送請求到完整的響應到達爲止。他不共享。

  • 任何的Servlet,Filter,Listener 生命週期是webapp的生命週期。他在全部回話的全部請求中共享。

  • 任何設置在相應對象上的屬性器生命週期是響應對象的生命週期。對象自己表明的生命週期也表示bean管理框架(如:JSF,CDI,Spring,等等)中的scope。這些框架經過接近範圍的屬性來保存他們bean的做用域。

Threadsafety 線程安全

That said, your major concern is possibly threadsafety. You should now have learnt that Servlets and filters are shared among all requests. That's the nice thing of Java, it's multithreaded and different threads (read: HTTP requests) can make use of the same instance. It would otherwise have been too expensive to recreate, init() and destroy() it on every single request.

考慮到線程安全。如今已經學習到Servlets和filters 全部請求共享的。這就是java的好處,他的多線程和不一樣線程可以不夠使用相同的實例。不然每一個請求上將會在從新建立,初始化和銷燬花費巨大的時間。

But you should also realize that you should never assign any request or session scoped data as an instance variable of a servlet or filter. It will be shared among all other requests in other sessions. That's threadunsafe! The below example illustrates that:

可是你應該意識到毫不能將request和session範圍的數據定義爲servlet 或filter的實例變量。他將會被其餘回話的其餘請求共享。這就是線程安全!下面的例子闡述了他:

public class ExampleServlet extends HttpServlet {

private Object thisIsNOTThreadSafe;

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    Object thisIsThreadSafe;

    thisIsNOTThreadSafe = request.getParameter("foo"); // BAD!! Shared among all requests!
    thisIsThreadSafe = request.getParameter("foo"); // OK, this is thread safe.
}

}

See also 參考

相關文章
相關標籤/搜索