Servlet
sun公司基於java開發動態web的一門技術
其中在這些API中提供了一個介面就是Servlet
servlet有兩個實現類,HttpServlet、GenericServlet,其中GenericServlet是HttpServlet的父類
在servlet3.0版本後可以不用寫web.xml檔案,可以直接使用註解定義載入。
我們直接在類上面加入註解
@WebServlet(urlPatterns = "/demo")
HelloServlet
-
新建一個普通專案,不要勾選maven中webapp模版,並刪掉專案中的src目錄,這個空工程就是Maven父工程
-
在pom.xml中匯入依賴
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> </dependency> <!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api --> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.3</version> </dependency>
如果匯入失敗,手動下載jar包放在本地倉庫中
-
建立子modul,勾選webapp模版
-
子專案中pom.xml新增
<parent>
標籤關於Maven父子工程:
在父工程的
pom.xml
中會多出<modules> <module>servlet01</module> </modules>
在子工程的
pom.xml
中會多出(Maven3.6.0之後一開始會有,專案載入完後會被自動清除掉,自己手動新增即可)<parent> <artifactId>javaweb01</artifactId> <groupId>com.zh1z3ven</groupId> <version>1.0-SNAPSHOT</version> </parent>
父專案中的jar包,子專案可以直接使用
-
修改子專案中的
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" metadata-complete="true"> </web-app>
-
子專案中新建
java
和resources
目錄,並修改對應目錄屬性 -
新建com.zh1z3ven.servlet包,編寫servlet程式
寫一個普通類,實現servlet介面
這裡需要注意父專案的pom.xml檔案中的
<scope>
標籤,注意如果父專案存在<scope>
標籤且值為provided
時,子modul無法成功引用父類jar包在Maven中依賴的域有:compile、provided、runtime、system、test、import
一、compile(預設)
當依賴的scope為compile的時候,那麼當前這個依賴的包,會在編譯的時候被加入進來,並且在打包(mvn package)的時候也會被加入進來。
編譯範圍有效,在編譯與打包時都會加入進去。二、provided
當依賴的scope為provided的時候,在編譯和測試的時候有效,在執行(mvn package)進行打包時不會加入。比如, 我們開發一個web應用,在編譯時我們需要依賴servlet-api.jar,但是在執行時我們不需要該 jar包,因為這個jar 包已由web伺服器提供,如果在打包時又被加入進去,那麼就可能產生衝突。此時我們就可以使用 provided 進行範圍修飾。
import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; public class HelloServlet extends HttpServlet { //由於get和post只是請求相互實現的方式,可以互相呼叫,業務邏輯都一樣 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { PrintWriter writer = resp.getWriter(); //響應流 writer.print("Hello Servlet"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
-
修改
web.xml
註冊servlet,編寫servlet對映<!-- 註冊servlet--> <servlet> <servlet-name>hello</servlet-name> <!-- 繫結servlet實現類--> <servlet-class>com.zh1z3ven.servlet.HelloServlet</servlet-class> </servlet> <!-- 註冊servlet路由--> <servlet-mapping> <servlet-name>hello</servlet-name> <!-- 設定訪問路由--> <url-pattern>/hello</url-pattern> </servlet-mapping>
-
配置Tomcat,配置完後先點選
apply
再點選ok
-
啟動測試
-
關於target目錄
Servlet原理
客戶端請求通過瀏覽器傳送到web伺服器,請求包含請求頭請求體,之後變成request物件傳給servlet,servlet呼叫service方法處理請求並返回response。而對於Servlet來說,當伺服器處理請求時,先根據URL中的路徑查詢是否在web.xml的<servlet-mapping>
(servlet對映)<url-pattern>
標籤有對應的值,有的話根據此Servlet對應的類名,通過該類的例項化物件呼叫service方法處理請求並返回響應。
Servlet-Mapping
-
一個servlet可以指定一個對映路徑
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
-
一個servlet可以指定多個對映路徑
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello1</url-pattern> </servlet-mapping>
-
一個servlet可以指定通用對映路徑
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello/*</url-pattern> </servlet-mapping>
-
指定一些字尾或字首等...,例如.do
以這個字尾結尾,不管前面uri加了多少層目錄都可以訪問到
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>
-
預設請求路徑
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
處理簡單報錯Servlet
ErrorServelt
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class ErrorServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html");
resp.setCharacterEncoding("utf-8");
PrintWriter writer = resp.getWriter();
writer.print("<h1>404</h1>");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
Web.xml中新增,這裡<url-pattern>/*</url-pattern>
雖然設定的預設萬用字元,但是url輸入其他註冊好的servlet還是會優先走對應的servlet。指定了固定路徑的對映預設優先順序最高,如果找不到才會走預設的處理請求。
<servlet>
<servlet-name>error</servlet-name>
<servlet-class>com.zh1z3ven.servlet.ErrorServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>error</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
ServletContext
Web容器在啟動的時候會為每個Web程式建立一個ServletContext物件,代表當前的Web應用物件。這個ServletContext可以儲存一些資料,也可以供Servlet從中讀取資料。類似於Servlet之間的中介,實現Servlet直接的資料共享。
ServletContext:Servlet上下文
可根據HttpServlet的例項化物件呼叫getServletContext獲取
ServletContext servletContext = this.getServletContext();
1、共享資料
在一個servlet中建立的資料可以在另一個servlet中拿到。ServletContext物件是可以被多個Servlet共用的
寫入資料
servletContext.setAttribute("username",username); //將資料寫入ServletContext中
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Hello Servlet doGet");
//this.getInitParameter() 初始化引數
//this.getServletConfig() sevlet配置
//this.getServletContext() sevlet上下文
//獲得ServletContext物件
ServletContext servletContext = this.getServletContext(); //this指代當前這個類本身
String username = "zh1z3ven";
servletContext.setAttribute("username",username); //將資料寫入ServletContext中
}
}
讀取資料
public class GetServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Hello GetServlet ");
ServletContext servletContext = this.getServletContext();
String username = (String) servletContext.getAttribute("username"); //強制型別轉換
PrintWriter writer = resp.getWriter();
writer.print("<h1>username: </h1>" + username);
}
}
Web.xml
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.zh1z3ven.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>getname</servlet-name>
<servlet-class>com.zh1z3ven.servlet.GetServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>getname</servlet-name>
<url-pattern>/getname</url-pattern>
</servlet-mapping>
測試結果
需要先訪問/s2/hello,才會觸發HelloServlet類中的doGet方法將name寫入ServletContext中;再訪問/s2/getname時觸發GetServlet中的doGet方法去讀取ServletContext中的name並根據程式碼回顯到頁面上。
2、獲取初始化引數getInitParameter
引數為web.xml中的<context-param>
下的引數
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
String url = servletContext.getInitParameter("url");
resp.setCharacterEncoding("utf-8");
resp.setContentType("text/html");
PrintWriter writer = resp.getWriter();
writer.print(url);
}
Web.xml
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
</context-param>
<servlet>
<servlet-name>gp</servlet-name>
<servlet-class>com.zh1z3ven.servlet.ServletDemo03</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>gp</servlet-name>
<url-pattern>/gp</url-pattern>
</servlet-mapping>
3、請求轉發requestDispatcher
獲取ServletContext上下文物件,呼叫requestDispatcher的forward方法轉發請求
RequestDispatcher requestDispatcher = servletContext.getRequestDispatcher("/gp"); //請求轉發目標路徑
requestDispatcher.forward(req,resp); //呼叫forward方法轉發請求
Web.xml
<servlet>
<servlet-name>sd4</servlet-name>
<servlet-class>com.zh1z3ven.servlet.ServletDemo04</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>sd4</servlet-name>
<url-pattern>/sd4</url-pattern>
</servlet-mapping>
class
public class ServletDemo04 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
//RequestDispatcher requestDispatcher = servletContext.getRequestDispatcher("/gp"); //請求轉發目標路徑
//requestDispatcher.forward(req,resp); //呼叫forward方法轉發請求
servletContext.getRequestDispatcher("/gp").forward(req,resp);
}
}
如果出現tomcat爆500的情況,可以嘗試關掉tomcat然後maven clean一下專案的target,在重啟tomcat可能就好了
4、讀取資原始檔
Properties,如果在專案的java目錄下有寫properties檔案的話預設是不會被匯出的,需要在pom.xml中多加一段build的配置,使得java目錄下的資原始檔也可以被匯出。
但是不管是resources和java目錄下的properties檔案都會被打報導WEB-INF/classes目錄下,也就是classpath路徑
思路:利用this.getServletContext().getResourceAsStream()
方法返回一個流,通過Properties
類的例項化物件呼叫load()
方法載入這個流獲取資料。
public class PropertiesServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/com/zh1z3ven/servlet/test.properties"); //返回InputStream流
Properties properties = new Properties(); //例項化Properties物件
properties.load(is); //loadInputStream流
String username = properties.getProperty("username");
String password = properties.getProperty("password");
resp.getWriter().print(username + ":" + password);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
Web.xml
<servlet>
<servlet-name>sd5</servlet-name>
<servlet-class>com.zh1z3ven.servlet.PropertiesServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>sd5</servlet-name>
<url-pattern>/sd5</url-pattern>
</servlet-mapping>
資原始檔路徑,同理如果是在原始碼目錄下,只需要寫好相對路徑即可。
例如:此test.properties
路徑為:
/WEB-INF/classes/com/zh1z3ven/servlet/test.properties
HttpServletResponse
web伺服器會將客戶端發來的http請求進行處理,生成兩個物件HttpServletRequest和HttpServletResponse。
- 如果要獲取客戶端發來的請求資訊:HttpServletRequest
- 如果要處理需要響應給客戶端的資訊:HttpServletResponse
1、簡單分類
向客戶端傳送資料的方法
ServletOutputStream getOutputStream() throws IOException; //普通輸出流
PrintWriter getWriter() throws IOException; //處理中
向瀏覽器傳送響應頭的方法
void setCharacterEncoding(String var1);
void setContentLength(int var1);
void setContentLengthLong(long var1);
void setContentType(String var1);
void setDateHeader(String var1, long var2);
void addDateHeader(String var1, long var2);
void setHeader(String var1, String var2);
void addHeader(String var1, String var2);
void setIntHeader(String var1, int var2);
void addIntHeader(String var1, int var2);
void setStatus(int var1);
2、常見應用
- 向瀏覽器輸出訊息(getWriter,getOutputStream)
- 下載檔案
- 獲取下載檔案的路徑
- 下載的檔名
- 讓瀏覽器支援我們能下載的東西
- 獲取下載檔案的輸入流
- 建立緩衝區
- 獲取OutputStream物件
- 將FileOutputStream流寫入到buffer緩衝區
- 使用OutputStream將buffer緩衝區的資料輸出到客戶端
public class FileServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("=====================123131===========================");
//獲取下載檔案的路徑
//String realPath = this.getServletContext().getRealPath("/1.png");//獲取絕對路徑
String realPath = "/Users/b/Desktop/javacode/JavaWeb_Maven/javaweb-02-servlet/response/src/main/resources/1.png";
System.out.println("下載檔案的路徑: " + realPath);
//下載的檔名
String fileName = realPath.substring(realPath.lastIndexOf("/") + 1);
System.out.println(fileName);
//讓瀏覽器支援我們能下載的東西
resp.setHeader("Content-Disposition", "attachment;filename=" + fileName);
//獲取下載檔案的輸入流
FileInputStream fis = new FileInputStream(realPath);
//建立緩衝區
int len = 0;
byte[] buffer = new byte[1024];
//獲取OutputStream物件
ServletOutputStream servletOutputStream = resp.getOutputStream();
//將FileOutputStream流寫入到buffer緩衝區
while ((len = fis.read(buffer)) != -1){
// 使用OutputStream將buffer緩衝區的資料輸出到客戶端
servletOutputStream.write(buffer, 0, len);
}
//釋放資源
servletOutputStream.close();
fis.close();
}
重定向
方法:
void sendRedirect(String var1) throws IOException;
Demo:
public class RedirectServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.sendRedirect("/download");
}
}
重定向與轉發
重定向與轉發都會使使用者訪問其他資源,只是發生位置不同,請求轉發放生在服務端,只能訪問該專案內部資源。重定向是第一次收到使用者請求後讓客戶端重新訪問一個新的url地址,客戶一共請求了兩次。
HttpServletRequest
1、Request常用方法
String getMethod() //獲取請求方式
String getContextPath() //獲取虛擬路徑
String getServletPath() //獲取Servlet路徑
String getQueryString() //獲取get請求方式引數
String getRequestURI() //獲取請求uri
StringBuffer getRequestURL() //獲取URL
String getProtocol() //獲取協議版本
String getRemoteAddr() //獲取客戶端ip
String getHeader(String name) //通過請求頭的名稱獲取請求頭的值
Enumeration<String> getHeaderNames() //獲取所有的請求頭名稱
String getParameter(String var1); //獲取一個指定引數的值
Enumeration<String> getParameterNames();
String[] getParameterValues(String var1); //獲取多個引數的值,返回一個String陣列
Map<String, String[]> getParameterMap();
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8"); //設定編碼
String username = req.getParameter("username");
String password = req.getParameter("password");
String[] hobbys = req.getParameterValues("hobbys");
System.out.println("================================");
System.out.println(username);
System.out.println(password);
System.out.println(Arrays.toString(hobbys)); //獲取string陣列
System.out.println("================================");
req.getRequestDispatcher("/success.jsp").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}