Servlet要點、請求與響應
阿新 • • 發佈:2019-01-10
一: Servlet要點
01.Servlet規範和搭建JavaWeb專案
什麼是Service:
Service是JavaEE的元件,service是一個程式類,要求必須實現javax.service.Service介面.
也是JavaEE中的規範.伺服器其實是Service的實現,
搭建標準的JavaWeb的專案結構:
1.建立一個java專案:HelloServletWeb:
2.在HelloServletWeb中建立一個資料夾webapp,表示web專案的根;
3.在webAPP中建立WEB-INF 資料夾,
4.在WEB-INF中建立資料夾:lib,classes
5.在WEB-INF中去Tomcat根/conf拷貝web.xml檔案,只需要保留根元素.
6.把當前專案的classpath路徑改成webapp/WEB-INF下的classes中.
02.第一個Servlet的程式
Service的第一個程式編寫步驟:
1):拷貝Tomcat根/lib/servlet-api.jar到專案的WEB-INF/lib目錄中,並做build path.
2):編寫Servlet程式,使之實現javax.servlet.Servlet介面,並覆蓋介面中的方法.
public class HelloServlet implement javax.servlet.Servilet(... 實現方法...)
3):發現方法中的引數出現arg0,arg1的情況是因為沒有關聯Servlet的原始碼.
是否關聯原始碼和程式最終的執行沒有關係,只是在開發階段引數美觀,可閱讀原始碼.
4):在service(ServletRequest req, ServletResponse res)方法,列印一句話.
引導:此時HelloServlet類,和Tomcat一點關係都沒有:我們需要告訴Tomcat來幫我們管理HelloServlet類,(Tomcat是Servlet的容器,會負責Servlet物件的宣告)所以,得以配置的形式,告訴Tomcat來幫我們管理自定義的Servlet類.
5 ):Servlet配置:在web.xml檔案中的配置,切記<url-pattern>/hello</url-pattern>中的hello要加"/"
6):部署專案並訪問:server.xml中:<Context docBase="D:\Java EE\Servlet\webapp" path="day3">
訪問:http://ip:port/contextPath/資源名
http://localhost:80/day3/hello
03.Servlet的生命週期方法
Servlet的生命週期:建立物件,初始化操作,執行操作,銷燬操作.
javax.servlet.Servlet介面中的方法:
String getServletInfo():獲取Servlet的資訊(Servlet的作者,版本,版權相關).
ServletConfig getServletConfig();獲取Servlet的配置資訊物件.
生命週期方法:在WEB的生命週期中(Tomcat啟動-->Tomcat關閉),Servlet是單例的.
構造器:在服務端程式第一次被請求的時候,呼叫,只被呼叫一次.
void init(ServletConfig config);在構造器執行完畢之後,呼叫init方法,也只會執行一次.
void service(ServletRequest req, ServletResponse resp):每一次請求都會執行該方法.
void destroy();正常關閉Tomcat才會執行(該方法不一定會被執行,我們沒有必要在其中編寫掃尾的操作).
總結: 構造器--->init方法---> {servlet方法}迴圈 ---->destory方法
注意:
1):Servlet類的構造器必須使用public修飾.
2):Servlet類必須是無引數構造器.
原因:底層建立Servlet物件:class.newInstance()方式.--->公共的無引數構造器.
總結:保證Servlet必須有一個公共的無引數構造器,方便Tomcat底層建立Servlet的物件.
04.Servlet的請求流程-圖片版
![Servlet的請求流程圖](https://img-blog.csdn.net/20180214221646461?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2VpeGluXzQwMTYxNzA4/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
05.Servlet的請求流程-文字版
1:瀏覽器先發送請求:http://localhost:80/day3/hello
2:DNS解析域名(忽略)
3:Tomcat解析請求:/day3/hello. 上下文路徑:/day3 資源的名稱/hello
4:解析Tomcat根/conf/server.xml檔案,獲取其中所有的<Context/>元素,並找到path屬性為/day3的元素.
<Content docBase="D:\Java EE\Servlet\webapp" path="day3"/>.
再讀取該<Content/>元素,再獲取docBase屬性值,該屬性值就是當前訪問的WEB專案的根路徑.
5:從該web的根路徑/WEB-INF下找到web.xml檔案.
6:讀取web.xml檔案,獲取所有的<url-patten>元素,並判斷哪一個<url-pattern>的文字內容為:/hello.
找不到:報404錯誤. 找到:GOTO 7.
7:通過/hello ,找到當前Servlet的全限定名:com._520it._01_hello.HelloServlet
8:從Servlet的例項快取池中去獲取com._520it._01_hello.HelloServlet對應的物件
Map<String,Servlet> cache = ....;
Servlet obj = cache.get("com._520it._01_hello.HelloServlet");
if(obj==null){
//第一次請求:GOTO 9
}else{
//非第一次請求:GOTO 12
}
9:使用反射建立Servlet物件.
Servlet obj = Class.forName("com._520it._01_hello.HelloServlet").newInstance();
10:把建立的Servlet物件,儲存到Servlet例項快取池中,供下一次請求使用.
cache.put("com._520it._01_hello.HelloServlet",obj);
11:容器建立ServletConfig物件,並呼叫init方法,來完成初始化操作.
obj.init(config);
12:容器建立ServletRequest和ServletResponse物件,並呼叫service方法.
obj.service(req,resp);
13:在service方法中對,當前請求的客戶端做響應
06.ServletConfig介面獲取初始化引數
ServletConfig介面:表示Servlet的資訊配置物件(web.xml中,當前Servlet的配置資訊).
其中方法:
String getServletName();獲取當前Servlet的名字,<servlet-name>元素的文字內容.
ServletContext getServletContext();獲取當前Servlet的指定的引數名獲取初始化引數值.
Enumeration<String> getInitParameterName();獲取當前Servlet的所有初始化引數的名字.
07.Servlet繼承體深入講解系列
public class Servlet1 extends MyHttpServlet{
private static final long serialVersionUID = 1L;
public void service(HttpServletRequest req, HttpServletResponse resp) {
super.service(req, resp);
System.out.println("utf");
}
}
public class MyHttpServlet extends MyGenericServlet{
private static final long serialVersionUID = 1L;
//只能處理一般的請求
public void service(ServletRequest req, ServletResponse resp){
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
service(request,response);//獲取請求方式,GET/POST
}
//專門處理Http的請求
public void service(HttpServletRequest req, HttpServletResponse resp){
String method = req.getMethod();//獲取請求方式,GET/POST
if("GET".equals(method)){
doGet(req,resp);
}else{
doPost(req,resp);
}
}
//專門用於處理POST請求
private void doPost(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("Servlet1.doPost()");
}
//專門用於處理GET請求
private void doGet(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("Servlet1.doGet()");
}
}
abstract public class MyGenericServlet implements Serializable,Servlet,ServletConfig{
private static final long serialVersionUID = 1L;
private ServletConfig config = null;
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
//專門暴露給子類,讓子類編寫自身的初始化程式碼
public void init(){
//NO OP
}
@Override
abstract public void service(ServletRequest req, ServletResponse resp) throws ServletException, IOException;
//把ServletConfig物件暴露給子類訪問
public ServletConfig getServletConfig() {
return this.config;
}
public String getServletInfo() {
return null;
}
public void destroy() {
}
public String getServletName() {
return config.getServletName();
}
public ServletContext getServletContext() {
return config.getServletContext();
}
public String getInitParameter(String name) {
return config.getInitParameter(name);
}
public Enumeration<String> getInitParameterNames() {
return config.getInitParameterNames();
}
}
8.Servlet繼承體系總結和開發Servlet
![servlet的繼承體系圖](https://img-blog.csdn.net/20180215180723738?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2VpeGluXzQwMTYxNzA4/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
前端web實際開發的時候只需繼承HttpServlet類,呼叫service()方法,並記得刪除方法中的super.service(req,resp);
9.HttpServletRequest介面和常用方法
String getInitParameter(String name):獲取初始化引數,初始化引數是開發人員設定的
String getParameter(String name):獲取使用者輸入的引數,引數是不同的使用者輸入的(通過表單)
常用方法:
01.String getMethod():返回請求方式:如GET/POST 只有表單中method=post才是post
02.String getRequestURI();返回請求行中的資源名字部分:如/test/index.html
03.StringBuffer getRequestURL():返回瀏覽器位址列中所有的資訊
04.String getContextPath():返回當前專案的上下文路徑(<Context/>元素的path屬性值.)
05.String getRemoteAddr():返回發出請求的客戶機的IP地址
06.String getHeader(String name):返回指定名稱的請求頭的值
獲取請求引數的方法:
01.String getParameter(String name):返回指定引數名字的值
02.String[] getParameterValues(String name):返回指定名字引數的多個引數值.
03.Enumeration<String> getParameterNames()返回所有的引數名的Enumeration物件
04.Map<String,String[]> getParameterMap():返回所有的引數和值所組成的Map物件.
10.處理請求中文亂碼問題
在Tomcat中,對於POST和GET請求,都預設才有ISO-8859-1的編碼方式.
而ISO-8850-1不支援中文,所以亂碼.
解決方案:
1:按照ISO-8859-1把亂碼恢復成二進位制形式
byte[] data = username.getBytes("ISO-8859-1");
2:再把二進位制形式的資料使用UTF-8重新編寫
String username = new String(data,"UTF-8");
一個引數需要使用兩行程式碼來轉碼,如果有N個引數,需要轉N次程式碼重複.
解決方案:
針對於POST的請求方式: req.setCharacterEncoding("UTF-8");
注意:1.只對POST有效. 2:必須放在獲取任意引數之前.
針對於GET的請求方式:修改Tomcat中的server.xml配置檔案,對GET方式的預設編碼.
URIEncoding="ISO-8859-1"改為"UTF-8"
11.HttpServletResponse介面和常用方法
ServletResponse介面:響應物件.封裝了獲取響應資訊的方法.
HttpServletResponse介面:ServletResponse的子介面,可以處理HTTP響應的方法.
常用方法:
01.OutputStream getOutputStream();獲取位元組輸出流物件. 輸出圖片等
注意:可以一起輸出多個getOutputStream(),但是不可以跟getWriter()一起使用:
02.PrintWriter getWriter():獲取字元流輸出物件 輸出英文或者中文等
03.設定輸出資料的MIME型別:resp.setContentType("text/html");
04.設定輸出資料的編碼方式:resp.setCharacterEncoding("UTF-8");
可以將上述3,4兩行程式碼合併成一行程式碼:resp.setContentType("text/html;charset=UTF-8");
注意:必須先設定MIME型別和編碼,再獲取輸出流,否則沒有效果.
12.網頁版的簡易計算器
靜態網頁版
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
<title>Insert title here</title>
</head>
<body>
<form action='/cal' method='post'>
<input type='number' name='num1' />
<select name='op'>
<option>+</option>
<option>-</option>
<option>*</option>
<option>/</option>
</select>
<input type='number' name='num2' />
<input type='submit' value=' = '/>
<input type='text' value='' disabled />
</form>
</body>
</html>
動態的Servlet網頁
public class CalServlet extends HttpServlet{
private static final long serialVersionUID = 1L;
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
//================================
//2:接受表單中的資料
String snum1 = req.getParameter("num1");
String op = req.getParameter("op");
String snum2 = req.getParameter("num2");
String result = "";
if(haslength(snum1) && haslength(snum2)){
Integer num1 = Integer.valueOf(snum1);
Integer num2 = Integer.valueOf(snum2);
if("+".equals(op)){
result = num1+num2+"";
}else if("-".equals(op)){
result = num1*num2+"";
}
}
//================================
//1:輸出一個計算器的介面
out.print("<form action='/cal' method='post'>");
out.print("<input type='number' name='num1' value='"+snum1+"'/>");
out.print("<select name='op'>");
out.print("<option>+</option>");
out.print("<option>-</option>");
out.print("<option>*</option>");
out.print("<option>/</option>");
out.print("</select>");
out.print("<input type='number' name='num2' value='"+snum2+"'/>");
out.print("<input type='submit' value=' = ' />");
out.print("<input type='text' value='"+result+"' disabled/>");
}
private boolean haslength(String str){
return str!=null && !"".equals(str.trim());
}
}