JavaEE-Listener学习笔记

Listener

一、Listener

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

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

  • Servlet监听器分类:

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

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

  • ServletContextListener(掌握)

    • 可以使用注解,可以在web.xml中配置
    • 监听 ServletContext 对象的创建和销毁
    • ServletContext对象生命周期
      • 服务器启动时创建,服务器正常关闭时销毁
    • ServletContextListener 企业应用
      • 保存全局唯一数据对象,获得ServletContext
      • 加载框架配置文件 spring
      • 启动定时器(固定时间需要自动运行程序)
  • 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域中");
    }
JavaEE-Listener学习笔记_第1张图片
自我状态感知监听器监听域中对象与session是否绑定
  • HttpSessionActivationListener
    • 负责钝化和活化的监听

    • 示例代码

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

          
              
              
                  
                  
              
          
        
      • 实现对应的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("对象被钝化了");
              }
          }
        
JavaEE-Listener学习笔记_第2张图片
img02.png

二、 定时器与Calendar日历类

  • Timer

    • schedule(TimerTask task, Date when) : 在指定的具体时间去执行执行某一任务,只执行一次
    • schedule(TimerTask task, long delay) : 在指定的延时时间之后去执行某一任务,只执行一次
    • 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() : 取消定时器
  • 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邮件访问协议, 用于接收邮件
  • 收发邮件的原理
JavaEE-Listener学习笔记_第3张图片
收发邮件的原理.png

Java发送邮件的API(了解)

  • Java Mail
  • 导入JAR包
  • 调用API.三个核心类
JavaEE-Listener学习笔记_第4张图片
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 threadLocal = new ThreadLocal();
        
        //获取连接池的方法
        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用户类
JavaEE-Listener学习笔记_第5张图片
准备数据库
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 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.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 测试结果

JavaEE-Listener学习笔记_第6张图片
img06.png
JavaEE-Listener学习笔记_第7张图片
img07.png

你可能感兴趣的:(JavaEE-Listener学习笔记)