監(jiān)聽(tīng)器
本文包括:
1、Listener簡(jiǎn)介
2吊履、Servlet監(jiān)聽(tīng)器
3寨典、監(jiān)聽(tīng)三個(gè)域?qū)ο髣?chuàng)建和銷毀的事件監(jiān)聽(tīng)器
4、監(jiān)聽(tīng)三個(gè)域?qū)ο蟮膶傩?Attribute)的變化的事件監(jiān)聽(tīng)器
5卦尊、監(jiān)聽(tīng)綁定到 HttpSession 域中的某個(gè)對(duì)象的狀態(tài)的事件監(jiān)聽(tīng)器
1、Listener簡(jiǎn)介
- Listener(監(jiān)聽(tīng)器)就是一個(gè)實(shí)現(xiàn)特定接口的普通java程序舌厨,這個(gè)程序?qū)iT用于監(jiān)聽(tīng)另一個(gè)java對(duì)象的方法調(diào)用或?qū)傩愿淖兤袢矗?dāng)被監(jiān)聽(tīng)對(duì)象發(fā)生上述事件后,監(jiān)聽(tīng)器某個(gè)方法將立即被執(zhí)行邓线。
-
為了加深理解淌友,自定義監(jiān)聽(tīng)器來(lái)練練手,假設(shè)現(xiàn)在有個(gè)體重100的人要吃飯了骇陈,要監(jiān)聽(tīng)他吃飯的動(dòng)作震庭,捕捉到了之后再打印它的體重,具體思路如下你雌;
-
事件源類:
public class Person { private String name; private int weight;// 體重 public String getName() { return name; } public void setName(String name) { this.name = name; } public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight; } }
-
監(jiān)聽(tīng)器接口:
public interface PersonListener { public void personeating(PersonEvent event);// 監(jiān)聽(tīng)方法器联,需要一個(gè)事件對(duì)象作為參數(shù) }
-
事件類:
public class PersonEvent { private Object source;// 事件源 public Object getSource() { return source; } public void setSource(Object source) { this.source = source; } // 提供一個(gè)這樣的構(gòu)造方法:構(gòu)造事件對(duì)象時(shí),接收事件源(Person) public PersonEvent(Person person) { this.source = person; } }
-
在事件源中注冊(cè)監(jiān)聽(tīng)器
public class Person { private String name; private int weight;// 體重 private PersonListener listener; // 注冊(cè)監(jiān)聽(tīng)器 public void addPersonListener(PersonListener listener) { this.listener = listener; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight; } }
-
操作事件源----- 在事件源方法中婿崭,構(gòu)造事件對(duì)象拨拓,參數(shù)為當(dāng)前事件源(this),傳遞事件對(duì)象給監(jiān)聽(tīng)器的監(jiān)聽(tīng)方法:
public class Person { private String name; private int weight;// 體重 private PersonListener listener; // 注冊(cè)監(jiān)聽(tīng)器 public void addPersonListener(PersonListener listener) { this.listener = listener; } // 吃飯 public void eat() { // 體重增加 weight += 5; // 調(diào)用監(jiān)聽(tīng)器監(jiān)聽(tīng)方法 if (listener != null) { // 監(jiān)聽(tīng)器存在 // 創(chuàng)建事件對(duì)象 --- 通過(guò)事件對(duì)象可以獲得事件源 PersonEvent event = new PersonEvent(this); listener.personeating(event); } } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight; } }
-
測(cè)試
public class PersonTest { public static void main(String[] args) { // 步驟一 創(chuàng)建事件源 Person person = new Person(); person.setName("小明"); person.setWeight(100); // 步驟二 給事件源注冊(cè)監(jiān)聽(tīng)器(該監(jiān)聽(tīng)器由匿名內(nèi)部類創(chuàng)建) person.addPersonListener(new PersonListener() { @Override public void personeating(PersonEvent event) { System.out.println("監(jiān)聽(tīng)到了氓栈,人正在吃飯渣磷!"); // 在監(jiān)聽(tīng)方法中可以獲得事件源對(duì)象,進(jìn)而可以操作事件源對(duì)象 Person person = (Person) event.getSource(); System.out.println(person.getName()); System.out.println(person.getWeight()); } }); // 步驟三 操作事件源 person.eat();// 結(jié)果監(jiān)聽(tīng)方法被調(diào)用 } }
2授瘦、Servlet監(jiān)聽(tīng)器
-
在Servlet規(guī)范中定義了多種類型的監(jiān)聽(tīng)器醋界,它們用于監(jiān)聽(tīng)的事件源是三個(gè)域?qū)ο缶顾危謩e為:
- ServletContext
- HttpSession
- ServletRequest
-
Servlet規(guī)范針對(duì)這三個(gè)域?qū)ο笊系牟僮鳎职堰@多種類型的監(jiān)聽(tīng)器劃分為三種類型:
- 監(jiān)聽(tīng)三個(gè)域?qū)ο蟮膭?chuàng)建和銷毀的事件監(jiān)聽(tīng)器
- 監(jiān)聽(tīng)三個(gè)域?qū)ο蟮膶傩?Attribute)的增加和刪除的事件監(jiān)聽(tīng)器
- 監(jiān)聽(tīng)綁定到 HttpSession 域中的某個(gè)對(duì)象的狀態(tài)的事件監(jiān)聽(tīng)器形纺。(查看API文檔)
-
編寫 Servlet 監(jiān)聽(tīng)器:
- 和編寫其它事件監(jiān)聽(tīng)器一樣丘侠,編寫Servlet監(jiān)聽(tīng)器也需要實(shí)現(xiàn)一個(gè)特定的接口,并針對(duì)相應(yīng)動(dòng)作覆蓋接口中的相應(yīng)方法逐样。
- 和其它事件監(jiān)聽(tīng)器略有不同的是蜗字,Servlet監(jiān)聽(tīng)器的注冊(cè)不是直接注冊(cè)在事件源上,而是由WEB容器負(fù)責(zé)注冊(cè)脂新,開(kāi)發(fā)人員只需在web.xml文件中使用<listener>標(biāo)簽配置好監(jiān)聽(tīng)器挪捕,web容器就會(huì)自動(dòng)把監(jiān)聽(tīng)器注冊(cè)到事件源中。
- 一個(gè) web.xml 文件中可以配置多個(gè) Servlet 事件監(jiān)聽(tīng)器戏羽,web 服務(wù)器按照它們?cè)?web.xml 文件中的注冊(cè)順序來(lái)加載和注冊(cè)這些 Serlvet 事件監(jiān)聽(tīng)器担神。配置代碼如下所示:
<!-- 對(duì)監(jiān)聽(tīng)器進(jìn)行注冊(cè) --> <!-- 監(jiān)聽(tīng)器和Servlet、Filter不同始花,不需要url配置妄讯,監(jiān)聽(tīng)器執(zhí)行不是由用戶訪問(wèn)的,監(jiān)聽(tīng)器 是由事件源自動(dòng)調(diào)用的 --> <listener> <listener-class>cn.itcast.servlet.listener.MyServletContextListener</listener-class> </listener>
3酷宵、監(jiān)聽(tīng)三個(gè)域?qū)ο髣?chuàng)建和銷毀的事件監(jiān)聽(tīng)器
3.1亥贸、ServletContextListener
-
ServletContextListener 接口用于監(jiān)聽(tīng) ServletContext 對(duì)象的創(chuàng)建和銷毀事件。
-
當(dāng) ServletContext 對(duì)象被創(chuàng)建時(shí)浇垦,調(diào)用接口中的方法:
ServletContextListener.contextInitialized (ServletContextEvent sce)
-
當(dāng) ServletContext 對(duì)象被銷毀時(shí)炕置,調(diào)用接口中的方法:
ServletContextListener.contextDestroyed(ServletContextEvent sce)
-
-
ServletContext域?qū)ο蠛螘r(shí)創(chuàng)建和銷毀:
- 創(chuàng)建:服務(wù)器啟動(dòng)時(shí),針對(duì)每一個(gè)web應(yīng)用創(chuàng)建Servletcontext
- 銷毀:服務(wù)器關(guān)閉前男韧,先關(guān)閉代表每一個(gè)web應(yīng)用的ServletContext
-
ServletContext主要用來(lái)干什么朴摊?
-
保存全局應(yīng)用數(shù)據(jù)對(duì)象
- 在服務(wù)器啟動(dòng)時(shí),對(duì)一些對(duì)象進(jìn)行初始化此虑,并且將對(duì)象保存ServletContext數(shù)據(jù)范圍內(nèi) —— 實(shí)現(xiàn)全局?jǐn)?shù)據(jù)
- 例如:創(chuàng)建數(shù)據(jù)庫(kù)連接池
-
加載框架配置文件
- Spring框架(配置文件隨服務(wù)器啟動(dòng)加載) org.springframework.web.context.ContextLoaderListener
-
實(shí)現(xiàn)任務(wù)調(diào)度(定時(shí)器)甚纲,啟動(dòng)定時(shí)程序
java.util.Timer:一種線程設(shè)施,用于安排以后在后臺(tái)線程中執(zhí)行的任務(wù)朦前,可安排任務(wù)執(zhí)行一次介杆,或者定期重復(fù)執(zhí)行。
-
Timer提供了啟動(dòng)定時(shí)任務(wù)方法 Timer.schedule()韭寸,其中有兩種方法需要記状荷凇:
-
在指定的一個(gè)時(shí)間時(shí)啟動(dòng)定時(shí)器,定期執(zhí)行一次
Timer.schedule(TimerTask task, Date firstTime, long period)
-
在當(dāng)前時(shí)間延遲多少毫秒后啟動(dòng)定時(shí)器恩伺,定期執(zhí)行一次
Timer.schedule(TimerTask task, long delay, long period)
-
-
停止定時(shí)器赴背,取消任務(wù)
Timer.cancel()
-
-
demo:
package cn.itcast.servlet.listener; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Timer; import java.util.TimerTask; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; /** * 自定義 Context監(jiān)聽(tīng)器 * * @author seawind * */ public class MyServletContextListener implements ServletContextListener { @Override public void contextDestroyed(ServletContextEvent sce) { System.out.println("監(jiān)聽(tīng)ServletContext對(duì)象銷毀了..."); } @Override public void contextInitialized(ServletContextEvent sce) { System.out.println("監(jiān)聽(tīng)ServletContext對(duì)象創(chuàng)建了..."); // 獲得事件源 ServletContext servletContext = sce.getServletContext(); // 向ServletContext 中保存數(shù)據(jù) // 啟動(dòng)定時(shí)器 final Timer timer = new Timer(); // 啟動(dòng)定時(shí)任務(wù) // timer.schedule(new TimerTask() { // @Override // // 這就是一個(gè)線程 // public void run() { // System.out.println("定時(shí)器執(zhí)行了..."); // } // }, 0, 3000); // 馬上啟動(dòng) 每隔3秒重復(fù)執(zhí)行 // 指定時(shí)間啟動(dòng)定時(shí)器 DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); try { Date first = dateFormat.parse("2012-08-07 10:42:00"); timer.schedule(new TimerTask() { int i; @Override public void run() { i++; System.out.println("從10點(diǎn)40分開(kāi)始啟動(dòng)程序,每隔3秒重復(fù)執(zhí)行"); if (i == 10) { timer.cancel();// 取消定時(shí)器任務(wù) } } }, first, 3000); } catch (ParseException e) { e.printStackTrace(); } } }
3.2、HttpSessionListener
-
HttpSessionListener接口用于監(jiān)聽(tīng)HttpSession的創(chuàng)建和銷毀
-
創(chuàng)建一個(gè)Session時(shí)癞尚,接口中的該方法將會(huì)被調(diào)用:
HttpSessionListener.sessionCreated(HttpSessionEvent se)
-
銷毀一個(gè)Session時(shí)耸三,接口中的該方法將會(huì)被調(diào)用:
HttpSessionListener.sessionDestroyed (HttpSessionEvent se)
-
-
Session域?qū)ο髣?chuàng)建和銷毀:
- 創(chuàng)建:瀏覽器訪問(wèn)服務(wù)器時(shí)乱陡,服務(wù)器為每個(gè)瀏覽器創(chuàng)建不同的 session 對(duì)象浇揩,服務(wù)器創(chuàng)建session
- 銷毀:如果用戶的session的30分鐘沒(méi)有使用,session就會(huì)過(guò)期憨颠,我們?cè)赥omcat的web.xml里面也可以配置session過(guò)期時(shí)間胳徽。
-
demo:
package cn.itcast.servlet.listener; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; public class MyHttpSessionListener implements HttpSessionListener { @Override public void sessionCreated(HttpSessionEvent se) { // 通過(guò)事件對(duì)象獲得session 的id System.out.println("session被創(chuàng)建了"); HttpSession session = se.getSession(); System.out.println("id:" + session.getId()); } @Override public void sessionDestroyed(HttpSessionEvent se) { System.out.println("session被銷毀了"); HttpSession session = se.getSession(); System.out.println("id:" + session.getId()); } }
關(guān)于HttpSession的生命周期、具體描述詳見(jiàn):JSP 會(huì)話管理
3.3爽彤、ServletRequestListener(很少用)
- ServletRequestListener 接口用于監(jiān)聽(tīng)ServletRequest 對(duì)象的創(chuàng)建和銷毀:
- ServletRequest對(duì)象被創(chuàng)建時(shí)养盗,監(jiān)聽(tīng)器的requestInitialized方法將會(huì)被調(diào)用。
- ServletRequest對(duì)象被銷毀時(shí)适篙,監(jiān)聽(tīng)器的requestDestroyed方法將會(huì)被調(diào)用往核。
- ServletRequest域?qū)ο髣?chuàng)建和銷毀的時(shí)機(jī):
- 創(chuàng)建:用戶每一次訪問(wèn),都會(huì)創(chuàng)建一個(gè)reqeust
- 銷毀:當(dāng)前訪問(wèn)結(jié)束嚷节,request對(duì)象就會(huì)銷毀
- 這個(gè)監(jiān)聽(tīng)器最需要注意的:
- 使用forward ---- request創(chuàng)建銷毀一次 (因?yàn)檗D(zhuǎn)發(fā)本質(zhì)是一次請(qǐng)求)
- 使用sendRedirect ---- request創(chuàng)建銷毀兩次 (因?yàn)橹囟ㄏ虮举|(zhì)是兩次請(qǐng)求)
關(guān)于ServletRequest詳見(jiàn):http://www.reibang.com/p/7e2e3fd58e91
-
-
springboot 配置監(jiān)聽(tīng)器: 1.添加@WebListener注解 @WebListener public class MyHttpSessionListener implements HttpSessionListener { 2.添加@ComponentScan,@ServletComponentScan @ComponentScan("com.springboot.demo.*") @ServletComponentScan(basePackages = "com.springboot.demo.*") public class DemoApplication {
-