一、监听器原理

1、监听器就是一个实现特定接口的普通java程序,这个程序专门用于监听一个java对象的方法调用或属性改变,当被监听对象发生上述事件后,监听器某个方法将立即被执行.

2、监听器典型案例:监听window窗口的事件监听器

public class Demo1 {
/**
* 面试题:请描述一下java事件监听机制.(和jquery事件完全类似)
* 1.java的事件监听机制涉及到三个组件:事件源、事件监听器、事件对象
* 2.当事件源上发生操作时,它将会调用事件监听器的一个方法,并在调用这个方法时,会传递事件对象过来.
* 3.事件监听器由开发人员编写,开发人员在事件监听器中,通过事件对象可以拿到事件源,从而对事件源上进行处理.
*/
public static void main(String[] args) {
Frame f = new Frame();//事件源
f.setSize(400, 400);
f.setVisible(true);
//这种传递实现接口的对象属于策略设计模式的应用
f.addWindowListener(new MyListener());//注册事件监听器
}
} class MyListener implements WindowListener{ public void windowClosing(WindowEvent e) {
Frame f = (Frame) e.getSource();
f.dispose();
}
public void windowDeactivated(WindowEvent e) {
}
...
}

3、自己实现监听器

//观察者设计模式(通常用来处理事件系统)
//事件源
class Person{
private PersonListener listener;
public void registerListener(PersonListener listener){
this.listener = listener;
}
public void run(){
if(listener!=null){
Even even = new Even(this);
this.listener.dorun(even);
}
System.out.println("runn!!");
}
public void eat(){
if(listener!=null){
Even e = new Even(this);
this.listener.doeat(e);
}
System.out.println("eat!!");
}
}
//事件监听器
interface PersonListener{
public void dorun(Even even);
public void doeat(Even even);
}
//事件对象(封装事件源)
class Even{
private Person person;
public Even() {
super();
}
public Even(Person person) {
super();
this.person = person;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
}
public class Demo3 { public static void main(String[] args) {
Person p = new Person();
p.registerListener(new MyListener1());
p.eat();
p.run();
}
} class MyListener1 implements PersonListener{ public void doeat(Even even) {
System.out.println(even.getPerson()+"你天天吃,你就知道吃,你猪啊!!");
}
public void dorun(Even even) {
System.out.println(even.getPerson()+"你吃完就跑,有病!!");
}
}

4、一个面试题,多个客户在餐厅并发点餐,现在只有一个打印机,打印出单子交给厨房,系统该怎么做?

一种方法是每隔10秒就检查一次,检测到就打印,这种不好,有cpu空转问题,应该是用个监听器取监听这个装菜单的容器(这个容器应该同步),只要它一add就执行监听器方法打印菜单.

ps:观察者模式的一个例子:求职者先在猎头处注册,当有新的工作机会时猎头就会通知求职者.

二、 servlet监听器

1、servlet监听器分类

在Servlet规范中定义了多种类型的监听器,它们用于监听的事件源分别为 ServletContext, HttpSession 和 ServletRequest 这三个域对象.Servlet规范针对这三个对象上的操作,又把这多种类型的监听器划分为三种类型.

•监听三个域对象创建和销毁的事件监听器

•监听域对象中属性的增加和删除的事件监听器

•监听绑定到 HttpSession 域中的某个对象的状态的事件监听器.(查看API文档),也就是某个javabean对象可以监听自己是否被放入HttpSession域中(无需在web.xml中配置,用的不多).

2、编写 Servlet 监听器

1>和编写其它事件监听器一样,编写servlet监听器也需要实现一个特定的接口,并针对相应动作覆盖接口中的相应方法.
2>和其它事件监听器略有不同的是,servlet监听器的注册不是直接注册在事件源上,而是由WEB容器负责注册,开发人员只需在web.xml文件中使用<listener>标签配置好监听器,web容器就会自动把监听器注册到事件源中.
3>一个 web.xml 文件中可以配置多个 Servlet 事件监听器,web 服务器按照它们在 web.xml 文件中的注册顺序来加载和注册这些 Serlvet 事件监听器.
3、监听三个域对象创建和销毁的事件监听器

1、ServletContextListener 接口用于监听 ServletContext 对象的创建和销毁事件.

当 ServletContext 对象被创建时,激发contextInitialized (ServletContextEvent sce)方法

当 ServletContext 对象被销毁时,激发contextDestroyed(ServletContextEvent sce)方法.

public class MyServletContextListener implements ServletContextListener {

