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域中");
}
- 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("对象被钝化了"); } }
-
二、 定时器与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邮件访问协议, 用于接收邮件
- 收发邮件的原理
Java发送邮件的API(了解)
- Java Mail
- 导入JAR包
- 调用API.三个核心类
四、案例:发送生日祝福邮件
需求:根据数据库中用户的生日在用户生日当天给其发送生日祝福的邮件。
分析:
1. 首先,使用监听器,在服务器一运行时就执行定时器的操作
2. 监听器的操作代码:使每一天的凌晨00:00都能执行发送生日祝福邮件的代码。
1. listener调用service/dao层查询数据库中是否有当天生日的用户。
2. 在dao层获取到当天的日期,模糊查询,返回用户的List集合回到Listener的TimerTask中
3. 调用javaMail的API完成发送邮件的功能
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();
}
}
-
- 准备数据库,提供用户的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 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());
}
}