Servlet開發
自MVC規範出現後,Servlet的責任開始明確下來,僅僅作為控制器使用,不再需要生成頁面標簽,也不再做為視圖層角色使用。servlet通常被稱為服務器端小程序,是運行在服務器端的程序 。用於處理用戶的請求及響應。
一. Servlet 介紹
Servlet是特殊的Java類,這個Java類必須繼承HTTPServlet,每個servlet可以響應用戶的請求。Servlet提供不同的方法用於相應用戶的請求
- doGet():用於相應客戶端的get請求
- doPost():用於響應客戶端的Post請求
- doPut():用於相應客戶端的put請求
- doDelete():用於相應客戶端的delete請求
事實上,客戶端請求通常只有get和post兩種,servlet為了響應這兩種請求,必須重寫doPost()和doGet()兩個方法。
大部分時候,servlet對於所有請求的響應都是都是完全一樣的。因此可以采用重寫一個方法來代替上面四個方法:只需要重寫service方法即可響應客戶端所有請求。
另外,HTTPServlet還包括另外兩個方法
- init(ServletConfig config):創建servlet實例時,使用該方法初始化Servlet資源。
- destroy():銷毀Servlet實例時,自動調用該方法回收資源
通常無需重寫這兩個方法。除非需要初始化Servlet時,完成某些資源初始化的方法,才考慮重寫init()方法。如果需要在銷毀Servlet之前,先完成某些資源的回收,比如關閉數據庫連接等,才需要重寫destroy方法。
Servlet與JSP的區別在於:
- Servlet沒有內置對象,原JSP中的內置對象必須由程序顯示創建
- 對於靜態的HTML標簽,servlet必須使用頁面輸出流逐行輸出
二. Servlet 配置
編輯好的servlet源文件並不能響應用戶請求,還必須將其編譯成class文件 。將編譯後的class文件放在WEB-INF/classes路徑下,如果servlet有包,則還應該將class文件放在對應的包路徑下。
為了讓Servlet響應用戶請求,還必須將Servlet配置在web應用中。配置Servlet時,需要修改web.xml文件》
從servlet3.0開始,配置servlet的兩種方式
- 在servlet類中使用@webServlet註解進行配置
- 通過在web.xml文件中進行配置
使用註解@webServlet配置Servlet時,常用的屬性有
屬性 | 是否必須 | 說明 |
asynsSupported | 否 | 指定該Servlet是否支持異步操作模式 |
displayName | 否 |
指定該Servlet的顯示名 |
initParam | 否 | 用於為該Servlet配置參數 |
loadOnStartup | 否 | 用於將該servlet配置成load-on-startup 的 Servlet |
name | 否 | 指定該servlet的名字 |
urlPatterns/value | 否 | 都指定servlet處理的URL |
如果打算使用註解來配置Servlet,有兩點需要指出:
- 不要在web.xml文件的根元素(<web-app.../>)中指定metadata-complete="true".
- 不要在web.xml文件中配置該servlet。
如果打算使用web.xml文件來配置該Servlet,則需要配置如下兩個部分。
- 配置Servlet的名字:對應web.xml文件中的<servlet/>元素
- 配置servlet的URL,對應web.xml文件中的<servlet-mapping/>元素。這一步是可選的。但如果沒有為Servlet配置URL,則該servlet不能響應該用戶請求。
三. JSP/Servlet 的生命周期
當Servlet在容器中運行時,其實例的創建及銷毀等都不是程序員決定的,而是由web容器進行控制的的
創建servlet實例有兩個時機
- 客戶端第一次請求某個Servlet時,系統創建該Servlet的實例:大部分的Servlet都是這種Servlet
- Web應用啟動時立即創建Servlet實例,即load-on-startup Servlet.
每個Servlet運行都遵循如下生命周期
- 創建Servlet實例
- Web容器調用Servlet的init方法,對Servlet進行初始化。
- Servlet初始化後,將一直保存在容器中,用於響應客戶端的請求,調用doGet()或者doPost()方法處理並響應請求,或者統一使用service方法
- web容器決定銷毀Servlet時,先調用Servlet的destroy()方法,通常在關閉Web應用時銷毀Servlet。
四. load-on-startup Servlet
應用啟動時就創建的Servlet通常是用於某些後臺服務的Servlet,或者攔截很多請求的Servlet,這種Servlet通常作為應用的基礎Servlet使用,提供重要的後臺服務。
配置load-on-startup的Servlet有兩種方式
- 在web.xml文件中通過<servlet.../>的子元素<load-on-startup.../>進行配置
- 通過@webServlet註解的loadOnStartup屬性指定
<load-on-startup.../>元素和loadOnStartup屬性都只接受一個整型值,這個整型值越小,Servlet越優先實例化
五. 訪問servlet的配置參數
配置Servlet時,還可以增加額外的配置參數。通過使用配置參數,可以實現提供更好的可移植性,避免將參數以硬代碼的方式寫在代碼程序中。
設置參數的兩種方式
- 通常@webServlet 的initParam屬性來指定
- 通過在web.xml文件的<servlet.../>元素中添加<init-param.../>指定
訪問Servlet參數通過servletConfig對象完成,ServletConfig提供如下方法。
String getInitParamter(String name):用於獲取初始化參數
六. 使用Servlet作為控制器
在標準的Java EE的MVC設計模式中Servlet僅作為控制器使用。JSP作為表現層技術,其作用有兩點
- 負責收集用戶請求參數
- 將應用的處理結果,狀態數據呈現給用戶
servlet僅充當控制器角色,它的作用類似於調度員;所有用戶請求都發給Servlet,Servlet調用Model來處理用戶請求,並調用JSP來呈現處理結果;或者Servlet直接調用JSP將應用的狀態數據呈現給用戶
model通常由JavaBean充當,所有業務邏輯,數據訪問邏輯都在model中實現。事實上,隱藏在Model下的可能還有很多豐富的組件,例如dao組件,領域對象等,下面介紹了一個使用Servlet作為控制器的MVC應用,該應用演示了一個簡單的登錄驗證。
<%@ page contentType="text/html;charset=UTF-8" language="java" errorPage="" %> <html> <head> <title>new document</title> </head> <body> <span style="color:red;font-weight: bold;"> <% if(request.getAttribute("err") != null){ out.println(request.getAttribute("err")+"<br/>"); } %> </span> 請輸入用戶名和密碼:<br/> <form id="login" method="post" action="login"> 用戶名: <input type="text" name="username"><br/> 密 碼:<input type="password" name="pass"/><br/> <input type="submit" value="登錄"> </form> </body> </html>
package java.gdut.servlet; import javax.servlet.RequestDispatcher; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpSession; import java.io.IOException; import java.sql.ResultSet; @WebServlet(name = "login",urlPatterns = "/login") public class LoginServlet extends javax.servlet.http.HttpServlet { protected void service(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException { String errMsg = ""; RequestDispatcher rd; String username = request.getParameter("username"); String pass = request.getParameter("pass"); try{ DbDao db = new DbDao("jdbc:mysql://localhost:3306/tb_test","com.mysql.jdbc.Driver","sherman","a123"); ResultSet rs = db.querry("select pass from user_inf where name=?",username); if(rs.next()){ if(rs.getString("pass").equals(pass)){ HttpSession session = request.getSession(true); session.setAttribute("name",username); rd=request.getRequestDispatcher("/welcome.jsp"); rd.forward(request,response); }else{ errMsg+="您的用戶密碼不符合,請重新輸入"; } }else{ errMsg+="您的用戶名不存在,請先註冊"; } }catch(Exception e){ e.printStackTrace(); } if(errMsg != null && !errMsg.equals("")){ rd = request.getRequestDispatcher("/login.jsp"); request.setAttribute("err",errMsg); rd.forward(request,response); } } }
package java.gdut.servlet; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; public class DbDao { private Connection conn; private String url; private String driver; private String user; private String pass; public DbDao(String url, String driver, String user, String pass) { this.url = url; this.driver = driver; this.user = user; this.pass = pass; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getDriver() { return driver; } public void setDriver(String driver) { this.driver = driver; } public String getUser() { return user; } public void setUser(String user) { this.user = user; } public String getPass() { return pass; } public void setPass(String pass) { this.pass = pass; } /** * 獲取數據庫連接 */ public Connection getConn() throws Exception { if (conn == null) { Class.forName(this.driver); conn = DriverManager.getConnection(this.url, this.user, this.pass); System.out.println(conn); } return conn; } /** * 執行插入 * @param sql * @param args * @return * @throws Exception */ public boolean insert(String sql,Object... args) throws Exception{ PreparedStatement prst = getConn().prepareStatement(sql); for (int i = 0; i <args.length ; i++) { prst.setObject(i+1,args[i]); } if(prst.executeUpdate() != 1){ return false; } return true; } /** * 執行查詢 * @param sql * @param args * @return * @throws Exception */ public ResultSet querry(String sql,Object... args)throws Exception{ PreparedStatement prst = getConn().prepareStatement(sql); for (int i = 0; i < args.length; i++) { prst.setObject(i+1,args[1]); } return prst.executeQuery(); } public void modify(String sql,Object... args)throws Exception{ PreparedStatement prst = getConn().prepareStatement(sql); for (int i = 0; i < args.length; i++) { prst.setObject(i+1,args[1]); } prst.executeUpdate(); prst.close(); } public void closeConn()throws Exception{ if(null != conn && !conn.isClosed()){ conn.close(); } } }
Servlet開發