Servlet - 基础
Servlet
標簽 : Java與Web
HTTP協議
HTTP(hypertext transport protocol),即超文本傳輸協議.這個協議詳細規定了瀏覽器(Browser)和萬維網服務器(WebServer)之間互相通信的規則.其主要特點可簡單概括如下:
1) 簡單快速: 客戶端向服務器請求服務時,只需傳送請求方法和路徑, 因此使得HTTP服務器的程序規模小,通信速度快;
2) 靈活: HTTP允許傳輸任意類型的數據對象(傳輸類型由Content-Type控制);
3) 無連接: 無連接的含義是限制每次連接只處理一個請求;
4) 無狀態: 無狀態是指協議對于事務處理沒有記憶能力(如果后續處理需要前面的信息,則必須重傳.這樣可能導致每次連接傳送的數據量增大.但如果在服務器不需要先前信息時它的應答就會非常快快).
HTTP請求
一個HTTP請求通常包含三部分(中間已空行隔開):
請求行: (方法 /統一資源標識符URI/協議/版本) 請求頭: (Accept/Accept-Language等) 空行: (CRLF) 請求體: (攜帶的數據信息, GET請求沒有)HTTP請求可以使用HTTP標準中定義的所有請求類型, HTTP1.1支持7種請求類型, 但在互聯網應用中最為常用的只有GET與POST.
HTTP-GET
GET /WeChat/cc3200/get_status.do HTTP/1.1 Host: aliyun User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:45.0) Gecko/20100101 Firefox/45.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Connection: keep-alive- 請求頭解析
| User-Agent | 瀏覽器與操作系統信息 |
| Accept | 當前瀏覽器可以接收的文檔類型 |
| Accept-Language | 當前瀏覽器支持的語言 |
| Accept-Encoding | 當前瀏覽器支持的壓縮格式:服務器會把數據壓縮后再發送到網絡中傳輸 |
| Accept-Charset | 當前瀏覽器支持的編碼 |
| Connection | 當前瀏覽器支持的連接方式(keep-alive即保持一段時間的連接,默認為3000ms) |
| Cookie | 如果不是第一次訪問該網址,可能會在請求中把上次服務器響應的Cookie數據一并發送過去 |
HTTP-POST
POST /WeChat/cc3200/get_status.do HTTP/1.1 Content-Length: 36 Cache-Control: max-age=0 Origin: http://localhost:8080 Content-Type: application/x-www-form-urlencoded Referer: http://localhost:8080/test/ ...user_name=feiqing&user_password=pass- 請求頭解析
| Referer | 表明請求來自哪個頁面 |
| Content-Type | application/x-www-form-urlencoded:表單數據類型,說明會使用URL編碼來格式化數據 |
| Content-Length | 請求體長度 |
| user_name=feiqing&user_password=pass | 請求體: 請求攜帶的數據 |
HTTP響應
一個HTTP響應通常也包含三部分(中間已空行隔開):
響應行: (協議/狀態碼/描述) 響應頭: (Server/Content-Length/Set-Cookie等) 空行: (CRLF) 響應體: (攜帶的數據)HTTP響應是由服務器發送給瀏覽器的數據,瀏覽器會根據HTTP響應來解析并顯示內容:
HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Content-Length: 8 Date: Sun, 17 Apr 2016 12:39:11 GMT<html>... </html>- 響應頭解析
| Server | 服務器信息 |
| Content-Length | 響應實體長度 |
| Set-Cookie | 響應給客戶端的Cookie |
| Expires: -1; / Cache-Control: no-cache; / Pragma: no-cache; | 設置瀏覽器不要緩存數據 |
| Refresh | 自動刷新頁面 |
在HTML文件中可用<meta/>標簽來設置響應頭信息:
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">響應狀態碼
狀態碼說明了響應的真正含義:
| 200 | 請求成功 |
| 404 | 請求資源沒找到 |
| 500 | 服務器內部錯誤 |
| 302 | 重定向: 表示服務器要求瀏覽器重新再發一個請求到服務器指定的一個Location |
| 304 | 緩存未過期(服務器資源未曾修改), 詳細可參考理解HTTP/304響應 |
Tomcat
Tomcat是一個免費開源的Serlvet容器,它是Apache基金會的Jakarta項目中的一個核心項目,由Apache,Sun和其它一些公司及個人共同開發而成. 由于有了Sun的參與和支持, 因此最新的Servlet和Jsp規范總能在Tomcat中得到體現.主頁:http://tomcat.apache.org/.
Tomcat目錄結構
- bin: 存放可執行腳本文件(如startup.bat/startup.sh等)
- conf: 存放Tomcat相關配置文件:
- server.xml: 整個Tomcat運行環境配置(如端口號/虛擬主機等)
- web.xml: 部署描述符文件(定義了默認JSP/Servlet處理規則,是所有web項目中WEB-INF/web.xml的父文件)
- context.xml: 對所有應用的統一配置.
- lib:Tomcat類庫, 該目錄中的jar包所有項目共享.
- logs : Tomcat日志目錄.
- webapps:存放WEB應用,其每個子目錄都是一個項目;
- work:運行時生成的文件.
server.xml
<Server port="8005" shutdown="SHUTDOWN"><Service name="Catalina"><Connector port="8080" protocol="HTTP/1.1"connectionTimeout="20000"redirectPort="8443" /><Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /><Engine name="Catalina" defaultHost="localhost"><Realm className="org.apache.catalina.realm.LockOutRealm"><Realm className="org.apache.catalina.realm.UserDatabaseRealm"resourceName="UserDatabase"/></Realm><Host name="localhost" appBase="webapps"unpackWARs="true" autoDeploy="true"><Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"prefix="localhost_access_log." suffix=".txt"pattern="%h %l %u %t "%r" %s %b" /></Host></Engine></Service> </Server>- 元素解析
| <Server/> | 根元素,整個Tomcat的配置信息 |
| <Service/> | 服務(在<Server/>中只能有一個<Service/>) |
| <Connector/> | 連接 |
| <Engine/> | 引擎,是<Service/>組件核心 |
| <Host/> | 每個<Host/>元素表示一臺虛擬主機.每臺虛擬主機都有自己的主機名和項目目錄 |
| <Context/> | 每個<Context/>元素表示一個應用.如果應用在<Host/>的appBase指定的目錄下,那么可以不配置<Context/>元素,如果是外部應用,那么就必須配置<Context/> |
Tomcat配置
1. 配置端口號
編輯%CATALANA_HOME%\conf\server.xml文件中的<Connector/>元素
<!-- A "Connector" represents an endpoint by which requests are receivedand responses are returned. Documentation at :Java HTTP Connector: /docs/config/http.html (blocking & non-blocking)Java AJP Connector: /docs/config/ajp.htmlAPR (HTTP/AJP) Connector: /docs/apr.htmlDefine a non-SSL HTTP/1.1 Connector on port 8080 --> <Connector port="8080" protocol="HTTP/1.1"connectionTimeout="20000"redirectPort="8443" />2. 配置外部應用
配置外部應用之后, 項目就可以不用拷貝到webapps目錄下,自定義項目存放位置,其配置方式有兩種:
- 1: 修改server.xml
在<Host/>元素中添加<Context/>元素
如果指定path為空(path=”“), 則默認訪問的項目就是/home/www/test, 而不再是webapps下的ROOT.
- 2: 編輯conf/catalana/localhost目錄:
新增test.xml文件
存放到%CATALANA_HOME%/conf/catalana/localhost目錄下, 文件名即為應用名.
Servlet
Servlet技術核心就是Servlet接口,所有Servlet實現類都必須實現Servlet接口,Servlet容器(如Tomcat)會把Servlet類加載到內存并生成唯一實例,當有請求到來時調用其方法.
- 實現Servlet方式有三種:
- 實現javax.servlet.Servlet接口
- 繼承javax.servlet.GenericServlet類
- 繼承javax.servlet.http.HttpServlet類
Servlet
Servlet接口定義如下
public interface Servlet {public void init(ServletConfig config) throws ServletException;public ServletConfig getServletConfig();public void service(ServletRequest req, ServletResponse res)throws ServletException, IOException;public String getServletInfo();public void destroy(); }| init | 在第一次請求該Servlet(默認)或容器啟動時, Servlet容器就會調用init(), 且只調用一次 |
| service | 每次請求Servlet都會調用該方法 |
| destroy | 銷毀Servlet時(卸載應用/關閉容器時), 調用該方法 |
- HelloServlet
- web.xml
url-pattern
<url-pattern/>用來指定Servlet的訪問路徑,必須以/開頭.
- 可以在<servlet-mapping/>配置多個<url-pattern/>, 此時一個Servlet實例就綁定多個URL.
- 可以在<url-pattern/>中使用通配符*,可以使一個Servlet綁定一組URL, 但*不能出現在中間位置,也不能只有*通配符, 另外, 通配符只是一種模糊匹配URL的方式,如果存在更具體的<url-pattern/>,那么會優先選擇精確匹配.
配置在容器啟動時創建Servlet實例
<load-on-startup/>元素可以讓容器在啟動時就創建該Servlet實例(調用init()方法),注意<load-on-startup/>元素的值必須是>=0的整數,它代表容器啟動時創建Servlet實例的順序.
GenericService
GenericService抽象類實現了Servlet接口并完成以下工作:
1. 將init()方法中的ServletConfig賦給一個實例變量, 使他可以通過getServletConfig()來獲取.
2. 為Servlet接口的所有方法提供默認實現.
3. 提供方法來包裝ServletConfig.
- Generic部分代碼
HttpServlet
HttpServlet是GenericServlet的子類,它提供了對HTTP協議的支持.覆蓋了GenericServlet的service()方法,并新增了接受HttpServletRequest/HttpServletResponse參數的service()方法:
@Override public void service(ServletRequest req, ServletResponse res)throws ServletException, IOException {HttpServletRequest request;HttpServletResponse response;if (!(req instanceof HttpServletRequest &&res instanceof HttpServletResponse)) {throw new ServletException("non-HTTP request or response");}request = (HttpServletRequest) req;response = (HttpServletResponse) res;service(request, response); }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 logicdoGet(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 lessmaybeSetLastModified(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);} }- 原始的service()將請求/響應向下轉型為HttpServletRequest/HttpServletResponse, 并調用新的service(). 由于HttpServlet在新的service()方法中已經做了很多工作, 因此在繼承HttpServlet實現自動以Servlet時, 則只需覆蓋doGet()/doPost()等即可, 而沒有必要覆蓋service()(極少數情況需要覆蓋doHead()等)
注意: Request/Response向下轉型總會成功:因為在調用service()方法時,Servlet容器總會預計使用HTTP,從而直接創建并傳遞HttpServletRequest/HttpServletResponse實例.
- HelloHttpServlet
HttpServletRequest
對于每一個HTTP請求, Servlet容器會在調用service()方法時創建Request實例并傳遞給service形參, HttpServletRequest是Request在HTTP環境下的實例,其封裝了有關請求的信息:
- 封裝請求頭信息;
- 封裝請求正文數據(GET沒有正文);
- 提供請求轉發/包含功能;
- 作為域對象, 可以傳遞數據.
獲取請求頭
| String getHeader(String name) | Returns the value of the specified request header as a String. |
| Enumeration<String> getHeaderNames() | Returns an enumeration of all the header names this request contains. |
| long getDateHeader(String name) | Returns the value of the specified request header as a long value that represents a Date object. |
| Enumeration<String> getHeaders(String name) | Returns all the values of the specified request header as an Enumeration of String objects. |
| int getIntHeader(String name) | Returns the value of the specified request header as an int. |
| String getRemoteAddr() | Returns the Internet Protocol (IP) address of the client or last proxy that sent the request. |
| String getMethod() | Returns the name of the HTTP method with which this request was made, for example, GET, POST, or PUT. |
| String getContextPath() | Returns the portion of the request URI that indicates the context of the request. |
- 獲取請求來源
獲取請求參數
| String getParameter(String name) | Returns the value of a request parameter as a String, or null if the parameter does not exist. |
| Map<String,String[]> getParameterMap() | Returns a java.util.Map of the parameters of this request. |
| Enumeration<String> getParameterNames() | Returns an Enumeration of String objects containing the names of the parameters contained in this request. |
| String[] getParameterValues(String name) | Returns an array of String objects containing all of the values the given request parameter has, or null if the parameter does not exist. |
- 獲取微信請求消息
請求轉發/包含
Request提供了getRequestDispatcher()來獲取一個RequestDispatcher, 并由其提供請求轉發/請求包含功能.
| RequestDispatcher getRequestDispatcher(String path) | Returns a RequestDispatcher object that acts as a wrapper for the resource located at the given path. |
請求轉發/請求包含都是由多個Servlet協作完成一個請求, 因此需要從一個Servlet中跳到另一個Servlet中:
| void include(ServletRequest request, ServletResponse response) | Includes the content of a resource (servlet, JSP page, HTML file) in the response. |
| void forward(ServletRequest request, ServletResponse response) | Forwards a request from a servlet to another resource (servlet, JSP file, or HTML file) on the server. |
- 請求轉發: 原Servlet只會保留設置的響應頭信息.
- 請求包含: 原Servlet既會保留響應頭, 還會保留響應體內容.
注意: 請求轉發時, 可能會因為原Servlet設置了過多的響應體內容導致拋出異常java.lang.IllegalStateException: Cannot forward after response has been committed
域對象傳遞數據
由于請求轉發/請求包含都只是一次請求, 因此在多個Servlet之間都是共用一個Reqeust, 因此可以利用Request的在多個Servlet之間共享數據:
| Object getAttribute(String name) | Returns the value of the named attribute as an Object, or null if no attribute of the given name exists. |
| Enumeration<String> getAttributeNames() | Returns an Enumeration containing the names of the attributes available to this request. |
| void setAttribute(String name, Object o) | Stores an attribute in this request. |
| void removeAttribute(String name) | Removes an attribute from this request. |
HttpServletResponse
同Request, Servlet容器會在每次調用service()方法時創建Response實例并傳遞給service()形參, HttpServletResponse是Response綁定在HTTP環境下的實例, 其隱藏了將響應發送給瀏覽器的復雜性:
- 設置響應狀態碼;
- 設置響應頭信息;
- 設置響應正文;
設置響應狀態碼
| void setStatus(int sc) | Sets the status code for this response. |
| void sendError(int sc) | Sends an error response to the client using the specified status code and clears the buffer. |
| void sendError(int sc, String msg) | Sends an error response to the client using the specified status and clears the buffer. |
關于狀態碼的描述, 詳見HTTP協議部分介紹, 在此就不再贅述.
- 響應404
設置響應頭信息
| void setHeader(String name, String value) | Sets a response header with the given name and value. |
| void addHeader(String name, String value) | Adds a response header with the given name and value. |
| void setIntHeader(String name, int value) | Sets a response header with the given name and integer value. |
| void addIntHeader(String name, int value) | Adds a response header with the given name and integer value. |
| void addDateHeader(String name, long date) | Adds a response header with the given name and date-value. |
| void setDateHeader(String name, long date) | Sets a response header with the given name and date-value. |
| void sendRedirect(String location) | Sends a temporary redirect response to the client using the specified redirect location URL and clears the buffer. |
關于HTTP響應頭的描述, 詳見HTTP協議部分介紹, 在此就不再贅述.
- 設置禁用瀏覽器緩存(Cache-Control, pragma, expires)
- 設置重定向(302, Location)
HttpServletResponse還提供了另外一種重定向的方式, 直接使用sendRedirect()方法, 避免了以上的步驟:
@Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {response.sendRedirect("http://www.baidu.com"); }設置響應正文
Response提供了如下兩個方法來獲取輸出流對象以響應HTTP正文內容
| ServletOutputStream getOutputStream() | Returns a ServletOutputStream suitable for writing binary data in the response. |
| PrintWriter getWriter() | Returns a PrintWriter object that can send character text to the client. |
OutputStream傳輸二進制數據流(字節數據), 常用作文件下載; Writer傳輸字符數據, 常用作響應HTTP正文內容(如HTML/XML等).
注意: 在一個請求中,不能同時使用這兩個流, 否則會拋出IllegalStateException.
字符響應流
@Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {PrintWriter writer = response.getWriter();writer.print("<html>");writer.print("<h1>content</h1>");writer.print("</html>"); }- 緩沖區
PrintWriter的默認緩沖區大小為8K, 因此當響應數據大小<8K時, 數據存放在緩沖區, 而不會立刻發送到瀏覽器, 直到Servlet執行結束,因此如果希望馬上發送給瀏覽器, 需要調用Response的flushBuffer()方法手動刷新緩沖區.
ServletConfig
在容器初始化Servlet時, 會將一個ServletConfig實例傳給init()方法,其封裝了@WebServlet/部署描述符傳遞給Servlet的配置信息:
| String getInitParameter(String name) | Gets the value of the initialization parameter with the given name. |
| Enumeration<String> getInitParameterNames() | Returns the names of the servlet’s initialization parameters as an Enumeration of String objects. |
| ServletContext getServletContext() | Returns a reference to the ServletContext in which the caller is executing. |
- java
- web.xml
ServletContext
ServletConfig中提供了獲取ServletContext的方法getServletContext(), ServletContext代表Servlet應用程序,且每個應用程序僅有一個ServletContext實例,其在容器啟動時創建, 在容器關閉時銷毀, 因此可以利用其在多個Servlet中傳遞數據.
所有域對象都有存取數據的功能,因為域對象內部有一個Map,用來存儲數據,下面是ServletContext對象用來操作數據的方法:
| void setAttribute(String name, Object object) | Binds an object to a given attribute name in this ServletContext. |
| Object getAttribute(String name) | Returns the servlet container attribute with the given name, or null if there is no attribute by that name. |
| Enumeration<String> getAttributeNames() | Returns an Enumeration containing the attribute names available within this ServletContext. |
| void removeAttribute(String name) | Removes the attribute with the given name from this ServletContext. |
應用初始化參數
前面看到ServletConfig可以獲取針對本Servlet的初始化參數,而利用ServletContext可以獲取針對本應用程序的公共初始化參數:
| String getInitParameter(String name) | Returns a String containing the value of the named context-wide initialization parameter, or null if the parameter does not exist. |
| Enumeration<String> getInitParameterNames() | Returns the names of the context’s initialization parameters as an Enumeration of String objects, or an empty Enumeration if the context has no initialization parameters. |
- web.xml
- java
獲取資源
可以使用ServletContext來獲取Web應用下的資源路徑/資源流等內容:
| String getRealPath(String path) | Gets the real path corresponding to the given virtual path. |
| URL getResource(String path) | Returns a URL to the resource that is mapped to the given path. |
| InputStream getResourceAsStream(String path)` | Returns the resource located at the named path as an InputStream object. |
| Set<String> getResourcePaths(String path) | Returns a directory-like listing of all the paths to resources within the web application whose longest sub-path matches the supplied path argument. |
總結
以上是生活随笔為你收集整理的Servlet - 基础的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: LAMP(linux下apache+my
- 下一篇: Lvs+keepalived 高可用性负