JavaEE-Listener学习笔记

Listener

一、Listener

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

  • 由于Servlet技术存在三个数据域范围:ServletContext 、HttpSession、ServletRequest,所以Servlet监听器主要监听上面三个域对象的创建和销毁、属性变更以及状态变化

  • Servlet监听器分类:

    • 三个域范围对象创建和销毁监听器
    • 三个域范围对象属性变更监听器
    • HttpSession范围中对象的自我状态感知监听器

1: 域对象创建和销毁监听器

  • <font color='red'>ServletContextListener(掌握)

    • 可以使用注解,可以在web.xml中配置
    • 监听 ServletContext 对象的创建和销毁
    • ServletContext对象生命周期
      • 服务器启动时创建,服务器正常关闭时销毁
    • ServletContextListener 企业应用
      • 保存全局唯一数据对象,获得ServletContext
      • 加载框架配置文件 spring
      • 启动定时器(固定时间需要自动运行程序)</font>
  • HttpSessionListener

    • 可以使用注解,可以在web.xml中配置
    • 监听session对象创建和销毁
    • Session的生命周期
      • Session的创建 :request.getSession();
      • Session的销毁 :服务器非正常关闭、session超时(配置web.xml setMaxInactiveInterval)、session.invalidate
    • tomcat服务器正常关闭时,会将session中的数据序列化硬盘上
  • ServletRequestListener

    • 可以使用注解,可以在web.xml中配置
    • 监听request对象创建销毁
    • request的生命周期
      • request创建 :客户端提交请求时
      • request销毁 ;响应结束时

2: 域对象属性变更监听器

  • 可以使用注解,可以在web.xml中配置
  • Servlet规范定义了监听 ServletContext, HttpSession, HttpServletRequest 这三个对象中的属性变更信息事件的监听器
    • ServletContextAttributeListener

    在使用监听器监听域对象属性变更时,实现其接口后重写三个方法:add,replace,remove。
    分别对应setAttribute()和removeAttribute().
    需要注意的是,重写的监听属性修改和移除的方法时,如果调用getName()和getValue(),
    显示的是其方法执行前的值。

    
    
* HttpSessionAttributeListener
* ServletRequestAttributeListener
  • 这三个接口中都定义了三个方法来处理被监听对象中的属性的增加,删除和替换的事件,同一个事件在这三个接口中对应的方法名称完全相同,只是接受的参数类型不同
    • attributeAdded() : 某个属性被添加时,web容器调用事件监听器的这个方法进行相应
    • attributeRemoved() : 某个属性被移除时,web容器调用事件监听器的这个方法进行相应
    • attributeReplaced() : 某个属性被替换时,web容器调用事件监听器的这个方法进行相应

3: HttpSession中对象状态感知监听器

  • 无需配置注解,或在web.xml中配置

  • HttpSession中对象的状态

    • 将对象保存在Session中
    • 将对象从session中移除
    • 当一个对象长时间存在Session中,而Session没有使用,将Session对象数据序列化硬盘(钝化)
    • 当使用一个已经钝化对象,需要从硬盘上将对象加载到内存(活化)
  • HttpSessionBindingListener

    • 负责绑定和解除绑定的监听
    • 如果一个对象已经在session存在中,再次存入时,会触发一次绑定和一次解除绑定,相当于替代了之前那个对象。

测试:javabean:User类实现HttpSessionBindingListener接口

//监听User对象是否存入Session对象。状态自我感知器,需事先监听器接口
部分代码:
public class User implements HttpSessionBindingListener{
    
    public void valueBound(HttpSessionBindingEvent event) {
        System.out.println("User对象存入session域中");
    }

    public void valueUnbound(HttpSessionBindingEvent event) {
        System.out.println("User对象未存入session域中");
    }
自我状态感知监听器监听域中对象与session是否绑定
  • HttpSessionActivationListener
    • 负责钝化和活化的监听

    • 示例代码

      • 在当前工程 WebRoot/META-INF/context.xml文件中配置以下内容

          <Context>
              <!-- maxIdleSwap="1" 代表如果一个session超过1分钟没有使用,就钝化到硬盘 -->
              <Manager className="org.apache.catalina.session.PersistentManager"
                  maxIdleSwap="1">
                  <!-- directory="it315" 代表钝化文件保存的目录为it315.这个目录会生成在tomcat/work目录 -->
                  <Store className="org.apache.catalina.session.FileStore"
                      directory="it315" />
              </Manager>
          </Context>
        
      • 实现对应的JavaBean.需要特别注意对应的JavaBean需要实现Serializable接口,否则是无法序列化到本地的.

          public class User implements HttpSessionActivationListener, Serializable {
              private String name;
              public String getName() {
                  return name;
              }
              public void setName(String name) {
                  this.name = name;
              }
              // 如果该类没有实现Serializable接口,将无法序列化到本地硬盘
              // 当活化时,也无法读取到对应的数据
              public void sessionDidActivate(HttpSessionEvent se) {
                  System.out.println("对象被活化了");
              }
              public void sessionWillPassivate(HttpSessionEvent se) {
                  System.out.println("对象被钝化了");
              }
          }
        
img02.png

二、 定时器与Calendar日历类