    public void contextInitialized(ServletContextEvent sce) {
System.out.println("servletContext被创建了!");
}
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("servletcontext被销毁了!!");
}
}
//web.xml,这里web服务器通过反射创建MyServletContextListener对象然后注册给ServletContext
<listener>
<listener-class>cn.itcast.web.listener.ServletContextListener</listener-class>
</listener>

提问,servletContext域对象何时创建和销毁:

•创建:服务器启动针对每一个web应用创建servletcontext.

•销毁:服务器关闭前先关闭代表每一个web应用的servletContext.

ps:实际开发可以用来初始化配置文件,例如spring中这么使用.

2、HttpSessionListener接口用于监听HttpSession的创建和销毁.

提问,Session域对象何时创建和销毁:

•创建:用户每一次访问时,服务器创建session

•销毁:如果用户的session 30分钟没有使用,服务器就会销毁session,我们在web.xml里面也可以配置session失效时间

注意:

1>如果直接访问jsp页面会创建session,因为直接访问jsp会被web服务器转为Servlet,然后传给Servet八大隐式对象,直接访问Servlet不会创建session.
2>刷新页面不会重新创建session,基于一个浏览器弹出来的都共用一个session,关了浏览器session不会摧毁,驻留内存30分钟不用自动摧毁.
3>客户端禁用cookie,刷新页面会一直创建session,因为没有带sessionID过来,服务器会认为是一次新的回话.

ps:实际开发可以用来统计浏览器使用率,来访者ip等.

3、ServletRequestListener用于监听ServletRequest 对象的创建和销毁.

提问,ServletRequest域对象何时创建和销毁:

•创建:用户每一次访问,都会创建一个reqeust,sendRedirect会创建,forward不会.
•销毁:当前访问结束,request对象就会销毁

ps:实际开发可以用来统计网站点击量.

4、监听域对象中属性的增加和删除的事件监听器(用的不多)

1、这三个监听器接口分别是ServletContextAttributeListener, HttpSessionAttributeListener ServletRequestAttributeListener.这三个接口中都定义了三个方法来处理被监听对象中的属性的增加,删除和替换的事件,同一个事件在这三个接口中对应的方法名称完全相同,只是接受的参数类型不同.

2、当向被监听器对象中增加一个属性时,web容器就调用事件监听器的 attributeAdded 方法进行响应,这个方法接受一个事件类型的参数,监听器可以通过这个参数来获得正在增加属性的域对象和被保存到域中的属性对象.

//ServletContextAttributeListener
public class MyServletContextAttributeListener implements
ServletContextAttributeListener { public void attributeAdded(ServletContextAttributeEvent scab) {
String name = scab.getName();
Object value = scab.getValue();
System.out.println("向servletContext中存了:" + name + "=" + value);
}
public void attributeRemoved(ServletContextAttributeEvent scab) {
System.out.println("从servletcontext中删除了:" + scab.getName());
}
public void attributeReplaced(ServletContextAttributeEvent scab) {
System.out.println("servletcontext中" + scab.getName() + "属性被替换了");
}
} //HttpSessionAttributeListener,ServletRequestAttributeListener
public class HttpSessionAndServletRequestAttributeListener implements
HttpSessionAttributeListener, ServletRequestAttributeListener { public void attributeAdded(HttpSessionBindingEvent se) {
System.out.println("向session中加入东西了!!");
}
public void attributeRemoved(HttpSessionBindingEvent se) {
System.out.println("从session中删了东西!!");
}
public void attributeReplaced(HttpSessionBindingEvent se) {
System.out.println("把session中的属性替换了!!");
} public void attributeAdded(ServletRequestAttributeEvent srae) {
System.out.println("向request中加入东西了!!");
}
public void attributeRemoved(ServletRequestAttributeEvent srae) {
System.out.println("从request中删了东西!!");
}
public void attributeReplaced(ServletRequestAttributeEvent srae) {
System.out.println("把request中的属性替换了!!");
}
}

三、Servlet监听器案例

1、统计网站当前在线用户

public class CountNumListener implements HttpSessionListener {

    public void sessionCreated(HttpSessionEvent se) {
ServletContext context = se.getSession().getServletContext();
Integer count = (Integer) context.getAttribute("count");
if(count==null){
count = 1;
}else{
count++;
}
context.setAttribute("count", count);
} public void sessionDestroyed(HttpSessionEvent se) {
ServletContext context = se.getSession().getServletContext();
Integer count = (Integer) context.getAttribute("count");
count--;
context.setAttribute("count", count);
}
} //直接如下写法是不行的,无法在页面中取到,一般是显示在首页中.
public class CountNumListener implements HttpSessionListener {
int count;
public void sessionCreated(HttpSessionEvent se) {
count++;
}
public void sessionDestroyed(HttpSessionEvent se) {
count--;
}
}

