JavaEE-Listener学习笔记

作者: 明天你好向前奔跑 | 来源:发表于2017-06-07 22:43 被阅读124次

    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

    相关文章

      网友评论

        本文标题:JavaEE-Listener学习笔记

        本文链接:https://www.haomeiwen.com/subject/jmaafxtx.html