  • Timer

    • schedule(TimerTask task, Date when) : 在指定的具体时间去执行执行某一任务,只执行一次
    • schedule(TimerTask task, long delay) : 在指定的延时时间之后去执行某一任务,只执行一次
    • <font color='red'>schedule(TimerTask task, Date when, long period) : 在指定的具体时间开始执行某一任务,不管任务是否执行完毕,一旦时间过了period参数指定的时间间隔,再次开启任务去执行.
    • schedule(TimerTask task, long delay, long period) : 在指定的延时时间开始执行某一任务,不管任务是否执行完毕,一旦时间过了period参数指定的时间间隔,再次开启任务去执行.
    • scheduleAtFixedRate(TimerTask task, long delay, long period) : 在指定的延时时间开始执行某一任务,执行完毕后,一旦时间过了period参数指定的时间间隔,再次开启任务去执行.
    • scheduleAtFixedRate(TimerTask task, Date when, long period) : 在指定的具体时间开始执行某一任务,执行完毕后,一旦时间过了period参数指定的时间间隔,再次开启任务去执行.
    • cancel() : 取消定时器</font>
  • Calendar

    • Calendar.getInstance() : 获取日历对象
    • Calendar.get(int field) : 获取具体字段的值 , 注意month字段是从0开始的
    • Calendar.set(int field, int value) : 设置具体某一字段的值
    • Calendar.getTime() : 获取当前时间的Date对象
    • Calendar.add(int field,int amount):根据日历的规则,为给定的日历字段添加或减去指定的时间量

三、 邮件相关概念

  • 邮件服务器 : 负责电子邮件收发管理的设备
  • 电子邮箱 : 在邮件服务器上开辟一块空间给用户, 用于收发邮件
  • 常见邮件协议
    • POP3 : Post Office Protocol - Version 3,邮局协议版本3, 用于接收邮件
    • SMTP : Simple Mail Transfer Protocol,简单邮件传输协议, 用于发送邮件
    • IMAP : Internet Mail Access Protocol,Internet邮件访问协议, 用于接收邮件
  • 收发邮件的原理
收发邮件的原理.png

Java发送邮件的API(了解)

  • Java Mail
  • 导入JAR包
  • 调用API.三个核心类
img04.png

四、案例:发送生日祝福邮件

需求:根据数据库中用户的生日在用户生日当天给其发送生日祝福的邮件。

分析:

1. 首先,使用监听器,在服务器一运行时就执行定时器的操作
2. 监听器的操作代码:使每一天的凌晨00:00都能执行发送生日祝福邮件的代码。
    1. listener调用service/dao层查询数据库中是否有当天生日的用户。
    2. 在dao层获取到当天的日期,模糊查询,返回用户的List集合回到Listener的TimerTask中
    3. 调用javaMail的API完成发送邮件的功能

1. 搭建环境

  1. 需要查询数据库,添加所需的jar包:数据库连接驱动,c3p0.jar,c3p0-config.xml,dbutils.jar及JDBCUtils工具类。

    public class JDBCUtils {

        private static DataSource ds = new ComboPooledDataSource();//根据默认配置文件创建连接池
        //创建与线程绑定的存储connection的map集合
        private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();
        
        //获取连接池的方法
        public static DataSource getDataSource() {
            return ds;
        }
        
        //从连接池中获取连接的方法
        public static Connection getConnection() throws SQLException {
            return ds.getConnection();
        }
        
        //从ThreadLocal中获取与当前线程绑定的链接的方法
        public static Connection getConnectionTL() throws SQLException {
            Connection connection = threadLocal.get();
            if(connection == null) {
                threadLocal.set(getConnection());
                connection = threadLocal.get();
            }
            return connection;
        }
        
        //开启事务的方法
        public static void startTransaction() throws SQLException {
            Connection connection = getConnectionTL();
            connection.setAutoCommit(false);
        }
        
        //提交事务并释放资源的方法
        public static void commitAndRelease() throws SQLException {
            Connection connection = getConnectionTL();
            connection.commit();
            if(connection != null) {
                connection.close();
                connection = null;//置为null以便更快的被gc垃圾回收器回收,释放内存
            }
            threadLocal.remove();//与threadLocal解绑
        }
        