2、自定义session扫描器,自己来管理session.

//将session装入集合自己管理,定时器每隔一段时间执行一次,session超过一段时间未使用,则删除.
public class SessionScanner implements HttpSessionListener,ServletContextListener {
//Collections这个集合工具类生成的集合是线程安全的.
private List<HttpSession> list = Collections.synchronizedList(new LinkedList<HttpSession>());
private Object lock = new Object(); public void contextInitialized(ServletContextEvent sce) {
Timer timer = new Timer();
timer.schedule(new MyTask(list,lock), 0, 30*1000);
} public void sessionCreated(HttpSessionEvent se) {
HttpSession session = se.getSession();
System.out.println(session + "被创建了!!");
synchronized (lock) { //锁旗标
list.add(session);
}
}
public void sessionDestroyed(HttpSessionEvent se) {
System.out.println(se.getSession() + "被销毁了");
}
public void contextDestroyed(ServletContextEvent sce) {
}
} class MyTask extends TimerTask{ private List list;
private Object lock;
public MyTask(List list,Object lock){
this.list = list;
this.lock = lock;
}
@Override
public void run() {
System.out.println("定时器执行!!");
synchronized (this.lock) {
ListIterator it = list.listIterator();
while(it.hasNext()){
HttpSession session = (HttpSession) it.next();
if((System.currentTimeMillis()-session.getLastAccessedTime())>30*1000){
session.invalidate();
//list.remove(session); //并发修改异常
it.remove();
}
}
}
}
}

ps:

class MyList{

    Object arr[] = new Object[10];
public void add(Object obj){ //session
if(arr[0]==null){
arr[0] = obj;
}
...
}
}

如上是自定义的一个集合,显示多个线程共同调用add方法时存在并发问题的原因,在if(arr[0]==null)后cpu切换,会造成集合元素的覆盖,并发环境下这里应该使用线程安全的集合Collections.synchronizedList或vector(线程安全的Arraylist),vector现在已经不推荐使用,Collections有将各种集合变为线程安全集合的方法.这里有一个注意问题:

List list = Collections.synchronizedList(new ArrayList());
...
synchronized(list) {
Iterator i = list.iterator(); // Must be in synchronized block
while (i.hasNext())
foo(i.next());
}

synchronizedList在迭代的时候,需要开发者自己加上线程锁控制代码,为什么呢

因为迭代器涉及的代码没有在java api中没有加上线程同步代码.

整个迭代的过程中如果在循环外面不加同步代码,在一次次迭代之间,其他线程对于这个容器的add或者remove会影响整个迭代的预期效果,所以这里需要用户在整个循环外面加上synchronized(list),这也是迭代中remove元素时出现并发访问异常的原因,感觉用for循环更好,就不用加锁了.

3、定时发邮件,现在一般使用线程池的定时任务代替传统的定时器.略

4、实现了HttpSessionActivationListener接口的 JavaBean 对象可以感知自己被活化和钝化的事件(用的不多)

//对象要写入硬盘必须序列化,实现Serializable 接口
public class MyBean implements HttpSessionActivationListener,Serializable { public void sessionDidActivate(HttpSessionEvent se) {
System.out.println("javabean随着session从硬盘回到内存了!!");
}
public void sessionWillPassivate(HttpSessionEvent se) {
System.out.println("javabean随着session到硬盘中去了!!");
}
}

5、网站实现踢人效果(就是将用户的session中去除),略.

