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 received
and responses are returned. Documentation at :
Java HTTP Connector: /docs/config/http.html (blocking & non-blocking)
Java AJP Connector: /docs/config/ajp.html
APR (HTTP/AJP) Connector: /docs/apr.html
Define 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/>
元素
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Context path="/test/" docBase="/home/www/test"/>
<!-- Access log processes all example.
Documentation at: /docs/config/valve.html
Note: The pattern used is equivalent to using pattern="common" -->
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log." suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
如果指定path爲空(path=”“), 則默認訪問的項目就是/home/www/test, 而不再是webapps下的ROOT.
- 2: 編輯conf/catalana/localhost目錄:
新增test.xml文件
<Context docBase="/home/www/test"/>
存放到%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
/**
* @author jifang.
* @since 2016/4/17 8:32.
*/
public class HelloServlet implements Servlet {
private ServletConfig config;
public void init(ServletConfig config) throws ServletException {
System.out.println("init()...");
this.config = config;
System.out.println("config: <" + config + ">");
}
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
System.out.println("service()...");
System.out.println("req: <" + req + ">, res: <" + res + ">");
res.getWriter().print("<h1>HelloServlet</h1>");
}
public void destroy() {
System.out.println("destroy()...");
}
public ServletConfig getServletConfig() {
return this.config;
}
public String getServletInfo() {
return null;
}
}
- web.xml
<web-app 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"
version="3.0"
metadata-complete="true">
<display-name>JavaWeb</display-name>
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.fq.web.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello_servlet.do</url-pattern>
</servlet-mapping>
</web-app>
url-pattern
<url-pattern/>
用來指定Servlet的訪問路徑,必須以/
開頭.
- 可以在
<servlet-mapping/>
配置多個<url-pattern/>
, 此時一個Servlet實例就綁定多個URL.- 可以在
<url-pattern/>
中使用通配符*
,可以使一個Servlet綁定一組URL, 但*
不能出現在中間位置,也不能只有*
通配符, 另外, 通配符只是一種模糊匹配URL的方式,如果存在更具體的<url-pattern/>
,那麼會優先選擇精確匹配.配置在容器啓動時創建Servlet實例
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.fq.web.servlet.HelloServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<load-on-startup/>
元素可以讓容器在啓動時就創建該Servlet實例(調用init()
方法),注意<load-on-startup/>
元素的值必須是>=0
的整數,它代表容器啓動時創建Servlet實例的順序.
GenericService
GenericService
抽象類實現了Servlet
接口並完成以下工作:
1. 將init()
方法中的ServletConfig
賦給一個實例變量, 使他可以通過getServletConfig()
來獲取.
2. 爲Servlet接口的所有方法提供默認實現.
3. 提供方法來包裝ServletConfig
.
- Generic部分代碼
public abstract class GenericServlet
implements Servlet, ServletConfig, java.io.Serializable
{
private transient ServletConfig config;
public GenericServlet() { }
public void destroy() {
}
public String getInitParameter(String name) {
ServletConfig sc = getServletConfig();
if (sc == null) {
throw new IllegalStateException(
lStrings.getString("err.servlet_config_not_initialized"));
}
return sc.getInitParameter(name);
}
public Enumeration<String> getInitParameterNames() {
ServletConfig sc = getServletConfig();
if (sc == null) {
throw new IllegalStateException(
lStrings.getString("err.servlet_config_not_initialized"));
}
return sc.getInitParameterNames();
}
public ServletConfig getServletConfig() {
return config;
}
public ServletContext getServletContext() {
ServletConfig sc = getServletConfig();
if (sc == null) {
throw new IllegalStateException(
lStrings.getString("err.servlet_config_not_initialized"));
}
return sc.getServletContext();
}
public String getServletInfo() {
return "";
}
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
public void init() throws ServletException {
}
public abstract void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
public String getServletName() {
ServletConfig sc = getServletConfig();
if (sc == null) {
throw new IllegalStateException(
lStrings.getString("err.servlet_config_not_initialized"));
}
return sc.getServletName();
}
}
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 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);
}
}
- 原始的
service()
將請求/響應向下轉型爲HttpServletRequest
/HttpServletResponse
, 並調用新的service()
. 由於HttpServlet
在新的service()
方法中已經做了很多工作, 因此在繼承HttpServlet
實現自動以Servlet時, 則只需覆蓋doGet()
/doPost()
等即可, 而沒有必要覆蓋service()
(極少數情況需要覆蓋doHead()
等)
注意: Request/Response向下轉型總會成功:因爲在調用
service()
方法時,Servlet容器總會預計使用HTTP,從而直接創建並傳遞HttpServletRequest
/HttpServletResponse
實例.
- HelloHttpServlet
/**
* @author jifang.
* @since 2016/4/20 19:48.
*/
@WebServlet(name = "HelloHttpServlet", urlPatterns = "/hello_http_servlet.do")
public class HelloHttpServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("doPost() ...");
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("doGet() ...");
}
}
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. |
- 獲取請求來源
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String referer = request.getHeader("Referer");
String userAgent = request.getHeader("User-Agent");
composeResponse(referer, userAgent, response);
}
private void composeResponse(String referer, String userAgent, HttpServletResponse response) throws IOException {
response.setHeader("Content-Type", "text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
if (!Strings.isNullOrEmpty(referer)) {
writer.print("<h1>來源地址: " + referer + "</h1>");
} else {
writer.print("<h1>來自瀏覽器地址欄</h1>");
}
writer.print("<hr>");
writer.print("<h1>來源信息: " + userAgent + "</h1>");
}
獲取請求參數
方法 | 描述 |
---|---|
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. |
- 獲取微信請求消息
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Element root;
String xml = request.getParameter("xml");
try {
if (!Strings.isNullOrEmpty(xml)) {
root = new SAXReader().read(new StringReader(xml)).getRootElement();
} else {
String data = CharStreams.toString(new InputStreamReader(request.getInputStream()));
root = new SAXReader().read(new StringReader(data)).getRootElement();
}
} catch (DocumentException | IOException e) {
LOGGER.error("parse wx xml error", e);
throw new RuntimeException();
}
// ...
}
請求轉發/包含
Request提供了getRequestDispatcher()
來獲取一個RequestDispatcher
, 並由其提供請求轉發/請求包含功能.
Request方法 | 描述 |
---|---|
RequestDispatcher getRequestDispatcher(String path) |
Returns a RequestDispatcher object that acts as a wrapper for the resource located at the given path. |
請求轉發/請求包含都是由多個Servlet協作完成一個請求, 因此需要從一個Servlet中跳到另一個Servlet中:
RequestDispatcher方法 | 描述 |
---|---|
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
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// response.sendError(404, "nothing!!");
response.setStatus(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)
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setHeader("Cache-Control", "no-cache");
response.setHeader("pragma", "no-cache");
response.setDateHeader("expires", -1);
}
- 設置重定向(302, Location)
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
response.setHeader("Location", "http://www.baidu.com");
}
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
public void init(ServletConfig config) throws ServletException {
this.config = config;
Enumeration<String> names = config.getInitParameterNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
String value = config.getInitParameter(name);
System.out.println(name + " -> " + value);
}
}
- web.xml
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.fq.web.servlet.HelloServlet</servlet-class>
<init-param>
<param-name>admin</param-name>
<param-value>com.fq</param-value>
</init-param>
<init-param>
<param-name>e-mail</param-name>
<param-value>[email protected]</param-value>
</init-param>
</servlet>
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
<context-param>
<param-name>admin</param-name>
<param-value>feiqing</param-value>
</context-param>
<context-param>
<param-name>e-mail</param-name>
<param-value>zhujifang666@163.com</param-value>
</context-param>
- java
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext context = getServletContext();
String admin = context.getInitParameter("admin");
String email = context.getInitParameter("e-mail");
System.out.printf("admin: %s%n", admin);
System.out.printf("e-mail: %s%n", email);
}
獲取資源
可以使用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. |