Servlet基礎使用
程式設計師學習一門新的技術,永遠繞不開一個永恆的話題——
Hello World!
,在我們學習servlet
的時候也首先來實現一個Hello World!
入門程式!
Hello World!
入門程式
首先編寫一個Servlet
類,在其中返回hello world!
。
public class IndexServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setCharacterEncoding("utf-8"); PrintWriter out = resp.getWriter(); out.write("hello world!"); out.flush(); out.close(); } @Override protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doGet(req, resp); } }
然後需要配置web.xml
讓容器識別到。
<?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"> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <servlet> <!-- servlet名字 --> <servlet-name>helloworldServlet</servlet-name> <!-- servlet的類路徑,會將這個類命名為上面配置的名字 --> <servlet-class>cn.fzkj.servlet.IndexServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>helloworldServlet</servlet-name> <!-- 攔截路徑,當訪問hello的時候就會交給這個servlet處理 --> <url-pattern>/hello</url-pattern> </servlet-mapping> </web-app>
這樣配置了之後一個簡單的hello world
入門程式就完成了。
在servlet3.0
版本中,將xml配置進一步簡化了,可以使用註解來完成。上面的servlet
程式碼就可以修改為:
// 標註這是一個servlet,名字叫helloworldServlet,攔截的路徑是/hello,這個註解的效果和之前的xml配置一樣。 @WebServlet(name = "helloworldServlet", urlPatterns = "/hello") public class IndexServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setCharacterEncoding("utf-8"); PrintWriter out = resp.getWriter(); out.write("hello world!"); out.flush(); out.close(); } @Override protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doGet(req, resp); } }
servlet
Java Servlet 是執行在 Web 伺服器或應用伺服器上的程式,它是作為來自 Web 瀏覽器或其他 HTTP 客戶端的請求和 HTTP 伺服器上的資料庫或應用程式之間的中間層。 ——菜鳥教程
http
請求通過servlet
來和程式進行互動。
servlet
生命週期
servlet
生命週期指的是servlet
從建立到銷燬的整個過程。它遵循以下的幾個過程:
- 初始化後會呼叫
init()
方法 - 呼叫
service()
方法來處理客戶端請求 - 銷燬前呼叫
destroy()
方法 - 最後由垃圾回收器回收
建立servlet
的方式
建立servlet
有三種方式,這裡介紹其中兩種比較常用的方式。
第一種:實現Servlet
介面
public class ServletDemo1 implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("=== 初始化執行 ===");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("=== 處理業務邏輯 ===");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
System.out.println("=== 銷燬執行 ===");
}
}
*第二種:繼承HttpServlet
類
其實通過檢視原始碼可以發現,HttpServlet
類最終也是實現了Servlet
介面,只是對Servlet
中一些方法做了實現。
public abstract class HttpServlet extends GenericServlet {}
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {}
使用HttpServlet
類建立
@WebServlet(name = "httpServletDemo", urlPatterns = "/hello")
public class ServletHttpDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("hello world");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
HttpServlet
對Servlet
介面做了實現,同時提供了一些用於處理http
請求的方法,在繼承了HttpServlet
類之後就可以非常方便的處理使用者請求,而不用在去關心其他。
手寫一個HttpServlet
根據上面的發現,我們自己也可以來對Servlet
介面進行一個實現,來達到和HttpServlet
類相似的功能。說幹就幹。
由於Servlet
中實際會呼叫Service
方法進行業務處理,那就需要將該方法進行重寫,讓它支援http
請求。
/**
* @DESCRIPTION: 繼承了servlet介面,實現類似於HttpServlet相似的功能
* @AUTHOR: AnotherOne
* @DATE: 2021/7/14 16:23
*/
public class BaseServlet implements Servlet {
public BaseServlet() {
// do nothing
}
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("=== BaseServlet ===");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
// 重寫該方法
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest) servletRequest;
response = (HttpServletResponse) servletResponse;
this.service(request, response);
}catch (Exception e){
System.out.println("無效的請求");
}
}
// 實現不同請求方式的判斷與業務的轉發
protected void service(HttpServletRequest res, HttpServletResponse rep) throws ServletException, IOException {
String method = res.getMethod();
if (HttpType.GET.name().equalsIgnoreCase(method)){
this.doGet(res, rep);
} else if (HttpType.POST.name().equalsIgnoreCase(method)){
this.doPost(res, rep);
} else {
System.out.println("暫不支援的請求型別");
}
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
System.out.println("=== BaseServlet ===");
}
protected void doGet(ServletRequest req, ServletResponse res){
System.out.println("BaseServlet get 預設實現");
}
protected void doPost(ServletRequest req, ServletResponse res){
System.out.println("BaseServlet post 預設實現");
}
/**
* 定義http請求列舉類
*/
enum HttpType {
GET,
POST,
PUT,
DELETE
}
}
接下來來寫個業務類驗證一下。
@WebServlet(name = "servletDemo2", urlPatterns = "/demo2")
public class ServletHttpDemo2 extends BaseServlet {
@Override
protected void doGet(ServletRequest req, ServletResponse res) {
// super.doGet(req, res);
System.out.println("子類業務處理");
}
}
當訪問/demo2
時控制檯可以打印出子類業務處理
,說明效果不錯。
ServletConfig
物件
在容器建立Servlet
例項物件的時候,會將一些初始化引數封裝到ServletConfig
物件中,並且在呼叫物件的init(ServletConfig config)
方法時傳遞給該例項。所以可以通過該物件得到一些初始化引數資訊。
@WebServlet(name = "httpServletDemo", urlPatterns = "/demo", initParams = {
@WebInitParam(name = "name", value = "fzkj"),
@WebInitParam(name = "age", value = "23")
})
public class ServletHttpDemo extends HttpServlet {
private ServletConfig config;
@Override
public void init(ServletConfig config) throws ServletException {
this.config = config;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 獲取配置的name和age屬性值
System.out.println(config.getInitParameter("name"));
System.out.println(config.getInitParameter("age"));
// 獲取當前servlet名稱
System.out.println(config.getServletName());
// 獲取當前引數屬性名稱的列舉
Enumeration<String> names = config.getInitParameterNames();
while(names.hasMoreElements()){
System.out.println(names.nextElement());
}
// 獲取servletcontext物件
ServletContext servletContext = config.getServletContext();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
上面程式碼是使用註解的方式定義初始化引數。也可以使用xml配置的方式進行。
<servlet>
<servlet-name>indeServlet</servlet-name>
<servlet-class>cn.fzkj.servlet.IndexServlet</servlet-class>
<init-param>
<param-name>namespace</param-name>
<param-value>fzkj</param-value>
</init-param>
<init-param>
<param-name>age</param-name>
<param-value>23</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>indeServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
這兩者的效果並沒有什麼不同。
上述程式碼執行結果:
fzkj
23
httpServletDemo
name
age
ServletContext
物件
web容器在啟動的時候,會為每個web應用程式建立一個ServletContext
物件,它代表的就是當前web應用。
所以在同一個web應用中,所有的servlet
共享同一個ServletContext
物件,因此可以通過這個物件來實現不同Servlet
之間的通訊。
servletContext
實現多個servlet
之間資料共享
/**
* @DESCRIPTION: 多個servlet資料共享 和 demo2
* @AUTHOR: AnotherOne
* @DATE: 2021/7/15 11:15
*/
@WebServlet(name = "demo1", urlPatterns = "/demo1")
public class Demo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("utf-8");
ServletContext context = this.getServletContext();
context.setAttribute("name", "fzkj");
PrintWriter out = resp.getWriter();
out.write("資料共享成功");
out.flush();
out.close();
}
}
/**
* @DESCRIPTION: TODO 多個servlet資料共享 和 demo1
* @AUTHOR: AnotherOne
* @DATE: 2021/7/15 11:15
*/
@WebServlet(name = "demo2", urlPatterns = "/demo2")
public class Demo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("utf-8");
ServletContext context = this.getServletContext();
String name = (String)context.getAttribute("name");
PrintWriter out = resp.getWriter();
out.write("資料讀取成功" + name);
out.flush();
out.close();
}
}
讀取web應用初始化引數
/**
* @DESCRIPTION: TODO 讀取web應用初始化引數 -> 在xml中配置<context-param></context-param>
* @AUTHOR: AnotherOne
* @DATE: 2021/7/15 11:28
*/
@WebServlet(name = "demo3", urlPatterns = "/demo3")
public class Demo3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
System.out.println(context.getInitParameter("names"));
}
}
<context-param>
<param-name>names</param-name>
<param-value>fzkj123</param-value>
</context-param>
是不是有一種可以通過註解的方式建立web初始化引數?後面再來研究
ServletContext物件實現請求轉發
/**
* @DESCRIPTION: TODO 實現請求轉發,轉發給Demo5
* @AUTHOR: AnotherOne
* @DATE: 2021/7/15 11:39
*/
@WebServlet(name = "dmeo4", urlPatterns = "/demo4")
public class Demo4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
resp.setCharacterEncoding("utf-8");
resp.getOutputStream().write("這是再demo4中".getBytes());
// 轉發給demo5
RequestDispatcher rd = context.getRequestDispatcher("/demo5");
rd.forward(req, resp);
}
}
/**
* @DESCRIPTION: TODO 接收demo4的轉發請求並處理
* @AUTHOR: AnotherOne
* @DATE: 2021/7/15 11:43
*/
@WebServlet(name = "dmeo5", urlPatterns = "/demo5")
public class Demo5 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("utf-8");
resp.getOutputStream().write("這是再demo5中".getBytes());
}
}
由於再demo4的處理邏輯中將請求轉發給了demo5,所以實際上頁面會列印這是再demo5中
。
Filter
Servlet 過濾器可以動態地攔截請求和響應,以變換或使用包含在請求或響應中的資訊。
可以將一個或多個 Servlet 過濾器附加到一個 Servlet 或一組 Servlet。Servlet 過濾器也可以附加到 JavaServer Pages (JSP) 檔案和 HTML 頁面。呼叫 Servlet 前呼叫所有附加的 Servlet 過濾器。
Servlet 過濾器是可用於 Servlet 程式設計的 Java 類,可以實現以下目的:
- 在客戶端的請求訪問後端資源之前,攔截這些請求。
- 在伺服器的響應傳送回客戶端之前,處理這些響應。
根據規範建議的各種型別的過濾器:
- 身份驗證過濾器(Authentication Filters)。
- 資料壓縮過濾器(Data compression Filters)。
- 加密過濾器(Encryption Filters)。
- 觸發資源訪問事件過濾器。
- 影象轉換過濾器(Image Conversion Filters)。
- 日誌記錄和稽核過濾器(Logging and Auditing Filters)。
- MIME-TYPE 鏈過濾器(MIME-TYPE Chain Filters)。
- 標記化過濾器(Tokenizing Filters)。
- XSL/T 過濾器(XSL/T Filters),轉換 XML 內容。
過濾器通過 Web 部署描述符(web.xml)中的 XML 標籤來宣告,然後對映到您的應用程式的部署描述符中的 Servlet 名稱或 URL 模式。
當 Web 容器啟動 Web 應用程式時,它會為您在部署描述符中宣告的每一個過濾器建立一個例項。
Filter的執行順序與在web.xml配置檔案中的配置順序一致,一般把Filter配置在所有的Servlet之前。
——菜鳥教程
Filter也稱之為過濾器,它是Servlet技術中最實用的技術,Web開發人員通過Filter技術,對web伺服器管理的所有web資源:例如Jsp, Servlet, 靜態圖片檔案或靜態 html 檔案等進行攔截,從而實現一些特殊的功能。例如實現URL級別的許可權訪問控制、過濾敏感詞彙、壓縮響應資訊等一些高階功能。
它主要用於對使用者請求進行預處理,也可以對HttpServletResponse進行後處理。使用Filter的完整流程:Filter對使用者請求進行預處理,接著將請求交給Servlet進行處理並生成響應,最後Filter再對伺服器響應進行後處理。
FilterChain
:在一個web應用中,可以開發編寫多個Filter,這些Filter組合起來稱之為一個Filter鏈。
web伺服器根據Filter在web.xml檔案中的註冊順序,決定先呼叫哪個Filter,當第一個Filter的doFilter方法被呼叫時,web伺服器會建立一個代表Filter鏈的FilterChain物件傳遞給該方法。在doFilter方法中,開發人員如果呼叫了FilterChain物件的doFilter方法,則web伺服器會檢查FilterChain物件中是否還有filter,如果有,則呼叫第2個filter,如果沒有,則呼叫目標資源。
建立一個簡單的過濾器
/**
* @DESCRIPTION: TODO
* @AUTHOR: AnotherOne
* @DATE: 2021/7/15 12:47
*/
// 標註這是一個過濾器,urlPatterns用於配置攔截的url規則,符合規則的url才會被攔截
@WebFilter(filterName = "fzkj_filter_demo1", urlPatterns = "/*", initParams = {
@WebInitParam(name = "name", value = "fzkj_filter_demo1"),
@WebInitParam(name = "age", value = "23")
})
public class Demo1 implements Filter {
private FilterConfig config;
@Override
public void init(FilterConfig config) throws ServletException {
this.config = config;
System.out.println("=== demo1 過濾器初始化 ===");
// 獲取當前過濾器名稱
System.out.println(config.getFilterName());
System.out.println("=========================");
// 獲取初始化引數‘name’的值
System.out.println(config.getInitParameter("name"));
System.out.println("=========================");
// 獲取所有初始化引數的名稱
Enumeration<String> names = config.getInitParameterNames();
while(names.hasMoreElements()){
System.out.println(names.nextElement());
}
System.out.println("=========================");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
System.out.println("經過 " + this.config.getFilterName() + " filter");
// 交給下一過濾器
chain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
System.out.println("=== demo1 過濾器銷燬 ===");
}
}
Filter
的中的方法與用法和Servlet
非常相似,不在過多贅述
持續更新~~