javaEE(16)_Servlet监听器的更多相关文章

  1. JavaEE(16) - JPA生命周期及监听器

    1. 理解实体的生命周期 2. 为实体生命周期事件定义监听器 3. 通过监听实现回调 4. 排除默认监听器和父类上定义的监听器 1. 理解实体的生命周期(Net Beans创建Java Project ...

  2. javaEE(3)_servlet基础

    一.Servlet简介 1.Servlet是sun公司提供的一门用于开发动态web资源的技术,Sun公司在其API中提供了一个servlet接口,用户若想用发一个动态web资源(即开发一个Java程序 ...

  3. javaEE(15)_Servlet过滤器

    一.Filter简介 1.Filter也称之为过滤器,它是Servlet技术中最激动人心的技术,WEB开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, ...

  4. Java_Class 16方格拼图游戏

    public class Main { public static void main(String[] args) { // TODO Auto-generated method stub Game ...

  5. java监听器原理理解与实现

    监听器模型涉及以下三个对象,模型图如下: (1)事件:用户对组件的一个操作,称之为一个事件(2)事件源:发生事件的组件就是事件源(3)事件监听器(处理器):监听并负责处理事件的方法 执行顺序如下: 1 ...

  6. Jmeter(十九) - 从入门到精通 - JMeter监听器 -上篇(详解教程)

    1.简介 监听器用来监听及显示JMeter取样器测试结果,能够以树.表及图形形式显示测试结果,也可以以文件方式保存测试结果,JMeter测试结果文件格式多样,比如XML格式.CSV格式.默认情况下,测 ...

  7. Jmeter(二十) - 从入门到精通 - JMeter监听器 -下篇(详解教程)

    1.简介 监听器用来监听及显示JMeter取样器测试结果,能够以树.表及图形形式显示测试结果,也可以以文件方式保存测试结果,JMeter测试结果文件格式多样,比如XML格式.CSV格式.默认情况下,测 ...

  8. linux环境下安装oracle数据库 原文在卡卡100http://www.cnblogs.com/kaka100

    centos55_oracle11gr2_install   第一个阶段:安装centos55 a:安装centos5.5   用图形界面安装  硬盘 16G 注意:用图形界面安装.. 第二个阶段:配 ...

  9. centos55_oracle11gr2_install

    第一个阶段:安装centos55 a:安装centos5.5   用图形界面安装  硬盘 16G 注意:用图形界面安装.. 第二个阶段:配置1:检查内存情况# grep MemTotal /proc/ ...

随机推荐

  1. HDU 3499【最短路】

    题意: 给你一幅图,然后起点终点,然后有一个条件是可以使某条边的花费减半,求最短路的最小花费. 思路: (来自大哥) 最短路的时候多一维,途中是否有花费减半的边: 然后转移,如果上一条有减半的,这一条 ...

  2. Codevs 3134 Circle

    3134 Circle 题目描述 Description 在一个圆上,有2*K个不同的结点,我们以这些点为端点,连K条线段,使得每个结点都恰好用一次.在满足这些线段将圆分成最少部分的前提下,请计算有多 ...

  3. JS自制SEO框架(js案例)

    学习了JS一段时间,自己封装了一些日常码代码需要用到的框架,需要的小伙伴可以参考一下该框架主要功能有:阻止事件冒泡.阻止默认事件.获取元素.添加事件.删除事件.单个事件代理,多个事件代理.清除clas ...

  4. nmon性能监控工具学习

    nmon在AIX环境上,是一款很出名的系统性能监控工具,其实它除了可以运行在AIX,还可以在Linux环境下编译.使用. 源码下载地址: http://nmon.sourceforge.net/pmw ...

  5. Tyvj1474 打鼹鼠

    Description 在这个“打鼹鼠”的游戏中,鼹鼠会不时地从洞中钻出来,不过不会从洞口钻进去(鼹鼠真胆大……).洞口都在一个大小为n(n<=1024)的正方形中.这个正方形在一个平面直角坐标 ...

  6. 执行gulp build报错

    问题与分析 在执行gulp build报错如下: D:\coding\Resume\Resumes>gulp build gulp build[5628]: src\node_contextif ...

  7. PostgreSQL - pgAdmin4远程连接数据库

    前言 PostgreSQL在安装的时候自带的pgAdmin这个可视化工具,自从将PostgreSQL9升级到了10版本后,自带的pgAdmin也从3升级到了4版本.pgAdmin4的变化非常巨大,刚接 ...

  8. [題解]luogu P1156 垃圾陷阱

    前言:[數據刪除] 來源:題解 不發題面了 首先我们来分析题目,“每个垃圾都可以用来吃或堆放”,浓浓的透露出一个背包气息.我们可以类比背包问题的放或不放.于是dp[i][j]dp[i][j]dp[i] ...

  9. djangoXadmin

    是一个基于admin二次开发的开源组件,但是貌似已经停止开发了. 安装方式:(py3.6,django2.1) 1 先用pip安装xadmin2,它会安装xadmin和一些依赖包 2 用pip卸载xa ...

  10. eclipse导入mavn工程报Failure to transfer org.apache.maven.plugins:maven-resources-plugin:pom:2.6 的解决办法

    详细报错: Failure to transfer org.apache.maven.plugins:maven-resources-plugin:pom:2.6 from http://10.74. ...