Servlet的监听器Listener(流量统计)

 

监听器:

•监听器-就是一个实现待定接口的普通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应用场景:

•记录一个网站的刷新量。
•当服务器关闭时,必须要保存到文件中或是数据库中去。
•当服务器启动时,先从文件中读取并放到ServletContext。
•在Filter中记录访问量,每次访问都加1。
 
•好处:信息不是太重要,没有必要每次用户访问都访问数据库或是操作文件。
•在为不影响用户的速度感受,应该开始一个新的线程同去操作数据。
 这样即使在后台使用同步技术,用户也不会感觉到速度很慢。

 

 

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>



 

 

 

 

 

设计思想:一个项目已经完成了,写死了,如果再想加代码进去,就用监听者模式!

 

 

 

 

 

 

 

 

 

 

 

阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zuosixiaonengshou/article/details/53638680
个人分类: JavaWeb开发技术
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