        //回滚事务并释放资源的方法
        public static void roolbackAndRelease() throws SQLException {
            Connection connection = getConnectionTL();
            connection.rollback();
            if(connection != null) {
                connection.close();
                connection = null;
            }
            threadLocal.remove();
        }
    }
    1. 准备数据库,提供用户的id,姓名,出生日期,邮箱的账号 。同时创建javabean用户类
准备数据库
3.需要使用到javaMail发送邮件,提供javaMail.jar的jar包和发送邮件的工具类MailUtils
4.搭建邮箱服务器环境,修改邮箱工具类的参数,即发送者的账号密码
4.新建包结构web--service--dao层

2. 代码实现

2.1 使用监听器,在服务器一运行时就执行定时器的操作

public class MyServletContextListener implements ServletContextListener {

//监听服务器启动后执行的方法
public void contextInitialized(ServletContextEvent sce) {
    //使用定时器,让其从服务器启动后的每天凌晨00:00执行该定时器内的TimerTask
    Timer timer = new Timer();
    TimerTask task = new TimerTask() {
        //匿名内部类,实质是该TimerTask具体的子类对象
        @Override
        public void run() { //run方法中写需要定时执行的方法
            System.out.println("定时器执行了---");
            try {
                //调用service/dao层查询用户的当前日期生日的用户
                BirthdayService service = new BirthdayService();
                List<User> list = service.findBirthday();
                
                //遍历list集合,获取到每个用户的邮箱,更改MailUtils的参数,调用方法发送邮件
                for (User user : list) {
                    String receiver = user.getEmail();
                    String emailBody = "亲爱的"+user.getName()+",祝你生日快乐!";
                    MailUtils.sendMail(receiver, emailBody);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            
        }
    };
    
    //参数1:设置定时器要执行的方法:这里要写发送邮件的功能,需要等服务器返回用户的信息后编写
    //参数2: 可以写两种参数,可以写具体的Date日期指定某个时间执行,也可以填入距离当前时间后的多少毫秒值执行
    //参数3:隔多少毫秒执行一次该定时器方法,这里写的1天
    Long delayTime = DateUtils.getDelayTime();//当前时间距离明天零点的延迟时间
    //timer.scheduleAtFixedRate(task, delayTime, 24*60*60*1000);
    //为了测试,将执行方法的参数2改为0,参数3设为1000,即服务器启动立即执行,每隔1秒钟执行一次
    timer.scheduleAtFixedRate(task, 0, 1000);
}

2.2 编写service层

BirthdayDAO dao = new BirthdayDAO();
//获取到当前时间 ,格式: 06-07
String birthday = DateUtils.getCurrentMonth()+"-"+DateUtils.getCurrentDay();
return dao.findBirthday(birthday);

2.3 编写dao层

QueryRunner runner = new QueryRunner(JDBCUtils.getDataSource());
String sql = "select * from user where birthday like ?";
System.out.println(birthday);
return runner.query(sql, new BeanListHandler<User>(User.class), "%"+birthday);

2.4 DateUtils工具类

public class DateUtils {

    /**
     * 获得当前时间距离指定日期零点的延迟时间
     * 
     * @param amount
     * @return
     */
    public static Long getDelayTime(int amount) {
        // 1 设置当前时间
        Calendar calendar = Calendar.getInstance();
        Date newDate = new Date();
        calendar.setTime(newDate);
        // 2 将时分秒设置成0
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        // 3 设置指定天数
        calendar.add(Calendar.DATE, amount);
        // 4 计算当前时间距离设置日期零点的延迟时间
        return calendar.getTimeInMillis() - newDate.getTime();
    }

    /**
     * 当前时间距离明天零点的延迟时间
     * 
     * @return
     */
    public static Long getDelayTime() {
        return getDelayTime(1);
    }

    /**
     * 获得一天的毫秒值
     * 
     * @return
     */
    public static Long getOneDay() {
        return 24 * 60 * 60 * 1000L;
    }

    /**
     * 获得当前时间的月份(两位)
     * 
     * @return
     */
    public static String getCurrentMonth() {
        // 1 设置当前时间
        Calendar calendar = Calendar.getInstance();
        Date newDate = new Date();
        calendar.setTime(newDate);

        int m = calendar.get(Calendar.MONTH) + 1;
        if (m < 10) {
            return "0" + m;
        }
        return "" + m;
    }

    /**
     * 获得当前时间中的几号(两位)
     * 
     * @return
     */
    public static String getCurrentDay() {
        // 1 设置当前时间
        Calendar calendar = Calendar.getInstance();
        Date newDate = new Date();
        calendar.setTime(newDate);

        int d = calendar.get(Calendar.DATE);
        if (d < 10) {
            return "0" + d;
        }
        return "" + d;
    }

    public static void main(String[] args) {
        System.out.println(getCurrentMonth());
        System.out.println(getCurrentDay());
    }
}

2.5 测试结果

img06.png
img07.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,904评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,581评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,527评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,463评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,546评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,572评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,582评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,330评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,776评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,087评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,257评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,923评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,571评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,192评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,436评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,145评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容

  • 本文包括:1、Listener简介2、Servlet监听器3、监听三个域对象创建和销毁的事件监听器4、监听三个域对...
    廖少少阅读 6,059评论 6 28
  • 一、概述 监听器就是一个实现特定接口的普通java程序,这个程序专门用于监听另一个java对象的方法调用或属性改变...
    yjaal阅读 1,991评论 0 5
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,230评论 11 349
  • 一、监听器的概览 监听器是指专门用于对其他对象身上发生的事件或状态的改变进行监听和相应处理的对象,当被监视的对象发...
    不知名的蛋挞阅读 1,353评论 0 3
  • 最近这段时间我在复习《财富自由之路》书籍,我通过复习和思考给我带来了又是一种不一样的感触,多了一份深刻地记忆,多了...
    小西伯利亚狼阅读 238评论 0 0