Servlet的監聽器Listener(流量統計)
阿新 • • 發佈:2019-01-07
監聽器:
•監聽器-就是一個實現待定介面的普通Java程式,此程式專門用於監聽別一個類的方法呼叫。 •都是使用觀察者設計模式。 •什麼是觀察者模式: •定義物件間一對多的依賴關係,當一個物件的狀態發生改變時,所有依賴於它的物件都得到通知自動更新。 •示例: •GUI程式設計中的addXxxxListener都是觀察者模式。觀察者模式的三個重要類:
觀察者設計模式示例:
開發步驟:
* 第一步:實現一個需要被監聽的類Person.
* 第二步:實現一個監聽介面PersonListener。
* 第三步:在Person類中,提供一個方法用於註冊PersonListener類,即registerListener
* 第四步:必須要在Person類中維護PersonListener類的例項。
* 第五步:在呼叫person.eat方法是,判斷PersonListener是否為null,如果不為null則呼叫它的eating方法。
* 第六步:在Main類中,例項化Person,並註冊一個監聽。
給觀察者模式-新增事件源:
開發步驟:
第一步:在前頁的基礎上繼續新增一個PersonEvent類(注意我說是類不是介面),代表事件對像。
第二步:給PersonEvent對像,新增一個Object屬性,用以標識事件源對像。
第三步:修改PersonListener介面的eating方法,讓它接收一個PersonEvent引數。
第四步:在Person類eat方法中,如果判斷PersonListener屬性不為空,則在呼叫eating方法,例項化PersonEvent並傳給eating方法。
第五步:在main方法中,通過PersonEvent的getSource方法測試是否是同一個對像。
監聽示例1-監聽ServletContext的建立和銷燬:
開發步驟:
第一步:實現ServletContextListener介面。
第二步:實現兩個方法。
contextInitialized
contextDestroyed
第三步:在web.xml中新增<listener/>節點。
這一點與swing中的新增監聽有所區別。
第四步:測試
1、釋出專案啟動。
2、通過Tomcat管理控制檯停止此專案。
監聽器2:-監聽ServletContext上的屬性變化:
ContextListener應用場景:
•記錄一個網站的重新整理量。 •當伺服器關閉時,必須要儲存到檔案中或是資料庫中去。canListenerWeb
先來認識一下,純java中的監聽器:
MyJFrame.java JFrame中,按鈕點選事件就是一個監聽器。
package demo.hello;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
public class MyJFrame extends JFrame{
private MyJFrame(){
setBounds(200, 200, 200, 300);
setDefaultCloseOperation(EXIT_ON_CLOSE);
getContentPane().setLayout(new FlowLayout());
final JButton btn=new JButton("按鈕");
getContentPane().add(btn);
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// if(e.getSource()==btn){
// System.out.println("aaaaaaa");;
// }
if(e.getSource() instanceof JButton){
System.out.println("aaaaaaa");;
}
}
});
setVisible(true);
}
public static void main(String[] args) {
new MyJFrame();
}
}
Person.java
package demo.listenerPattern.one;
/*
* 被監聽者
* run方法放開讓別人監聽(觀察)
*/
public class Person {
private String name;
private IPersonListener listener;//如果這裡允許新增多個監聽,則宣告成集合List<IPeronListener>
public Person(String name) {
this.name = name;
}
public void run(){
if(listener!=null){
listener.runBeforeReaction();
}
System.out.println(name+"is running.....");
System.out.println(name+"is running.....");
System.out.println(name+"is running.....");
if(listener!=null){
listener.runAfterReaction();
}
}
//給外面註冊監聽器
public void addActionListener(IPersonListener listener){
this.listener=listener;
}
}
IPersonListener.java
package demo.listenerPattern.one;
/*
* 監聽器
* 實現該介面的類物件是監聽者
*/
public interface IPersonListener {
public abstract void runBeforeReaction();
public abstract void runAfterReaction();
}
Client.java
package demo.listenerPattern.one;
public class Client {
public static void main(String[] args) {
Person p=new Person("小天");
p.run();
System.out.println("---------------------");
p.addActionListener(new IPersonListener() {
@Override
public void runBeforeReaction() {
System.out.println("小天加油。。。快跑.....");
}
@Override
public void runAfterReaction() {
System.out.println("小天你竟然跑完了。。。厲害啊.....");
}
});
p.run();
}
}
註冊事件則監聽,否則不監聽
加入源事件:
Cat.java
package demo.listenerPattern.two;
public class Cat {
private String name;
private ICatListener listener;
public Cat(String name) {
this.name=name;
}
public void climb(){
if(listener!=null){
CatEvent e=new CatEvent(this);
listener.climbReaction(e);
}
System.out.println("a cat is climbing! his name is "+name);
}
public void addActionListener(ICatListener listener){
this.listener=listener;
}
public String getName() {
return name;
}
}
ICatListener.java
package demo.listenerPattern.two;
public interface ICatListener {
public void climbReaction(CatEvent e);
}
class CatEvent{
private Cat cat;
public CatEvent(Cat cat) {
this.cat=cat;
}
public Object getSource(){
return cat;
}
public String getName(){
return cat.getName();
}
}
Client.java
package demo.listenerPattern.two;
public class Client {
public static void main(String[] args) {
Cat cat=new Cat("ABC");
cat.climb();
System.out.println("---------------------");
cat.addActionListener(new ICatListener() {
@Override
public void climbReaction(CatEvent e) {
System.out.println("Look! there is a big fat cat!");
System.out.println(e.getSource());
}
});
cat.climb();
}
}
在JavaWeb中做監聽器:
這裡監聽了ServletContext的建立和銷燬和ServletContext上的屬性變化,做了一個訪問流量統計的小應用,並且把訪問量寫到了本地硬碟的一個檔案中永久儲存,不會因專案停止,訪問量就歸零了。
穿越整個軟體的生命週期:Filter+ServletContextListener(訪問量)
index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
</head>
<body>
<h1>這是主頁</h1>
<h2>等下做訪問量的應用</h2>
訪問量:${count }<br/>
<a href="<c:url value='/jsps/a.jsp' />">另一個介面</a>
<%
application.setAttribute("name", "Jack");
application.setAttribute("age", "22");
%>
</body>
</html>
CountFilter.java
package cn.hncu.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
public class CountFilter implements Filter{
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
//這裡統計訪問量只是一個小小的應用,如果說在這裡做統計,在多執行緒的情況下,容易出錯,應再開一個執行緒專門用來統計訪問量
final HttpServletRequest httpReq=(HttpServletRequest) request;
// new MyThread(httpReq).start();錯誤的解決方法
new Thread(){
public void run() {
AddCount.addCount(httpReq);
};
}.start();
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
}
class AddCount{
public synchronized static void addCount(HttpServletRequest req){
Integer count=Integer.parseInt(""+req.getServletContext().getAttribute("count"));
count++;
req.getServletContext().setAttribute("count", count);
}
}
/*
* 反模式:雖然同樣加了鎖,但是並不是同一個物件,所以加鎖沒有成功
*/
class MyThread extends Thread{
private HttpServletRequest req;
public MyThread(HttpServletRequest req){
this.req=req;
}
@Override
public void run() {
synchronized (this) {
Integer count=Integer.parseInt(""+req.getServletContext().getAttribute("count"));
count++;
req.getServletContext().setAttribute("count", count);
}
}
}
a.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
</head>
<body>
<h1>這是另外一個網頁</h1>
訪問量:${count }<br/>
<%
application.setAttribute("name", "Tom");//修改屬性
application.removeAttribute("age");//刪除屬性
%>
</body>
</html>
MyContextListener.java
package cn.hncu.listener;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
/*
* servlet監聽器開發步驟:
* 1.寫一個類實現XXXListener介面(6個=3個容器+3個容器中的屬性操作)
* 2.在web.xml中配置<listener>----規範:一般寫在<Filter>和<servlet>之間
*/
public class MyContextListener implements ServletContextListener{
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("專案初始化,讀取訪問量.....");
//count.text
try {
String fileName=sce.getServletContext().getRealPath("/count.txt");//專案根目錄的絕對路徑
BufferedReader br=new BufferedReader(new FileReader(fileName));
String srcCount=br.readLine();
Integer count=Integer.valueOf(srcCount);
sce.getServletContext().setAttribute("count", count);
} catch (Exception e) {
System.out.println("專案初始化讀取沒有點選量,出異常");
sce.getServletContext().setAttribute("count", 0);
}
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("專案關閉了,儲存點選量......");
String fileName=sce.getServletContext().getRealPath("/count.txt");//專案根目錄的絕對路徑
try {
//由於要把點選量原樣寫到本地檔案儲存起來,所以要用到PrintWriter,不然再次讀取時會掛掉(轉換不成整數了)
PrintWriter pw=new PrintWriter(fileName);//IO中要用絕對路徑
pw.println(sce.getServletContext().getAttribute("count"));
pw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
MyContextAttributeListener.java
package cn.hncu.listener;
import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
public class MyContextAttributeListener implements ServletContextAttributeListener{
@Override
public void attributeAdded(ServletContextAttributeEvent scae) {
System.out.println("這裡添加了一個屬性:"+scae.getName()+"--"+scae.getValue());
}
@Override
public void attributeRemoved(ServletContextAttributeEvent scae) {
System.out.println("這裡移除了一個屬性:"+scae.getName()+"--"+scae.getValue());
}
@Override
public void attributeReplaced(ServletContextAttributeEvent scae) {
System.out.println("這裡修改了一個屬性:"+scae.getName()+"--"+scae.getValue());
}
}
web.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<display-name></display-name>
<listener>
<listener-class>cn.hncu.listener.MyContextListener</listener-class>
</listener>
<listener>
<listener-class>cn.hncu.listener.MyContextAttributeListener</listener-class>
</listener>
<filter>
<filter-name>CountFilter</filter-name>
<filter-class>cn.hncu.filter.CountFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CountFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
設計思想:一個專案已經完成了,寫死了,如果再想加程式碼進去,就用監聽者模式!