柚子快報(bào)激活碼778899分享:Servlet
柚子快報(bào)激活碼778899分享:Servlet
一 Servlet概述
1.1 什么是Servlet
Servlet 是 Server Applet 的簡(jiǎn)稱,是用Java編寫的,可以運(yùn)行在 Web 服務(wù)器(Tomcat)上的程序,是Sun公司制定的一套規(guī)范(接口)。
Servlet的主要用途:
接受、處理來自瀏覽器端(BS架構(gòu)中的B端)的請(qǐng)求和用戶輸入 響應(yīng)來自數(shù)據(jù)庫(kù)或者服務(wù)端(BS架構(gòu)中的S端)產(chǎn)生的數(shù)據(jù)到瀏覽器端,動(dòng)態(tài)構(gòu)建網(wǎng)頁(yè)。
1.2 手動(dòng)實(shí)現(xiàn)Servlet小程序
1.2.1 實(shí)現(xiàn)步驟
自定義一個(gè)類型,實(shí)現(xiàn)Servlet接口或者繼承HttpServlet類 使用javac指令,將源文件編譯成字節(jié)碼文件 將編譯完的字節(jié)碼文件按照一定的組織結(jié)構(gòu)打包,并編寫web.xml配置文件 將整個(gè)組織結(jié)構(gòu)放入Tomcat的webapps文件夾中 啟動(dòng)Tomcat,在瀏覽器上輸入符合規(guī)范的地址去訪問Servlet。
1.2.2 具體實(shí)現(xiàn)
步驟1: 編寫一個(gè)類HelloWorldServlet,繼承HttpServlet類型
package com.zrgj;
?
import java.io.IOException;
?
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
?
public class HelloWorldServlet extends HttpServlet{
@Override
public void service(HttpServletRequest request,HttpServletResponse response)
throws ServletException,IOException{
response.getWriter().println("hello world");
}
}
步驟2: 編譯,生成.class文件
javac HelloWorldServlet.java -classpath ? xxxxx\xxxxxx\servlet-api.jar
步驟3:項(xiàng)目的組織結(jié)構(gòu)
appName 項(xiàng)目名稱(主要用來區(qū)分其他項(xiàng)目的)
-- ? WEB-INF文件夾
-- classes文件夾: 用來存儲(chǔ)class文件的整個(gè)路徑
? com\zrgj\HelloWorldServlet.class
-- lib(可選文件夾)
-- web.xml: 當(dāng)前項(xiàng)目的主配置文件
-- index.jsp
? -- css
? -- js
? -- html
比如:
myfirstservlet ?
-- WEB-INF
? ? --classes
? ? --web.xml
web.xml里的內(nèi)容如下:
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">
? ?
? ?<servlet>
? ? ? ?<servlet-name>aservlet-name>
? ? ? ?<servlet-class>com.zrgj.HelloWorldServletservlet-class>
? ?servlet>
? ?<servlet-mapping>
? ? ? ?<servlet-name>aservlet-name>
? ? ? ?<url-pattern>/hellourl-pattern>
? ?servlet-mapping>
web-app>
步驟4:將整個(gè)文件夾組織結(jié)構(gòu),拷貝到tomcat的webapps文件夾里。這一步的專業(yè)術(shù)語叫部署項(xiàng)目
步驟5:?jiǎn)?dòng)tomcat
找到tomcat的bin目錄下的startup.bat可執(zhí)行文件,啟動(dòng)后,不要關(guān)閉。
步驟6:在瀏覽器上輸入地址,向tomcat容器發(fā)送請(qǐng)求
http://localhost:8080/myfirstweb/hello ,回車后,應(yīng)該會(huì)看到hello world字樣
部署完項(xiàng)目后的執(zhí)行流程:
1. 瀏覽器向服務(wù)器發(fā)送請(qǐng)求,訪問服務(wù)器上的某一個(gè)項(xiàng)目下的某一個(gè)資源。 ?
? 路徑如下:http://服務(wù)器IP:服務(wù)器端口/項(xiàng)目名/資源路徑
2. 服務(wù)器收到請(qǐng)求后,會(huì)先確認(rèn)并找到該項(xiàng)目
3. 如果找到了該項(xiàng)目,會(huì)加載里面的web.xml配置文件
4. 然后通過資源路徑,去匹配servlet-mapping里的url-pattern,來確定servlet-name。
5. 根據(jù)servlet-name,定位到對(duì)應(yīng)的servlet里的servlet-class。
6. 根據(jù)servlet-class標(biāo)簽里的類全名,去項(xiàng)目的classes目錄下查找對(duì)應(yīng)的類文件
7. 執(zhí)行類文件里的service方法,將數(shù)據(jù)最終響應(yīng)給瀏覽器
1.3 Idea開發(fā)Servlet程序
第一步:創(chuàng)建java項(xiàng)目(java module):MyFirstWeb。
第二步:添加web框架支持。 也就是找到add Frameworks Support,選擇web application.
幫助我們自動(dòng)維護(hù)web項(xiàng)目的組織結(jié)構(gòu): web-inf,web.xml
第三步:在WEB-INF下創(chuàng)建lib文件夾,將第三方j(luò)ar包放入,比如servlet-api.jar文件
注意:別忘記 add as library
第四步:編寫Servlet組件
第五步:配置web.xml
第六步:關(guān)聯(lián)tomcat:
edit configuration...
?
add new ...
?
tomcat server (注意千萬不要用tomEE server)
?
local:
?
選擇tomcat home
?
fix ? 定義項(xiàng)目部署的虛擬名稱
?
apply ok
第七步:?jiǎn)?dòng)tomcat 進(jìn)行測(cè)試
1.4 常見的HTTP錯(cuò)誤代碼
404:Not found,表示您要請(qǐng)求的資源服務(wù)器無法幫您找到。原因可能如下:
1. 沒有寫web.xml配置文件
2. 地址欄上的地址信息寫錯(cuò)了,可能沒有寫部署的項(xiàng)目名稱,或者項(xiàng)目名后的資源路徑寫錯(cuò)誤
3. web.xml里的url-pattern對(duì)應(yīng)上了,但是兩個(gè)name沒有對(duì)應(yīng)上。
6:上面都改正確了,但是忘記了重新部署。 ? ^_^
405:Method Not Allowed
servlet組件里沒有重寫service/doGet/doPost方法,或者是方法寫的不標(biāo)準(zhǔn),比如參數(shù),異常的聲明等。
500:Internal Server Error,表示服務(wù)器內(nèi)部發(fā)生錯(cuò)誤
1. servlet-class標(biāo)簽里的類名書寫有誤。
2. 編寫的類型不是一個(gè)servlet組件,也就是沒有繼承HttpServlet或者實(shí)現(xiàn)Servlet接口
3. Servlet 代碼中拋出異常導(dǎo)致的,也就是servlet類里你寫的代碼有問題
二 Servlet工作原理
2.1 Servlet的生命周期
servlet容器是如何創(chuàng)建Servlet對(duì)象,如何為Servlet對(duì)象分配資源,如何調(diào)用Servlet對(duì)象的方法來處理請(qǐng)求,以及如何銷毀Servlet對(duì)象的整個(gè)過程。而這個(gè)過程可以分為四個(gè)階段,分別是實(shí)例化階段、初始化階段、就緒階段、銷毀階段
2.1.1 階段一:實(shí)例化
什么是實(shí)例化?
即容器調(diào)用Servlet的構(gòu)造器,創(chuàng)建一個(gè)Servlet對(duì)象 MySecondServlet s = new MySecondServlet() ? <------不是程序員調(diào)用的,而是容器幫助我們調(diào)用并創(chuàng)建的。
什么時(shí)候?qū)嵗?/p>
情形一,開始容器里面沒有Servlet對(duì)象,只有收到請(qǐng)求后才會(huì)創(chuàng)建Servlet對(duì)象(默認(rèn)情況) 當(dāng)瀏覽器發(fā)送請(qǐng)求:http://localhost:8088/employee_v1/listEmp
tomcat容器會(huì)收到該請(qǐng)求,該請(qǐng)求是請(qǐng)求的是ListEmp.class這個(gè)組件,因此tomcat容器會(huì)幫助程序員創(chuàng)建ListEmp這個(gè)組件的對(duì)象
?
其他的沒有訪問過的組件,比如AddEmp,DelEmp等這些組件,在容器中沒有。。。。。。 情形二,容器啟動(dòng)之后就立即創(chuàng)建相應(yīng)的實(shí)例, 需要添加配置
總結(jié):
1. 誰負(fù)責(zé)實(shí)例化servlet類(也可以叫組件)的對(duì)象 : tomcat(servlet容器)
2. 什么時(shí)機(jī)實(shí)例化:
- 瀏覽器發(fā)送請(qǐng)求這個(gè)組件時(shí)創(chuàng)建
- 添加配置參數(shù)load-on-startup, 在tomcat服務(wù)器啟動(dòng)時(shí)創(chuàng)建。
2.1.2 階段二:初始化
什么是初始化?
容器在創(chuàng)建好Servlet對(duì)象后,會(huì)立即調(diào)用該對(duì)象的init方法 一般情況下,我們不用寫init方法,因?yàn)間enericServlet已經(jīng)提供了init方法的實(shí)現(xiàn)(將容器傳遞過來的ServletConfig對(duì)象保存下來,并且,提供了getServletConfig方法來獲取ServletConfig對(duì)象)。 init方法只會(huì)執(zhí)行一次
默認(rèn)情況下,init方法不需要重寫。但是有的時(shí)候,如果你有這方面的需求時(shí),比如將公司信息作為初始化參數(shù),用于init方法來到程序中,就可以重寫init進(jìn)行獲取。
Servlet的初始化參數(shù)如何配置,在web.xml中添加如下形式:
注意:init-param的位置必須放在load-on-startup前面
如何讀取Servlet的初始化參數(shù)
String ServletConfig.getInitParameter("school")
總結(jié):
servlet容器在實(shí)例化servlet對(duì)象后,會(huì)自動(dòng)調(diào)用init方法進(jìn)行初始化操作。程序員可以重寫init方法。 使用ServletConfig對(duì)象來讀取初始化參數(shù)。
2.1.3 階段三:就緒
容器收到請(qǐng)求后調(diào)用Servlet對(duì)象的service()來處理請(qǐng)求
2.1.4 階段四:銷毀
容器依據(jù)自身算法刪除Servlet對(duì)象,刪除前會(huì)調(diào)用destroy() 只會(huì)執(zhí)行一次 可以override destroy方法來實(shí)現(xiàn)自己的處理邏輯 應(yīng)用程序卸載時(shí)一定會(huì)調(diào)用destroy方法
web.xml
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">
組件:
package com.shuilidianli.web;
import javax.servlet.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class LifeServlet extends GenericServlet {
public void service(ServletRequest request,
ServletResponse resp) throws ServletException, IOException {
//獲取初始化階段的數(shù)據(jù),
//獲取ServletConfig對(duì)象
ServletConfig servletConfig = super.getServletConfig();
//獲取初始化參數(shù)
String school = servletConfig.getInitParameter("school");
System.out.println(school);
}
@Override
public void init() throws ServletException {
System.out.println("------init--------");
}
@Override
public void destroy() {
System.out.println("------銷毀--------");
}
}
2.2 請(qǐng)求方式
2.2.1 請(qǐng)求方式介紹
不同的請(qǐng)求方式在數(shù)據(jù)傳輸時(shí),會(huì)有所不同;在表單提交以及服務(wù)器處理時(shí)都會(huì)采用不同的方式。瀏覽器針對(duì)每種請(qǐng)求方式也會(huì)使用不同的緩存技術(shù),提高相應(yīng)的處理速度。
常用的請(qǐng)求方式有:
get: 請(qǐng)求服務(wù)器上指定的資源
post: 向服務(wù)器上的資源提交數(shù)據(jù)
head: 和get相似
put: 上傳資源
delete: 刪除資源
get請(qǐng)求:
瀏覽器發(fā)送get請(qǐng)求的場(chǎng)景:
1. 在地址欄單純的輸入一個(gè)地址
2. a標(biāo)記產(chǎn)生的請(qǐng)求
3. form表單默認(rèn)提交方式,也是get請(qǐng)求
注意:
1. get請(qǐng)求,如果帶有一些提交數(shù)據(jù),比如用戶名,密碼等。那么這些數(shù)據(jù)會(huì)被添加到地址欄中,不安全。
2. get請(qǐng)求提交的數(shù)據(jù)量比較小,在4KB以內(nèi)。
post:
瀏覽器發(fā)送post請(qǐng)求的場(chǎng)景:
1. form表單的method屬性的值為post時(shí)。
注意:
要傳輸給服務(wù)器的處理數(shù)據(jù)都會(huì)在正文(http信息數(shù)據(jù)的body)中存儲(chǔ)。相對(duì)安全
小貼士:
如果想要提交form表單,兩個(gè)屬性要知道:
1. method屬性,用于指定請(qǐng)求方式,默認(rèn)不寫是get請(qǐng)求
2. action屬性,是必填項(xiàng),因?yàn)閍ction的值就是地址欄上的url-pattern.
2.2.2 service/doGet/doPost/
實(shí)際上:當(dāng)瀏覽器發(fā)送請(qǐng)求后,會(huì)默認(rèn)執(zhí)行servlet組件繼承過來的service方法,然后service方法里的邏輯:String method = req.getMethod(); 會(huì)獲取請(qǐng)求的方法,
如果是get請(qǐng)求,會(huì)執(zhí)行doGet(req, resp); 如果是post請(qǐng)求,會(huì)執(zhí)行doPost(req, resp);
上述代碼說明:程序員在寫servlet時(shí),可以選擇覆蓋service方法,也可以選擇覆蓋doGet() 或者是doPost方法。前提如果是get請(qǐng)求,可以直接覆蓋doGet(), 如果是post請(qǐng)求,可以直接覆蓋doPost()
注意:如果重寫了service方法,就不會(huì)再執(zhí)行doGet/doPost方法,除非重寫的service方法里調(diào)用了doGet/doPost
package com.shuilidianli.web;
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 TestMethodServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("-----doGet-------");
PrintWriter pw = resp.getWriter();
pw.println("get");
pw.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("-----doPost-------");
doGet(req,resp);
}
}
web.xml
test.html
2.3 請(qǐng)求對(duì)象
web服務(wù)器(tomcat容器)在接收到瀏覽器發(fā)送過來的請(qǐng)求后,發(fā)送過來的所有通信數(shù)據(jù)都會(huì)被web服務(wù)器進(jìn)行封裝和提供。這些信息主要被封裝到兩個(gè)對(duì)象上。
一個(gè)是HttpServletRequest類型的對(duì)象:
1. 瀏覽器發(fā)送的所有數(shù)據(jù)都會(huì)被web服務(wù)器封裝到HttpServletRequest類型的對(duì)象上,此類型提供了相應(yīng)的方法可以從對(duì)象上獲取相關(guān)的數(shù)據(jù)。比如請(qǐng)求行的數(shù)據(jù)、消息頭、實(shí)體內(nèi)容(正文、參數(shù)值)
2.作用:
讀取和設(shè)置http的請(qǐng)求數(shù)據(jù)
取得和設(shè)置cookies
實(shí)現(xiàn)請(qǐng)求轉(zhuǎn)發(fā)
取得路徑信息
標(biāo)識(shí)HTTP會(huì)話
2.3.1 參數(shù)值的獲取
不管是get請(qǐng)求提交的少量數(shù)據(jù),還是post請(qǐng)求提交的大量的數(shù)據(jù)。那么服務(wù)器端就要接受到這些數(shù)據(jù),并處理。
瀏覽器發(fā)送的數(shù)據(jù)有以下兩種情況:
1:1的情況:
localhost:8088/day42_servlet/web?username=zhangsan&password=123456
上述的地址,?后面是要傳入到服務(wù)端的數(shù)據(jù),username對(duì)應(yīng)一個(gè)值,password對(duì)應(yīng)一個(gè)值
1:N的情況
localhost:8088/day42_servlet/web?username=zhangsan&password=123456&hobby=book&hobby=movie&gender=f
上述的地址,?后面是要傳入到服務(wù)端的數(shù)據(jù),username對(duì)應(yīng)一個(gè)值,password對(duì)應(yīng)一個(gè)值,gender對(duì)應(yīng)一個(gè)值
而hobby對(duì)應(yīng)兩個(gè)值,hobby與它的值就是1:N的情況
服務(wù)端的請(qǐng)求對(duì)象提供了相應(yīng)的獲取參數(shù)值的方法。
方法1:String getParameter() :
解析:獲取的是1對(duì)1的參數(shù)值, 比如用戶名或密碼
用法: String value = request.getParameter("key")
注意: 如果沒有獲取值,那么返回的是null,而不是異常
方法2:String[] getParameterValues():
解析:獲取的是1對(duì)多的參數(shù)值, 比如 愛好
用法:String[] values = request.getParameter("key")
注意:如果沒有獲取值,那么返回的是null,而不是異常
參數(shù):瀏覽器封裝的參數(shù)是以鍵值對(duì)的形式體現(xiàn)的,比如username=zhangsan. username是參數(shù)名,zhangsan是參數(shù)值。
注意:input元素的name屬性用于指定參數(shù)名
2.4 響應(yīng)對(duì)象
一個(gè)是HttpServletResponse類型的對(duì)象
1.web服務(wù)器會(huì)對(duì)瀏覽器進(jìn)行響應(yīng),響應(yīng)的所有數(shù)據(jù)都會(huì)被封裝到HttpServletResponse類型的對(duì)象上。此類型提供了相關(guān)設(shè)置方法,可以設(shè)置狀態(tài)行、消息頭、實(shí)體內(nèi)容等。
2.作用:
設(shè)置客戶端的輸出內(nèi)容
設(shè)置響應(yīng)狀態(tài)碼
設(shè)置瀏覽器的解碼方式
設(shè)置cookies
實(shí)現(xiàn)重定向
2.5 中文亂碼問題
兩處亂碼:
第一處: 后端響應(yīng)瀏覽器時(shí): response向?yàn)g覽器返回中文時(shí)出現(xiàn)亂碼
response.getWriter().println("你好,Servlet");
第二處: post請(qǐng)求時(shí),瀏覽器向后端發(fā)送了中文時(shí)出現(xiàn)了亂碼
String username = request.getParameter("username"); username=張三 是漢字時(shí)
亂碼的情況原因很簡(jiǎn)單,就是編碼過程使用的字符集與解碼過程使用的字符集不一致造成的,不是邏輯問題。
編碼:字符串變成字節(jié)數(shù)組的形式 "中國(guó)"-->[-86,-92,-78,-12,-64,-23] 使用字符手冊(cè) UTF8 三個(gè)字節(jié)對(duì)應(yīng)一個(gè)漢字
解碼:字節(jié)數(shù)組變成字符串的形式 [-86,-92,-78,-12,-64,-23]-->"中國(guó)" 使用字符手冊(cè)u(píng)ncode 兩個(gè)字節(jié)對(duì)應(yīng)一個(gè)漢字
瀏覽器使用post請(qǐng)求方式提交中文時(shí):
1. 確保瀏覽器的html頁(yè)面是utf-8編碼集
2. 在servlet中(也就是服務(wù)端)的service方法的第一行處,使用
request.setCharacterEncoding("utf-8");即可
瀏覽器使用get請(qǐng)求方式提交中文時(shí):
1. 確保瀏覽器的html頁(yè)面是utf-8編碼集
2. 因?yàn)榉?wù)端默認(rèn)使用iso-8859-1進(jìn)行解碼。我們的解決方式就是在服務(wù)端再次使用iso-8859-1進(jìn)行編碼,然后在使用utf-8進(jìn)行解碼。
String username = request.getParameter("username") //
byte[] bs = username.getBytes("iso-8859-1") ;
//借助string的構(gòu)造器,重新解碼即可
String str = new String(bs,"utf-8");//
服務(wù)端向?yàn)g覽器發(fā)送中文亂碼情況:
只需要在服務(wù)端對(duì)中文進(jìn)行編碼前,寫入這一行代碼即可:
response.setContentType("text/html;charset=utf8");
2.6 Servlet調(diào)用DAO
第一步:引入第三方資源,比如相關(guān)依賴的jar包坐標(biāo), mysql驅(qū)動(dòng),druid數(shù)據(jù)源
第二步:在resources配置druid.properties
第三步:編寫DruidUtil工具類,并測(cè)試(里面提供main方法)
第四步:根據(jù)ORM關(guān)系模型,編寫emp表的實(shí)體類Emp
第五步:編寫相關(guān)的Dao接口以及Dao接口的實(shí)現(xiàn)類
第六步:編寫EmpServlet組件
第七步: 編寫web.xml
第八步:File–>Project structure–>Artifacts–>Available Elements–>右鍵點(diǎn)擊項(xiàng)目–>put into output root
第九步:重新部署項(xiàng)目(啟動(dòng)tomcat) 在瀏覽器上輸入地址進(jìn)行測(cè)試
2.7 總結(jié) servlet的運(yùn)行流程:
1: 瀏覽器發(fā)送地址請(qǐng)求
2: 根據(jù)ip和port號(hào)定位到tomcat容器
3: tomcat容器根據(jù)appName確定項(xiàng)目
4: 然后tomcat查找當(dāng)前項(xiàng)目下的web.xml配置信息,用url-pattern與地址信息進(jìn)行匹配
5: 根據(jù)兩個(gè)相同的name找到對(duì)應(yīng)的servlet組件
6: 執(zhí)行servlet組件里的service方法(執(zhí)行的是classes里的class文件)
7: 向?yàn)g覽器發(fā)送響應(yīng)信息。
8:瀏覽器收到響應(yīng)信息后會(huì)進(jìn)行解析和顯示。
三 Servlet對(duì)路徑的處理
3.1 重定向
用戶在瀏覽器輸入一個(gè)地址,或者點(diǎn)擊按鈕觸發(fā)一個(gè)請(qǐng)求地址,然后去往服務(wù)器端,此時(shí)服務(wù)端處理完相關(guān)計(jì)算后,再次向?yàn)g覽器發(fā)送一個(gè)302狀態(tài)和一個(gè)新的地址(比如www.baidu.com). 之后瀏覽器收到了302狀態(tài)碼和一個(gè)新的地址,然后瀏覽器就重新向這個(gè)新的地址發(fā)送請(qǐng)求。
302: 表示重定向, 當(dāng)瀏覽器收到的狀態(tài)碼是302,表示需要重新發(fā)送請(qǐng)求,請(qǐng)求地址是和302一起過來的那個(gè)新的地址。
適合的場(chǎng)景:
用戶使用瀏覽器觸發(fā)了一次請(qǐng)求后,然后服務(wù)端向?yàn)g覽器發(fā)送一個(gè)新的地址,由瀏覽器自動(dòng)再次發(fā)送一個(gè)請(qǐng)求。
比如:添加一個(gè)新員工后,然后頁(yè)面自動(dòng)顯示所有員工信息,包含兩次請(qǐng)求
第一次:insert, 用戶主動(dòng)觸發(fā)的
第二次: select, 是服務(wù)端告訴瀏覽器再次觸發(fā)
再比如: 登錄成功后,跳轉(zhuǎn)到主頁(yè)面(所有員工信息) :
第一次: 登錄驗(yàn)證(LoginServlet)
-- 調(diào)用DAO的checkLogin方法,返回true或者false
-- 如果登錄成功,此時(shí)應(yīng)該向?yàn)g覽器發(fā)送響應(yīng)數(shù)據(jù)(302,新地址appName/findAll)
第二次: 跳轉(zhuǎn)到主頁(yè)面(FindAllServlet)
-- 調(diào)用DAO的findAll方法,查詢所有的員工信息,返回ResulSet
-- 遍歷ResultSet ,然后使用流對(duì)象的println方法,拼接成一個(gè)table。
-- 響應(yīng)對(duì)象將數(shù)據(jù)打包,返回給瀏覽器,瀏覽器解析成一個(gè)table
如何重定向
response.sendRedirect(String url)
url: 是重定向的新地址, 該地址可以是任意地址, 302狀態(tài)碼是響應(yīng)對(duì)象自己設(shè)置的,不需要程序員設(shè)置
重定向的特點(diǎn):
1. 重定向的地址可以是任何地址
2. 重定向后,瀏覽器的地址發(fā)生了變化
3. 重定向所涉及到的web組件并不共享同一個(gè)request和response
4. 重定向前,不可以關(guān)閉流 ,即不能調(diào)用 pw.close()
3.2 轉(zhuǎn)發(fā)
3.2.1 什么是轉(zhuǎn)發(fā)
一個(gè)web組件(servlet/jsp)將未完成的任務(wù)轉(zhuǎn)交給另外一個(gè)web(servlet/jsp)組件。
3.2.2 如何轉(zhuǎn)發(fā)
步驟1: 綁定數(shù)據(jù)
request.getAttribute("school","shuilidianli")
步驟2: 獲取轉(zhuǎn)發(fā)器,同時(shí)指定目的地的地址
RequestDispatcher rd = request.getRequestDispatcher("/servlet03");
步驟3: 開始轉(zhuǎn)發(fā),帶上request和response對(duì)象
rd.forward(request,response);
3.2.3 轉(zhuǎn)發(fā)的特點(diǎn)
轉(zhuǎn)發(fā)是服務(wù)器的行為
瀏覽器在這個(gè)過程中只有一次行為,路徑不會(huì)發(fā)生任何的變化
轉(zhuǎn)發(fā)可以帶有數(shù)據(jù) request對(duì)象中
轉(zhuǎn)發(fā)只能在當(dāng)前項(xiàng)目中進(jìn)行轉(zhuǎn)發(fā),不能轉(zhuǎn)發(fā)外部資源
3.3 員工管理系統(tǒng)v1
3.3.0 準(zhǔn)備工作
第一步:創(chuàng)建java的項(xiàng)目(注意,是module) employee_v1
第二步:添加web項(xiàng)目支持
第三步:引入相關(guān)jar包,放入lib下
第四步:定義項(xiàng)目的源碼組織結(jié)構(gòu)
com.employee.util
com.employee.vo
com.employee.dao
com.employee.dao.impl
com.employee.web
com.employee.test
第五步:編寫DruidUtil連接池工具類
第六步:編寫emp表的實(shí)體類Employee類型
第七步:編寫emp表的Dao接口:EmployeeDao
public interface EmpDao {
void addEmp(Emp emp);
void delEmpById(int id);
void modEmp(Emp emp);
Emp findById(int id);
ArrayList
ArrayList
}
第八步:編寫Dao接口的實(shí)現(xiàn)類:EmployeeDaoImpl
3.3.1 員工列表界面的實(shí)現(xiàn)
請(qǐng)求地址: https://localhost:8088/employee_v1/findAll
1)編寫員工列表的Servlet組件: FindAllServlet
2)編寫web.xml
3)啟動(dòng)tomcat,進(jìn)行測(cè)試。
3.3.2 添加員工的功能實(shí)現(xiàn)
1)在web目錄下,編寫一個(gè)addEmp.html
2)編寫添加員工的組件AddEmpServlet
3)修改web.xml,新添加
4)重新部署tomcat,進(jìn)行測(cè)試
- 先訪問添加員工的HTMl頁(yè)面。https://localhost:8088/employee_v1/addEmp.html
- 然后添加數(shù)據(jù),點(diǎn)擊提交
3.3.3 刪除員工的功能實(shí)現(xiàn)
1)修改員工列表界面:添加刪除按鈕
2)修改web.xml,新添加一對(duì)配置信息
3)創(chuàng)建對(duì)應(yīng)的組件 DelEmpServlet
4)部署tomcat,進(jìn)行測(cè)試
3.3.4 修改員工的功能實(shí)現(xiàn)
邏輯應(yīng)該經(jīng)過一個(gè)查詢組件,進(jìn)行顯示該員工原有的信息,然后在該頁(yè)面中,進(jìn)行修改部分值,最后點(diǎn)擊提交,保存到數(shù)據(jù)庫(kù)。
第一部分:編寫跳轉(zhuǎn)到修改頁(yè)面的邏輯。
1)修改員工列表界面:添加去修改按鈕
2)編寫web.xml,新添加一對(duì)配置
2)編寫ToUpdateEmp組件:獲取瀏覽器傳輸過來的員工id,從數(shù)據(jù)庫(kù)中查詢出來,顯示到一個(gè)form表單中,在瀏覽器中顯示
3)啟動(dòng)tomcat,去測(cè)試
第二部分:編寫在修改頁(yè)面上修改信息,點(diǎn)擊提交的邏輯
4)編寫web.xml,新添加一對(duì)配置
5)編寫對(duì)應(yīng)的UpdateEmp組件
6)啟動(dòng)tomcat,去測(cè)試
3.4 處理請(qǐng)求資源路徑
3.4.1 什么是請(qǐng)求資源路徑
在瀏覽器地址欄中輸入的地址格式如下:
https://ip:port/appName/xxx.html?..... addEmp.html
http://localhost:8088/day44_servlet/findAll findAllServlet
https://ip:port/虛擬資源/具體資源
解析:
ip: 服務(wù)器所在IP地址
port: 服務(wù)器的端口號(hào)
appName: 要訪問的項(xiàng)目名稱
/appName/xxx.html?..... 就是請(qǐng)求資源路徑,也就是getRequestURI()方法的返回值。
3.4.2 處理請(qǐng)求資源路徑的過程
當(dāng)瀏覽器地址欄上輸入https://ip:port/appName/xxx.html時(shí)
瀏覽器依據(jù)ip和port,與servlet容器建立連接,然后將后面的請(qǐng)求資源路徑發(fā)送給servlet容器 servlet容器依據(jù)應(yīng)用名/appName找到應(yīng)用所在的文件夾,容器會(huì)默認(rèn)請(qǐng)求的是一個(gè)servlet。 開始查找web.xml文件中的所有的servlet配置
匹配方式以下有三種
1)精確匹配
通過將請(qǐng)求資源路徑中的具體資源名稱與web.xml文件中的url-pattern進(jìn)行對(duì)比,嚴(yán)格匹配相等后找到對(duì)應(yīng)資源并執(zhí)行。 如
此時(shí),盡管應(yīng)用中有abc.html這個(gè)具體的頁(yè)面,也會(huì)去執(zhí)行該url-pattern對(duì)應(yīng)的servlet組件,而不是返回這個(gè)具體的abc.html
2)通配符匹配
使用“*”號(hào)來匹配0個(gè)或者多個(gè)字符,如下寫法
代表輸入任何不同的url地址都將匹配成功,比如
http://ip:port/appName/abc.html
http://ip:port/appName/abc/def/ghi 這些地址都會(huì)匹配成功
3)后綴匹配
不能使用斜杠開頭,使用*. 開頭的任意多個(gè)字符,如
會(huì)匹配以.do結(jié)尾的請(qǐng)求,以下的地址都能匹配成功
http://ip:port/appName/abc.do
http://ip:port/appName/abc/def/gh/abc.do
以什么結(jié)尾,自行決定,有意義即可。比如
4)無匹配的Servlet的處理
如果上述三者匹配都沒有匹配成功時(shí),容器就會(huì)查找相應(yīng)的文件
查找到對(duì)應(yīng)的文件,則返回 找不到對(duì)應(yīng)的文件,返回404
5)三者的優(yōu)先級(jí)
精確匹配> 通配符匹配 > 后綴匹配
6)三種匹配方式的共存問題:
在web.xml文件中可以有精確匹配,也可以有通配符匹配
在web.xml文件中可以有精確匹配,也可以有后綴匹配
在web.xml文件中通配符匹配和后綴匹配可以共存的,但是通配符已經(jīng)可以攔截所有的后綴匹配了,因此后綴匹配無效。所以寫與不寫都一樣了。
3.4.3 Servlet合并
一般情況下,Servlet的主要作用是充當(dāng)控制器的角色,即接受請(qǐng)求后,分發(fā)給不同的資源,這時(shí)Servlet只需要有一個(gè)就可以完成分發(fā)的過程,所以需要將Servlet合并。
實(shí)現(xiàn)合并的步驟如下:
第一步:使用后綴匹配模式,修改web.xml文件
將配置的多個(gè)相關(guān)的Servlet節(jié)點(diǎn)刪除 保留一對(duì)servlet、servlet-mapping 修改url-pattern節(jié)點(diǎn)的內(nèi)容為: *.emp
第二步:獲取請(qǐng)求資源路徑,分析具體請(qǐng)求資源后,依據(jù)分析結(jié)果調(diào)用不同的分支處理代碼
調(diào)用request.getRequestURI()方法獲取請(qǐng)求資源路徑 分析對(duì)應(yīng)資源后分發(fā)
String uri = request.getRequestURI();
if(uri.contains("/appName/findAll.emp")){
//......
}else if(uri.contains("/appName/addEmp.emp")){
//....
}else if(...){
//....
}
3.5 員工管理系統(tǒng)v2
1)創(chuàng)建module名 employee_v2
2)添加web支持,即 add framework support
3)創(chuàng)建lib文件夾,將jar拷貝過來
4))將com包拷貝到新項(xiàng)目下、以及druid.properties拷貝過來
5))將web下的addEmp.html拷貝過來
6)修改web.xml
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">
7)將五個(gè)Servlet組件合成一個(gè)ActionServlet
小貼士:
修改所有請(qǐng)求資源路徑位置處的內(nèi)容。添加.emp
1: addEmp.html--->action="addEmp.emp"
2: 修改和刪除按鈕處的a鏈接,要添加.action
3: 三處重定向也要改
4: 修改頁(yè)面的action的值也要改
測(cè)試:
localhost:8088/employee_v2/listEmp.action
四 狀態(tài)管理
建議,建議,強(qiáng)烈建議
在講這個(gè)章節(jié)前,建議先講第五章節(jié)的第一小節(jié):5.1 servlet注解。 方便編寫代碼。畢竟前面的內(nèi)容已經(jīng)熟悉了web.xml的配置了。
4.1 狀態(tài)管理概述
4.1.1 為什么需要狀態(tài)管理
Web應(yīng)用程序使用的是HTTP協(xié)議進(jìn)行通信,而HTTP協(xié)議是“無狀態(tài)”協(xié)議,即服務(wù)器一旦響應(yīng)完客戶的請(qǐng)求后,就斷開連接,而同一個(gè)客戶的下一次請(qǐng)求將重新建立網(wǎng)絡(luò)連接。 說白點(diǎn),就是HTTP協(xié)議只能用于傳輸數(shù)據(jù),不記錄狀態(tài)(就是不記錄時(shí)誰發(fā)送的請(qǐng)求)
服務(wù)器端有時(shí)需要判斷是否為同一個(gè)客戶發(fā)送的請(qǐng)求。比如客戶的多次選購(gòu)商品。因此有必要跟蹤同一個(gè)客戶發(fā)送的多次請(qǐng)求。
不然會(huì)出現(xiàn)的情況: 如果不跟蹤,那么多個(gè)客戶添加的商品可能在同一個(gè)購(gòu)物車?yán)?,某一個(gè)客戶添加的商品可以在多個(gè)購(gòu)物車?yán)铩?/p>
4.1.2 什么是狀態(tài)管理
有的時(shí)候,需要將瀏覽器與服務(wù)器之間的多次交互(請(qǐng)求和響應(yīng))看成一個(gè)整體(同一個(gè)用戶的多次請(qǐng)求),并將多次交互時(shí)所涉及的數(shù)據(jù)(即狀態(tài))保存下來,提供給后續(xù)的交互進(jìn)行數(shù)據(jù)的管理即狀態(tài)管理。
狀態(tài)就是數(shù)據(jù) 管理指的是在這個(gè)多次交互的過程中對(duì)數(shù)據(jù)的存儲(chǔ)、修改、刪除。
生活中很多與狀態(tài)管理類似的案例。如洗車卡記錄洗車次數(shù)就是很典型的狀態(tài)管理。
洗車卡可以是一張記錄簡(jiǎn)單次數(shù)的標(biāo)示,車主每次攜帶卡片洗車后由商家修改,車主即可帶走這張記錄數(shù)據(jù)的卡片,商家不會(huì)保存任何數(shù)據(jù),客戶自己負(fù)責(zé)攜帶需要維護(hù)的數(shù)據(jù)。
還有一種處理方式就是商家只給客戶一個(gè)卡號(hào),每次客戶來洗車時(shí)自己不會(huì)記錄洗車次數(shù),只要報(bào)上卡號(hào),商家就會(huì)從系統(tǒng)中找到與卡號(hào)對(duì)應(yīng)的數(shù)據(jù),修改后仍然是商家保存,客戶帶走的只是一個(gè)卡號(hào)這個(gè)標(biāo)示。
以上兩種模式都能實(shí)現(xiàn)洗車次數(shù)的記錄,也就是數(shù)據(jù)的管理,只是各有利弊,程序中的狀態(tài)管理與這個(gè)案例都采用了同樣的處理原理。
name:用于區(qū)分不同Cookie的名字 value:Cookie的值
案例演示:
1. 編寫一個(gè)Servlet,實(shí)現(xiàn)將用戶名及區(qū)域兩個(gè)信息以Cookie的形式保存的功能
2. 程序運(yùn)行后,通過查看瀏覽器的輔助功能確認(rèn)兩個(gè)Cookie是否保存成功
在瀏覽器上查看Cookie信息: 右鍵檢查或者F12
4.2.3 如何查詢Cookie
當(dāng)客戶端向服務(wù)器發(fā)出請(qǐng)求時(shí),服務(wù)器端可以嘗試著從請(qǐng)求數(shù)據(jù)包的消息頭中獲取是否攜帶了Cookie信息。實(shí)現(xiàn)這一功能的代碼如下:
Cookie[] request.getCookies();
注意:如果沒有Cookie信息, 返回null.
由于客戶端是可以存放多個(gè)Cookie的,所以request提供的獲取Cookie的方法的返回值是Cookie數(shù)組,如果想進(jìn)一步獲取某一個(gè)Cookie信息可以通過遍歷數(shù)組,分別使用相應(yīng)的getXXX方法來獲取每一個(gè)Cookie的name和value。
獲取一個(gè)Cookie對(duì)象的名稱或值
String getName();
String getValue();
4.2.4 如何修改Cookie
所謂Cookie的修改,本質(zhì)是獲取到要變更值的Cookie,通過setValue方法將新的數(shù)據(jù)存入到cookie中,然后由response響應(yīng)對(duì)象發(fā)回到客戶端,對(duì)原有舊值覆蓋后即實(shí)現(xiàn)了修改。
主要實(shí)現(xiàn)代碼:
Cookie[] cookies = request.getCookies();
if(cookies!=null){
for(Cookie c : cookies){
String cookieName = c.getName();
if(name.equals(“uname”)){
c.setValue(“Mark”);
}
}
其中response.addCookie(c)是非常重要的語句,如果沒有這一行代碼,那么就算是使用setValue方法修改了Cookie的值,但是不發(fā)回到客戶端的話,也不會(huì)實(shí)現(xiàn)數(shù)值的改變。所以只要見到response.addCookie這行代碼,即服務(wù)器端發(fā)回了帶有Set-Cookie消息頭的信息。
4.2.5 cookie的過期時(shí)間
默認(rèn)情況下,cookie保存到瀏覽器端的內(nèi)存中,只要不關(guān)閉瀏覽器,cookie就一直在。只要瀏覽器已關(guān)閉,就消失。 如果想要在關(guān)閉瀏覽器后,還想要將cookie保存一段時(shí)間,就可以通過設(shè)置cookie過期時(shí)間來達(dá)到目的。
設(shè)置Cookie的過期時(shí)間使用如下代碼:
void setMaxAge(int seconds);
該方法是Cookie提供的實(shí)例方法。參數(shù)seconds的單位為秒,但精度不是很高。
seconds > 0 :代表Cookie保存在硬盤上的時(shí)長(zhǎng)
seconds = 0 : 代表Cookie的生命時(shí)長(zhǎng)為現(xiàn)在,而這一刻稍縱即逝,所以馬上Cookie就等同于過了生存時(shí)間,所以會(huì)被立即刪除。這也是刪除Cookie的實(shí)現(xiàn)方式。
seconds < 0 :缺省值,瀏覽器會(huì)將Cookie保存在內(nèi)存中。
以下代碼實(shí)現(xiàn)了Cookie保存在硬盤上40秒:
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URLEncoder;
import javax.servlet.*;
import javax.servlet.*;
public class AddCookieServlet extends HttpServlet {
public void service(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType(
"text/html;charset=utf-8");
PrintWriter out = response.getWriter();
//創(chuàng)建cookie
Cookie c = new Cookie("username","Lisa");
Cookie c2 = new Cookie("city","NewYork");
response.addCookie(c);
response.addCookie(c2);
out.close();
}
}
4.2.6 cookie的路徑問題
1)客戶端如何保存cookie的路徑
客戶端存儲(chǔ)Cookie之后,并不是針對(duì)同一個(gè)應(yīng)用訪問任何資源時(shí)都自動(dòng)發(fā)送Cookie到服務(wù)器端,而是會(huì)進(jìn)行路徑的判斷。只有符合路徑規(guī)范的請(qǐng)求才會(huì)發(fā)送Cookie到服務(wù)器端。
客戶端在接受Cookie時(shí)會(huì)為該Cookie記錄一個(gè)默認(rèn)路徑,這個(gè)路徑記錄的是添加這個(gè)Cookie的Web組件的路徑。
如,當(dāng)客戶端向 http://localhost:8080/test/file/addCookie.jsp發(fā)送請(qǐng)求時(shí)創(chuàng)建了cookie,那么該cookie的路徑就是 /test/file.
2)客戶端什么時(shí)候發(fā)送Cookie
只有當(dāng)訪問的地址是Cookie的路徑或者其子路徑時(shí),瀏覽器才發(fā)送Cookie到服務(wù)器端。
如,Cookie的路徑是 /test/file,那么如果訪問的是 /test/file/a.jsp 或者 /test/file/b/c.jsp時(shí),都會(huì)發(fā)送Cookie。
如果訪問的是 /test/d.jsp,則瀏覽器不會(huì)發(fā)送Cookie。
3) 如何設(shè)置Cookie的路徑
設(shè)置Cookie的路徑可以使用Cookie的API方法,setPath(String uri);
如以下代碼就實(shí)現(xiàn)了設(shè)置Cookie的路徑為應(yīng)用的頂級(jí)目錄,這樣所有資源路徑要么與此路徑相等,要么是子路徑,從而實(shí)現(xiàn)了客戶端發(fā)送任何請(qǐng)求時(shí)都會(huì)發(fā)送Cookie。
Cookie c = new Cookie(“uname”,“jack”);
c.setPath(“/appName”);
response.addCookie(c);
cookie.setPath("/appName/account")
appName/acount/add
appName/acount/del
appName/acount/update
appName/manager/add
appName/manager/del
appName/manager/update
appName/program/add
appName/program/del
appName/program/update
1.4.5 cookie的限制
--cookie可以被用戶禁止
--cookie會(huì)將狀態(tài)數(shù)據(jù)保存到瀏覽器端,不安全,
--cookie只能保存少量數(shù)據(jù),大約在4kb左右
--cookie的個(gè)數(shù)有限制
--cookie只能保存字符串
4.3 Session狀態(tài)管理
4.3.1 什么是session
見名知意,是會(huì)話的意思。當(dāng)瀏覽器訪問服務(wù)器時(shí),服務(wù)器會(huì)為每一個(gè)瀏覽器在服務(wù)器端的內(nèi)存中分配空間,單獨(dú)維護(hù)一個(gè)Session對(duì)象,每個(gè)session對(duì)象都有一個(gè)唯一標(biāo)識(shí)符,叫sessionId。服務(wù)器會(huì)將sessionId以cookie的方式發(fā)給瀏覽器,瀏覽器會(huì)保存sessionid.
當(dāng)瀏覽器再次向服務(wù)器發(fā)送請(qǐng)求時(shí),會(huì)帶上sessionId。服務(wù)端收到sessionId后,會(huì)依據(jù)sessionId查找是否有對(duì)應(yīng)的session對(duì)象。
flag = true:先從請(qǐng)求中找找看是否有SID,沒有會(huì)創(chuàng)建新Session對(duì)象,有SID會(huì)查找與編號(hào)對(duì)應(yīng)的對(duì)象,找到匹配的對(duì)象則返回,找不到SID對(duì)應(yīng)的對(duì)象時(shí)則會(huì)創(chuàng)建新Session對(duì)象。 總結(jié):填寫true就一定會(huì)得到一個(gè)Session對(duì)象。要么返回找到的,要么返回新創(chuàng)建的 flag = false:不存在SID以及按照SID找不到Session對(duì)象時(shí)都會(huì)返回null,只有根據(jù)SID找到對(duì)應(yīng)的對(duì)象時(shí)會(huì)返回具體的Session對(duì)象。 總結(jié):填寫false時(shí),不會(huì)創(chuàng)建新的Session. 要么返回找到的,要么返回null。
如何想要設(shè)置flag為true, 可以使用以下重載方法, 相當(dāng)于設(shè)置了flag為true. 提供該方法主要是為了書寫代碼時(shí)更方便,大多數(shù)情況下還是希望能夠返回一個(gè)Session對(duì)象的。
HttpSession s = request.getSession();
4.3.3 Session的常用API
Session對(duì)象,也是采用name-value對(duì)的形式來區(qū)分每一組數(shù)據(jù)。
1)綁定數(shù)據(jù)的代碼:
void session.setAttribute(String name,Object obj)
Session對(duì)象可以保存更復(fù)雜的對(duì)象類型數(shù)據(jù)了,不像Cookie只能保存字符串。
2)獲取綁定數(shù)據(jù)的代碼:
Object obj = session.getAttribute(String name)
3)移除綁定數(shù)據(jù)的代碼
void session.removeAttribute(String name)
4)刪除session對(duì)象
session.invalidate(): 立即失效
該方法會(huì)使得服務(wù)器端與該客戶端對(duì)應(yīng)的Session對(duì)象不再被Session容器管理,進(jìn)入到垃圾回收的狀態(tài)。對(duì)于這種立即刪除Session對(duì)象的操作主要應(yīng)用于不再需要身份識(shí)別的情況下,如登出操作。
4.3.4 Session的有效時(shí)間(超時(shí)時(shí)間)
Session會(huì)以對(duì)象的形式占用服務(wù)器端的內(nèi)存,過多的以及長(zhǎng)期的消耗內(nèi)存會(huì)降低服務(wù)器端的運(yùn)行效率,所以Session對(duì)象存在于內(nèi)存中時(shí)會(huì)有默認(rèn)的時(shí)間限制,一旦Session對(duì)象存在的時(shí)間超過了這個(gè)缺省的時(shí)間限制則認(rèn)為是Session超時(shí),Session會(huì)失效,不能再繼續(xù)訪問。
Web服務(wù)器缺省的超時(shí)時(shí)間設(shè)置一般是30分鐘。
如果用戶想要自定義session的時(shí)間,有如下兩種方式,
1)第一種:聲明式 (全局)
在web.xml中添加如下配置
使用聲明式來修改缺省時(shí)間,那么該應(yīng)用創(chuàng)建的所有Session對(duì)象的生命周期都會(huì)應(yīng)用這個(gè)規(guī)定的時(shí)間,單位為分鐘。
2)第二種:編程式 (局部)
在代碼中使用session的api進(jìn)行設(shè)置
session.setMaxInactiveInterval(int seconds) //單位是秒
4.3.5 session的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
安全(狀態(tài)數(shù)據(jù)保存到服務(wù)器端)
session綁定的數(shù)據(jù)類型更加豐富,cookie只能是字符串
session保存的數(shù)據(jù)量更大,cookie只能是4kb
缺點(diǎn):
session占服務(wù)器的內(nèi)存。
案例演示:登錄驗(yàn)證
需求:如果訪問的頁(yè)面沒有經(jīng)過登錄,那么需要立即跳轉(zhuǎn)到登錄頁(yè)面
4.4 員工管理系統(tǒng) v3
步驟1: 整合pom.xml
步驟2: 將v3里的源碼src 全部拷貝到v4的src里
步驟3: 將v3里的web文件夾里的內(nèi)容全部拷貝到v4的web文件夾里面
步驟4: 測(cè)試是否好使。 添加tomcat, 修改虛擬項(xiàng)目名,配置項(xiàng)目構(gòu)建(project structure), 訪問如下地址
http://localhost:8088/employee_v4/listEmp.action
4.4.1 添加注冊(cè)功能
1)維護(hù)mysql的user表
use mydb;
create table if not exists user(
id int primary key auto_increment,
username varchar(10) not null,
realname varchar(10) not null,
password varchar(20) not null,
gender varchar(1) not null
) engine=InnoDB auto_increment=1 default charset=utf8;
delete from user;
insert into user values(null,'zhangsan','張大三','zs123456','m');
commit;
select * from user;
2)為user表創(chuàng)建實(shí)體類User, 定義UserDao接口,實(shí)現(xiàn)UserDaoImpl實(shí)現(xiàn)類
五 Servlet的特性
5.1 Servlet注解
5.1.1 注解簡(jiǎn)介
Servlet注解是?==Java Servlet 3.0版本==引入的一種特性,允許在Servlet類上使用注解來配置Servlet,而不需要傳統(tǒng)的web.xml配置文件。通過使用@WebServlet注解,可以指定Servlet的名稱、URL模式以及其他相關(guān)配置。
?Servlet注解的優(yōu)點(diǎn)?
?簡(jiǎn)化配置?:通過注解,可以避免在web.xml文件中進(jìn)行繁瑣的配置,減少代碼量,提高開發(fā)效率。 ?動(dòng)態(tài)配置?:注解允許在運(yùn)行時(shí)動(dòng)態(tài)地添加、修改或刪除Servlet的配置,提高了系統(tǒng)的靈活性。
Servlet注解的缺點(diǎn)
?兼容性問題?:雖然Servlet注解提供了很多便利,但在一些較舊的Java EE環(huán)境中可能不支持,需要額外的兼容性處理。 ?復(fù)雜性增加?:對(duì)于復(fù)雜的配置需求,可能需要結(jié)合web.xml文件使用,增加了配置的復(fù)雜性。
5.1.2 注解應(yīng)用
注解要寫在每個(gè)Servlet組件的類上面 格式:@WebServlet(…) 常用屬性:value和urlPatterns一般是必須的,但是二者不能共存,若同時(shí)指定,一般自動(dòng)忽略value。
屬性名類名屬性描述nameString指定servlet的name屬性,等價(jià)于
案例演示1:
@WebServlet(name = "MyServlet", //Servlet名
urlPatterns = "/serv02", //Servlet訪問的url
loadOnStartup = 1, //Servlet創(chuàng)建的時(shí)機(jī)
initParams = { //ServletConfig的相關(guān)配置
@WebInitParam(name="name", value="YWANG"),
@WebInitParam(name="pwd", value="123456")
}
)
案例演示2:
@WebServlet("/web01")
5.2 ServletContext的應(yīng)用
5.2.1 ServletContext的簡(jiǎn)介
ServletContext:Servlet上下文對(duì)象,它是一個(gè)接口。一個(gè)web工程,只有一個(gè)ServletContext對(duì)象實(shí)例(單例),同一個(gè)web應(yīng)用程序中,所有的Servlet組件共享同一個(gè)ServletContext對(duì)象。web服務(wù)器啟動(dòng)時(shí)創(chuàng)建,web服務(wù)器關(guān)閉時(shí)銷毀,也就是說,只要容器不關(guān)閉或者應(yīng)用不卸載,servlet上下文對(duì)象就一直存在。
ServletContext一般有以下四個(gè)用途:
1.獲取web.xml中配置的上下文參數(shù)context-param
2.獲取當(dāng)前的工程路徑(項(xiàng)目名稱),格式:/工程路徑
3.獲取工程部署后在服務(wù)器硬盤上的絕對(duì)路徑
4.像Map一樣存取數(shù)據(jù)
獲取ServletContext對(duì)象的方式
1. GenericServlet提供了getServletContext()方法。(推薦)
2. HttpServletRequest提供了getServletContext()方法。(推薦)
3. ServletConfig提供了getServletContext()方法。
4. HttpSession提供了getServletContext()方法。
5.2.1 案例演示:
全局參數(shù),在web.xml中配置
可以有多個(gè)
package com.servlet.context;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* ServletContext對(duì)象:
* 1. 在整個(gè)項(xiàng)目中是唯一的,只有一個(gè)。(單例)
* 2. 所有的Servlet組件(包括JSP)都可以拿到這個(gè)對(duì)象
* 3. 既然是唯一的,我們就可以使用該對(duì)象來存儲(chǔ)一個(gè)共享數(shù)據(jù)。 此時(shí):ServletContext對(duì)象也可以稱之為域?qū)ο?/p>
* 域:就是用來存儲(chǔ)數(shù)據(jù)的。
* 4. 也可以使用上下文對(duì)象,獲取全局變量context-param,以及項(xiàng)目名,以及項(xiàng)目在服務(wù)器硬盤上的絕對(duì)路徑
*/
@WebServlet(value = "/testServletContext")
public class ServletContextDemo extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException {
// 獲取ServletContext對(duì)象
ServletContext sct1 = this.getServletContext();//從GenericServlet中繼承過來的
ServletContext sct2 = request.getServletContext(); //從請(qǐng)求對(duì)象上獲取
ServletContext sct3 = this.getServletConfig().getServletContext(); //從ServletConfig上獲取
ServletContext sct4 = request.getSession().getServletContext();
//判斷四個(gè)變量指向的是不是同一個(gè)對(duì)象
System.out.println(sct1 == sct2);
System.out.println(sct1 == sct3);
System.out.println(sct1 == sct4);
//獲取全局參數(shù): 全局參數(shù)在web.xml中配置
String deptname = sct3.getInitParameter("a");
System.out.println("deptname: " + deptname);
//獲取當(dāng)前項(xiàng)目的名稱
String contextPath = sct1.getContextPath();
String contextPath1 = request.getContextPath();//從請(qǐng)求對(duì)象身上也可以獲取到
System.out.println(contextPath+" "+contextPath1);
//獲取當(dāng)前項(xiàng)目在服務(wù)器端的硬盤上的絕對(duì)路徑
String realPath = sct3.getRealPath(""); // 傳入一個(gè)字符串 ,可以是空字符串,可以是斜杠
System.out.println(realPath);
}
}
5.3 過濾器接口
5.3.1 什么是過濾器
Servlet規(guī)范中的三大接口:==Servlet接口,F(xiàn)ilter接口、Listener接口。==
過濾器接口,是Servlet2.3版本以來,定義的一種小型的,可插拔的Web組件,可以用來攔截和處理Servlet容器的請(qǐng)求和響應(yīng)過程。以便查看,提取或以某種方式操作正在客戶端與服務(wù)器端之間交換的數(shù)據(jù)。
應(yīng)用場(chǎng)景如下:
1. 對(duì)請(qǐng)求的數(shù)據(jù)是否包含敏感詞進(jìn)行提前篩選過濾,沒有必要在下一層(業(yè)務(wù)層)進(jìn)行處理。
2. 用戶沒有進(jìn)行登錄,應(yīng)該跳往登錄頁(yè)面等操作
插拔的理解:
方便增加或減少某個(gè)功能模塊,需要添加過濾就多部署一個(gè)class修改一下web.xml文件,需要減少某個(gè)功能只要?jiǎng)h除web.xml中對(duì)應(yīng)的聲明即可。
方便修改處理邏輯。當(dāng)把一些功能獨(dú)立到某個(gè)模塊時(shí),如果邏輯變了,只修改這一個(gè)文件并更新就可以實(shí)現(xiàn)功能的改變,而不用修改每一個(gè)使用這個(gè)插件的組件。
5.3.2 如何編寫過濾器
編寫過濾器遵循下列步驟:
編寫一個(gè)實(shí)現(xiàn)了Filter接口的類 實(shí)現(xiàn)Filter接口的三個(gè)方法,過濾邏輯在doFilter方法中實(shí)現(xiàn) 在Web程序中注冊(cè)過濾器 把過濾器和Web應(yīng)用一起打包部署
5.3.3 過濾器的執(zhí)行流程
單個(gè)過濾器的執(zhí)行流程:
1. 客戶端發(fā)來請(qǐng)求后,不會(huì)直接將請(qǐng)求送達(dá)Servlet,而是先走過濾器1的doFilter方法中的code1。
2. 當(dāng)遇到chain.doFilter()方法時(shí),就會(huì)調(diào)用Servlet組件的service()方法,執(zhí)行里面的邏輯代碼。
3. service()方法執(zhí)行結(jié)束后并不會(huì)立即將響應(yīng)返回給客戶端,而是回到過濾器1的doFilter()方法中code2部分,如果該部分有代碼就會(huì)執(zhí)行,執(zhí)行結(jié)束后才會(huì)將response對(duì)象返回給客戶端。
從流程中可以看到,過濾器不僅僅對(duì)Servlet的執(zhí)行前起到過濾作用,對(duì)于執(zhí)行后同樣有過濾效果。所以,過濾器是對(duì)request和response的檢查
多個(gè)過濾器的執(zhí)行流程:
過濾器1的doFilter的code1 ,然后 過濾器2的doFilter的code1 ,然后再試 service()方法 ,再然后是過濾器2的doFilter的code2 ,之后是 過濾器1的doFilter的code2 ,最后返回給客戶端
在這個(gè)動(dòng)作的傳遞過程中一定要寫 chain.doFilter()
多個(gè)過濾器的優(yōu)先級(jí)
在一個(gè)Web應(yīng)用中,可以有多個(gè)過濾器,它們的優(yōu)先級(jí)由位于web.xml文件中的聲明順序決定,具體是按照
5.3.4 過濾器的初始化參數(shù)
容器啟動(dòng)之后,會(huì)創(chuàng)建過濾器實(shí)例。通過init方法來完成過濾器的初始化。初始化時(shí)可以添加一些配置,提升動(dòng)態(tài)性。而這些參數(shù)通過在web.xml文件中的
讀取這些name-value對(duì)需要使用FilterConfig對(duì)象,從web.xml文件到FilterConfig對(duì)象的過程由容器完成,并通過參數(shù)傳入到init方法之中,只需要設(shè)定一些成員對(duì)象保存數(shù)值就可以在doFilter方法中使用。
初始化參數(shù)配置方法如下:
讀取初始化參數(shù)使用如下代碼:
public class CommentFilter implements Filter{
FilterConfig config = null;
public void init(FilterConfig arg0) throws ServletException {
config = arg0;
}
public void doFilter(ServletRequest arg0,ServletResponse arg1, FilterChain arg2) throws IOException, ServletException {
String illegalStr = config.getInitParameter("illegalStr");
// … …
}
public void destroy() {
// … …
}
}
5.3.5 案例演示:敏感詞過濾
CommentFilter1類
package com.shuilidianli.filter;
import javax.servlet.*;
import java.io.IOException;
public class CommentFilter1 implements Filter {
//添加全局變量FilterConfig
FilterConfig config = null;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//給全局變量config賦值
config = filterConfig;
}
@Override
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf8");
//使用全局變量config獲取一些敏感詞
String word1 = config.getInitParameter("word1");
System.out.println("word1:"+word1);
//獲取評(píng)論內(nèi)容
String comment = request.getParameter("comment");
if(comment.contains(word1)){
//轉(zhuǎn)發(fā)到comment.jsp頁(yè)面進(jìn)行提醒
request.setAttribute("message","您輸入了敏感詞"+word1);
request.getRequestDispatcher("comment.jsp").forward(request,response);
}else{
//繼續(xù)向下執(zhí)行
chain.doFilter(request,response);
}
}
@Override
public void destroy() {
}
}
CommentFilter2類
package com.shuilidianli.filter;
import javax.servlet.*;
import java.io.IOException;
public class CommentFilter2 implements Filter {
//添加全局變量FilterConfig
FilterConfig config = null;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//給全局變量config賦值
config = filterConfig;
}
@Override
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf8");
//使用全局變量config獲取一些敏感詞
String word2 = config.getInitParameter("word2");
//獲取評(píng)論內(nèi)容
String comment = request.getParameter("comment");
if(comment.contains(word2)){
//轉(zhuǎn)發(fā)到comment.jsp頁(yè)面進(jìn)行提醒
request.setAttribute("message","您輸入了敏感詞"+word2);
request.getRequestDispatcher("comment.jsp").forward(request,response);
}else{
//繼續(xù)向下執(zhí)行
chain.doFilter(request,response);
}
}
@Override
public void destroy() {
}
}
CommentServlet類
package com.shuilidianli.web;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/toComment")
public class CommentServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
//處理中文亂碼問題
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf8");
String comment = request.getParameter("comment");
PrintWriter pw = response.getWriter();
pw.println("評(píng)論通過,您的評(píng)論內(nèi)容如下:
");
pw.println("
"+comment+"
");pw.close();
}
}
web.xml
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">
employee_v3:
增刪改查:
登錄注冊(cè):
驗(yàn)證碼在登錄或者注冊(cè)頁(yè)面其中一個(gè)即可
過濾器:驗(yàn)證是否登錄過,如果沒有登錄過,重定向到登錄頁(yè)面
5.4 監(jiān)聽器
5.4.1 監(jiān)聽器的概念
Servlet規(guī)范中的三大接口,除了Servlet接口,F(xiàn)ilter接口,剩下的就是==Listener接口==了。
監(jiān)聽器采用了設(shè)計(jì)模式中的觀察者模式,監(jiān)聽器本身是觀察者,被監(jiān)聽的對(duì)象是被觀察者。當(dāng)被監(jiān)聽對(duì)象的狀態(tài)發(fā)生改變時(shí),就會(huì)通知觀察者,也就是監(jiān)聽器,監(jiān)聽器在收到通知后可以做出相應(yīng)的處理邏輯。
Servlet監(jiān)聽器是Servlet規(guī)范中定義的一種特殊類,用于監(jiān)聽ServletContext、HttpSession和ServletRequest等域?qū)ο蟮膭?chuàng)建與銷毀事件,以及監(jiān)聽這些域?qū)ο笾袑傩园l(fā)生修改的事件。
5.4.2 監(jiān)聽器的分類
在Servlet2.5版本以來,定義了以下三類8種監(jiān)聽器:
第一大類:監(jiān)聽 Session、request、context 這三個(gè)==對(duì)象的創(chuàng)建與銷毀==的:
HttpSessionListener ServletContextListener ServletRequestListener
第二大類:監(jiān)聽==對(duì)象屬性==變化的:
HttpSessionAttributeLister ServletContextAttributeListener ServletRequestAttributeListener
第三類:監(jiān)聽Session 內(nèi)的對(duì)象的:
HttpSessionBindingListener HttpSessionActivationListener。 與上面六類不同,這兩類 Listener 監(jiān)聽的是Session 內(nèi)的對(duì)象,而非 Session 本身,不需要在 web.xml中配置。
5.4.3 如何編寫監(jiān)聽器
步驟1:編寫一個(gè)java類,依據(jù)監(jiān)聽的事件類型選擇實(shí)現(xiàn)相應(yīng)的監(jiān)聽器接口,如,要監(jiān)聽Sessioin對(duì)象的創(chuàng)建和銷毀,要實(shí)現(xiàn)HttpSessionListener
步驟2:在監(jiān)聽器接口方法中,實(shí)現(xiàn)相應(yīng)的監(jiān)聽處理邏輯
步驟3:在web.xml文件中注冊(cè)該監(jiān)聽器
public class CouListener implements HttpSessionListener{
public void sessionCreate(HttpSessionEvent arg0){
//..
HttpSession session = arg0.getSession();
//獲取sesssion或者上下文對(duì)象的
ServletContext ctx = session.getServletContext();
//..
}
}
在web.xml文件中,增加以下節(jié)點(diǎn)
5.4.4 應(yīng)用場(chǎng)景
由于ServletRequest,HttpSession,ServletContext對(duì)象都是容器創(chuàng)建的,通過對(duì)這些對(duì)象注冊(cè)監(jiān)聽器,就可以得知何時(shí)創(chuàng)建了。比如
1)在contextDestroyed方法中對(duì)應(yīng)用級(jí)別的資源進(jìn)行釋放
2)統(tǒng)計(jì)在線人數(shù)可以通過HttpSessionListener監(jiān)聽器的sessionCreated方法監(jiān)聽session的創(chuàng)建動(dòng)作。
案例演示:統(tǒng)計(jì)在線人數(shù)
統(tǒng)計(jì)在線人數(shù)(基于Session的統(tǒng)計(jì))
監(jiān)聽Session的創(chuàng)建及銷毀事件,修改在線人數(shù),并將這個(gè)數(shù)據(jù)輸出顯示。
(一)單態(tài)登錄:一個(gè)賬號(hào)只能在一臺(tái)機(jī)器上登錄。
package servlet.listener.singleton;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
*
* LoginSessionListener.java
*
* @title 實(shí)現(xiàn)單態(tài)登錄的監(jiān)聽器
* @description
* @author SAM-SHO
* @Date 2014-12-10
*/
public class LoginSessionListener implements HttpSessionAttributeListener {
Log log = LogFactory.getLog(this.getClass());
Map
public void attributeAdded(HttpSessionBindingEvent event) {
String name = event.getName();
// 登錄
if (name.equals("personInfo")) {
PersonInfo personInfo = (PersonInfo) event.getValue();
if (map.get(personInfo.getAccount()) != null) {
// map 中有記錄,表明該帳號(hào)在其他機(jī)器上登錄過,將以前的登錄失效
HttpSession session = map.get(personInfo.getAccount());
PersonInfo oldPersonInfo = (PersonInfo) session.getAttribute("personInfo");//map已經(jīng)存在的舊的信息
log.info("帳號(hào)" + oldPersonInfo.getAccount() + "在" + oldPersonInfo.getIp() + "已經(jīng)登錄,該登錄將被迫下線。");
session.removeAttribute("personInfo");
session.setAttribute("msg", "您的帳號(hào)已經(jīng)在其他機(jī)器上登錄,您被迫下線。");
}
// 將session以用戶名為索引,放入map中
map.put(personInfo.getAccount(), event.getSession());
log.info("帳號(hào)" + personInfo.getAccount() + "在" + personInfo.getIp() + "登錄。");
}
}
public void attributeRemoved(HttpSessionBindingEvent event) {
String name = event.getName();
// 注銷
if (name.equals("personInfo")) {
// 將該session從map中移除
PersonInfo personInfo = (PersonInfo) event.getValue();
map.remove(personInfo.getAccount());
log.info("帳號(hào)" + personInfo.getAccount() + "注銷。");
}
}
public void attributeReplaced(HttpSessionBindingEvent event) {
String name = event.getName();
// 沒有注銷的情況下,用另一個(gè)帳號(hào)登錄
if (name.equals("personInfo")) {
// 移除舊的的登錄信息
PersonInfo oldPersonInfo = (PersonInfo) event.getValue();
map.remove(oldPersonInfo.getAccount());
// 新的登錄信息
PersonInfo personInfo = (PersonInfo) event.getSession().getAttribute("personInfo");
// 也要檢查新登錄的帳號(hào)是否在別的機(jī)器上登錄過
if (map.get(personInfo.getAccount()) != null) {
// map 中有記錄,表明該帳號(hào)在其他機(jī)器上登錄過,將以前的登錄失效
HttpSession session = map.get(personInfo.getAccount());
session.removeAttribute("personInfo");
session.setAttribute("msg", "您的帳號(hào)已經(jīng)在其他機(jī)器上登錄,您被迫下線。");
}
map.put("personInfo", event.getSession());
}
}
}
package servlet.listener.singleton;
import java.io.Serializable;
import java.util.Date;
/**
*
* PersonInfo.java
*
* @title
* @description
* @author SAM-SHO
* @Date 2014-12-10
*/
public class PersonInfo implements Serializable {
private static final long serialVersionUID = 4063725584941336123L;
// 帳號(hào)
private String account;
// 登錄IP地址
private String ip;
// 登錄時(shí)間
private Date loginDate;
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public Date getLoginDate() {
return loginDate;
}
public void setLoginDate(Date loginDate) {
this.loginDate = loginDate;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((account == null) ? 0 : account.hashCode());
result = prime * result + ((ip == null) ? 0 : ip.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
PersonInfo other = (PersonInfo) obj;
if (account == null) {
if (other.account != null)
return false;
} else if (!account.equals(other.account))
return false;
if (ip == null) {
if (other.ip != null)
return false;
} else if (!ip.equals(other.ip))
return false;
return true;
}
}
(二)顯示在線人數(shù):會(huì)需要3個(gè)監(jiān)聽器
1、ContextListener:獲取服務(wù)啟動(dòng)的時(shí)間等。
2、RequestListener:獲取客戶端的IP、訪問地址,訪問次數(shù)等。
3、SessionListener:需要監(jiān)聽 Session 的創(chuàng)建與屬性變化。
4、代碼如下:
package com.helloweenvsfei.listener;
import java.util.Date;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import com.helloweenvsfei.util.ApplicationConstants;
public class MyContextListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
// 啟動(dòng)時(shí),記錄服務(wù)器啟動(dòng)時(shí)間
ApplicationConstants.START_DATE = new Date();
}
public void contextDestroyed(ServletContextEvent event) {
// 關(guān)閉時(shí),將結(jié)果清除。也可以將結(jié)果保存到硬盤上。
ApplicationConstants.START_DATE = null;
ApplicationConstants.MAX_ONLINE_COUNT_DATE = null;
}
}
package com.helloweenvsfei.listener;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
public class MyRequestListener implements ServletRequestListener {
public void requestDestroyed(ServletRequestEvent event) {
}
public void requestInitialized(ServletRequestEvent event) {
HttpServletRequest request = (HttpServletRequest) event
.getServletRequest();
HttpSession session = request.getSession(true);
// 記錄IP地址
session.setAttribute("ip", request.getRemoteAddr());
// 記錄訪問次數(shù),只記錄訪問 .html, .do, .jsp, .action 的累計(jì)次數(shù)
String uri = request.getRequestURI();
String[] suffix = { ".html", ".do", ".jsp", ".action" };
for (int i=0; i if (uri.endsWith(suffix[i])) { break; } if(i == suffix.length-1) return; } Integer activeTimes = (Integer) session.getAttribute("activeTimes"); if (activeTimes == null) { activeTimes = 0; } session.setAttribute("activeTimes", activeTimes + 1); } } package com.helloweenvsfei.listener; import java.util.Date; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionAttributeListener; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; import com.helloweenvsfei.util.ApplicationConstants; public class MySessionListener implements HttpSessionListener, HttpSessionAttributeListener { public void sessionCreated(HttpSessionEvent sessionEvent) { HttpSession session = sessionEvent.getSession(); // 將 session 放入 map ApplicationConstants.SESSION_MAP.put(session.getId(), session); // 總訪問人數(shù)++ ApplicationConstants.TOTAL_HISTORY_COUNT++; // 如果當(dāng)前在線人數(shù)超過歷史記錄,則更新最大在線人數(shù),并記錄時(shí)間 if (ApplicationConstants.SESSION_MAP.size() > ApplicationConstants.MAX_ONLINE_COUNT) { ApplicationConstants.MAX_ONLINE_COUNT = ApplicationConstants.SESSION_MAP .size(); ApplicationConstants.MAX_ONLINE_COUNT_DATE = new Date(); } } public void sessionDestroyed(HttpSessionEvent sessionEvent) { HttpSession session = sessionEvent.getSession(); // 將session從map中移除 ApplicationConstants.SESSION_MAP.remove(session.getId()); } public void attributeAdded(HttpSessionBindingEvent event) { if (event.getName().equals("personInfo")) { // 當(dāng)前登錄用戶數(shù)++ ApplicationConstants.CURRENT_LOGIN_COUNT++; HttpSession session = event.getSession(); // 查找該帳號(hào)有沒有在其他機(jī)器上登錄 for (HttpSession sess : ApplicationConstants.SESSION_MAP.values()) { // 如果該帳號(hào)已經(jīng)在其他機(jī)器上登錄,則以前的登錄失效 if (event.getValue().equals(sess.getAttribute("personInfo")) && session.getId() != sess.getId()) { sess.invalidate(); } } } } public void attributeRemoved(HttpSessionBindingEvent event) { // 注銷 當(dāng)前登錄用戶數(shù)-- if (event.getName().equals("personInfo")) { ApplicationConstants.CURRENT_LOGIN_COUNT--; } } public void attributeReplaced(HttpSessionBindingEvent event) { // 重新登錄 if (event.getName().equals("personInfo")) { HttpSession session = event.getSession(); for (HttpSession sess : ApplicationConstants.SESSION_MAP.values()) { // 如果新帳號(hào)在其他機(jī)器上登錄過,則以前登錄失效 if (event.getValue().equals(sess.getAttribute("personInfo")) && session.getId() != sess.getId()) { sess.invalidate(); } } } } } 然后在web.xml中配置這三個(gè)監(jiān)聽器即可 默認(rèn)鈍化的位置: %CATALINA_BASE%\work\Catalina\localhost\項(xiàng)目名\下 柚子快報(bào)激活碼778899分享:Servlet
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。