一、监听器
1.监听器(Listener)
是一个专门用于对其他对象身上发生的事件或状态改变进行监听和相应处理的对象,当被监视的对象发生情况时,立即采取相应的行动。监听器其实就是一个实现特定接口的普通java程序,这个程序专门用于监听另一个java对象的方法调用或属性改变,当被监听对象发生上述事件后,监听器某个方法立即被执行
简而言之:所谓的监听器就是一个能够监听程序内容或者状态是否发生改变的一个Java类
有点类似现实世界中大厦里的火灾警报器
补充讲解:与监听器相关的术语
事件源:指的是被监听对象(汽车)
监听器:指的是监听的对象(报警器)
事件源和监听器绑定:在汽车上安装报警器
事件:指的是事件源对象的改变(踹了汽车一脚)----主要功能获得事件源对象。
2.监听器的作用
导读模块:虽然监听器的作用很强大,但是servlet中并没有相关的实现类,只是有一些接口,而这也恰恰体现出了servlet的灵活性
1.那么servlet中都提供了哪些监听器呢?
补充:1)其实servlet中的监听器主要是针对request,session,servletContext开展的
2)监听器要想起作用则必须要有实现类,而实现类要起作用,则必须要有动作,所谓的动作其实就是事件,所谓的事件就是与之关联的一些类
3)servlet中一共有8个监听器接口,6个事件
1.1 8个监听器接口和6个事件:
1、ServletContextListener接口
[接口方法] contextInitialized()与 contextDestroyed()
[接收事件] ServletContextEvent
[触发场景] 在Container加载Web应用程序时(例如启动 Container之后),会呼叫contextInitialized(),而当容器移除Web应用程序时,会呼叫contextDestroyed ()方法。
2、ServletContextAttributeListener
[接口方法] attributeAdded()、 attributeReplaced()、attributeRemoved()
[接收事件] ServletContextAttributeEvent
[触发场景] 若有对象加入为application(ServletContext)对象的属性,则会呼叫attributeAdded(),同理在置换属性与移除属性时,会分别呼叫attributeReplaced()、attributeRemoved()。
3、ServletRequestListener
[接口方法] requestInitialized()与 requestDestroyed()
[接收事件] RequestEvent
[触发场景] 在request(HttpServletRequest)对象建立或被消灭时,会分别呼叫这两个方法。
4、ServletRequestAttributeListener
[接口方法] attributeAdded()、 attributeReplaced()、attributeRemoved()
[接收事件] HttpSessionBindingEvent
[触发场景] 若有对象加入为request(HttpServletRequest)对象的属性,则会呼叫attributeAdded(),同理在置换属性与移除属性时,会分别呼叫attributeReplaced()、 attributeRemoved()。
5、HttpSessionListener
[接口方法] sessionCreated()与sessionDestroyed ()
[接收事件] HttpSessionEvent
[触发场景] 在session (HttpSession)对象建立或被消灭时,会分别呼叫这两个方法。
6、HttpSessionActivationListener
[接口方法] sessionDidActivate()与 sessionWillPassivate()
[接收事件] HttpSessionEvent
[触发场景] Activate与Passivate是用于置换对象的动作,当session对象为了资源利用或负载平衡等原因而必须暂时储存至硬盘或其它储存器时(透 过对象序列化),所作的动作称之为Passivate,而硬盘或储存器上的session对象重新加载JVM时所采的动作称之为Activate,所以容 易理解的,sessionDidActivate()与 sessionWillPassivate()分别于Activeate后与将Passivate前呼叫。
7、HttpSessionAttributeListener
[接口方法] attributeAdded()、 attributeReplaced()、attributeRemoved()
[接收事件] HttpSessionBindingEvent
[触发场景] 若有对象加入为session(HttpSession)对象的属性,则会呼叫attributeAdded(),同理在置换属性与移除属性时,会分别呼叫attributeReplaced()、 attributeRemoved()。
8、HttpSessionBindingListener
[接口方法] valueBound()与valueUnbound()
[接收事件] HttpSessionBindingEvent
[触发场景] 实现HttpSessionBindingListener接 口的类别,其实例如果被加入至session(HttpSession)对象的属性中,则会呼叫 valueBound(),如果被从session(HttpSession)对象的属性中移除,则会呼叫valueUnbound(),实现 HttpSessionBindingListener接口的类别不需在web.xml中设定。
小结与分类:
备:一共分为三大类
(1)监听ServletContext、Request、Session对象的创建和销毁,需要在web.xml中配置
ServletContextListener
ServletRequestListener
HttpSessionListener
友情提示:这三种是最常用的了…
(2)监听ServletContext、Request、Session对象属性的变化,需要在web.xml中配置
ServletContextAttributeListener
ServletRequestAttributeListener
HttpSessionAttributeListener
(3)监听Session内部的对象,不需要再web.xml中配置
HttpSessionActivationListener
HttpSessionBindingListener
2.监听器的作用:
统计在线人数,利用HttpSessionLisener
加载初始化信息:利用ServletContextListener
统计网站访问量
实现访问监控
后续的Spring等框架中也要用到
3.监听器实例
3.1使用步骤
1)创建实现类实现需要的接口
2)在web.xml中配置<listener></listener>节点进行声明
3)实现接口中的方法
3.2统计在线人数监听器实例:
`package com.rj.bd.manage.listeners;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import com.sun.org.apache.bcel.internal.generic.NEW;
public class SessionListener implements HttpSessionListener {
/**
* @desc 1.session创建的时候触发
*/
@Override
public void sessionCreated(HttpSessionEvent se) {
System.out.println("Session 开始创建了:: ID="+se.getSession().getId());
ServletContext ctx = se.getSession( ).getServletContext( );
Integer numSessions = (Integer) ctx.getAttribute("numSessions");
if (numSessions == null) {
numSessions = new Integer(1);
}
else {
int count = numSessions.intValue( );
numSessions = new Integer(count + 1);
}
ctx.setAttribute("numSessions", numSessions);
}
/**
* @desc 2.session销毁的时候触发
*/
@Override
public void sessionDestroyed(HttpSessionEvent se)
{
System.out.println("Session 销毁了:: ID="+se.getSession().getId());
ServletContext ctx=se.getSession().getServletContext();
Integer numSessions = (Integer)ctx.getAttribute("numSessions");
if(numSessions == null) {
numSessions = new Integer(0);
}
else {
int count = numSessions.intValue( );
numSessions = new Integer(count - 2);
}
ctx.setAttribute("numSessions", numSessions);
}
}`
4扩展:监听器,过滤器,servlet的级别
【Listener】->【Filter】->【Servlet】
5.预加载实例
`package com.rj.bd.manage.listeners;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class MyLiceneListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("===============servlet服务初始化(也就是tomcat正式启动成功)==================");
System.out.println("***************1.可以开始预加载一些信息了****************");
ServletContext context = sce.getServletContext();
/**
* 1.可以预先加载连库4要素
*/
Map<String, Object> map=new HashMap<String, Object>();
map.put("username", "root");
map.put("password", "root");
map.put("drivername", "com.mysql.jdbc.Driver");
map.put("url", "jdbc://..........");
context.setAttribute("map", map);
/**
* 2.也可以在这里实现注入....其实也就是Spring框架核心的注入的原理所在。。。。。(后续讲)
*/
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("===============servlet服务销毁(也就是tomcat重新部署)==================");
}
}`
6.Request请求监听器记录ip实例
`package com.rj.bd.manage.listeners;
import javax.servlet.ServletRequest;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
public class MyServletRequestListener implements ServletRequestListener {
@Override
public void requestDestroyed(ServletRequestEvent sre) {
ServletRequest servletRequest = sre.getServletRequest();
System.out.println("ServletRequest destroyed. Remote IP="+servletRequest.getRemoteAddr());
}
@Override
public void requestInitialized(ServletRequestEvent sre) {
ServletRequest servletRequest = sre.getServletRequest();
System.out.println("ServletRequest initialized. Remote IP="+servletRequest.getRemoteAddr());
}
}`
二、发送邮件
1.生成四位验证码实例
`public class CodeUtils {
public static void main(String[] args) {
for (int i = 0; i <10; i++) {
CodeUtils.createCode();
}
}
public static String createCode()
{
String[] beforeShuffle = new String[] { "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };
List<String> list = Arrays.asList(beforeShuffle);
Collections.shuffle(list);//自动洗牌
StringBuilder sb =new StringBuilder();
for (int i = 0; i < list.size(); i++)
{
sb.append(list.get(i));
}
String afterShuffle = sb.toString();
String result = afterShuffle.substring(5, 9);
System.out .println(result) ;
return result;
}
}`
2.日期处理工具类
public class DateUtils {
/**
* 通过时间秒毫秒数判断两个时间的间隔
* @param date1
* @param date2
* @return
* @throws ParseException
*/
public static int differentDaysByMillisecond(String dateStr,String dateStr2) throws ParseException
{
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
Date fDate=format.parse(dateStr);
Date oDate=format.parse(dateStr2);
//初始化日历工具类
Calendar aCalendar = Calendar.getInstance();
//设定开始的日期
aCalendar.setTime(fDate);
int day1 = aCalendar.get(Calendar.DAY_OF_YEAR);//DAY_OF_YEAR:一年的第多少天
//社情结束的日期
aCalendar.setTime(oDate);
int day2 = aCalendar.get(Calendar.DAY_OF_YEAR);
int days=day2-day1;
System.out.print(days);
return days;
}
public static void main(String[] args) throws ParseException {
String dateStr = "2019-11-26 ";
Date now=new Date();//获取当前的日期
SimpleDateFormat dFormat=new SimpleDateFormat("yyyy-MM-dd");//对当前日期进行格式化处理
System.out.println("dateStr2:"+dFormat.format(now));
System.out.println("两个日期的差距:" + differentDaysByMillisecond(dateStr,dFormat.format(now)));
}
}
3.properties配置文件处理类
1.创建一个config文件夹,且该文件夹与src是平级的,且权限也是与src的级别一样(要构建权限级别),在该目录下创建mail.properties
2.在mail.properties写入一些数据
emailFrom=发送的邮箱
emialFromAuthorization=邮箱码
emailRole=XX管理系統
PS:在mail,properties中要对该文件设定编码集为UTF-8,且不要再里面有过多的空格和换行
package com.rj.bd.prepar;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
public class PropertyUtils {
public static Map<String, Object> getyInfoOfPropert(String propertyPath) throws IOException
{
Map<String, Object> map=new HashMap<String, Object>();
Properties prop=new Properties();
InputStream inputFile=PropertyUtils.class.getClassLoader().getResourceAsStream(propertyPath);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputFile,"utf-8"));
prop.load(bufferedReader);
Iterator<String> it=prop.stringPropertyNames().iterator();
while(it.hasNext()){
String key=it.next();
System.out.println(key+":"+prop.getProperty(key));
map.put(key, prop.getProperty(key));
}
inputFile.close();
return map;
}
public static void main(String[] args) throws IOException {
PropertyUtils.getyInfoOfPropert("mail.properties");
}
}
4.补充讲解
导读模块:1.日常工作生活中少不了和邮件打交道,比如我们会用邮件进行信息交流,向上级汇报日常工作;再比如大家熟悉的某个WEB系统注册阶段,通常会有一个功能,点击发送到目标邮箱的链接完成账户激活
2.开始之前先简单介绍一下与邮件相关的一些协议
SMTP 协议
SMTP 的全称是 Simple Mail Transfer Protocol,即简单邮件传输协议,它是一组用于从源地址到目的地址传输邮件的规范,通过它来控制邮件的中转方式
SMTP 协议属于TCP/IP协议簇,它帮助每台计算机在发送或中转信件时找到下一个目的地。
SMTP 服务器就是遵循SMTP协议的发送邮件服务器
SMTP 认证,简单地说就是要求必须在提供了账户名和密码之后才可以登录 SMTP 服务器,这就使得那些垃圾邮件的散播者无可乘之机
增加 SMTP 认证的目的是为了使用户避免受到垃圾邮件的侵扰
实际上,SMTP 已经是E-mail传输的标准
SMTP 规范(RFC 821)规定,每一行都要以\r\n 来结尾
POP 协议
POP3(Post Office Protocol 3)即邮局协议的第3个版本,是因特网电子邮件的第一个离线协议标准
POP邮局协议负责从邮件服务器中检索电子邮件
它要求邮件服务器完成下面几种任务之一
从邮件服务器中检索邮件并从服务器中删除这个邮件
从邮件服务器中检索邮件但不删除它
不检索邮件,只是询问是否有新邮件到达
POP协议支持多用户互联网邮件扩展,后者允许用户在电子邮件上附带二进制文件,如文字处理文件和电子表格文件等,实际上这样就可以传输任何格式的文件了,包括图片和声音文件等
在用户阅读邮件时,POP命令所有的邮件信息立即下载到用户的计算机上,不在服务器上保留
IMAP 协议
互联网信息访问协议(IMAP)是一种优于POP的新协议
和POP一样,IMAP也能下载邮件、从服务器中删除邮件或询问是否有新邮件,但IMAP克服了POP的一些缺点
它可以决定客户机请求邮件服务器提交所收到邮件的方式,请求邮件服务器只下载所选中的邮件而不是全部邮件
客户机可先阅读邮件信息的标题和发送者的名字再决定是否下载这个邮件
通过用户的客户机电子邮件程序,IMAP可让用户在服务器上创建并管理邮件文件夹或邮箱、删除邮件、查询某封信的一部分或全部内容,完成所有这些工作时都不需要把邮件从服务器下载到用户的个人计算机上。
小结:不同的协议实现的功能的侧重点不一样
因为人们发的邮件不一样,所以这些协议还是都要用的
3.我们今天要讲的邮件发送并不是指从头到尾的创造一整套的邮件发送系统,而是利用现有的邮件服务商例如网易或者腾讯作为我们广义上的发送代理进而实现发送邮件
普通邮件发送
public class EmailUtils {
//自己的邮箱
public static String sendEmailAccount ="******@163.com";
//邮箱授权码
public static String sendEmailPwd="***********";
//收件人邮箱
public static String receiveMailAccount="***********@qq.com";
public static void main(String[] args) throws Exception {
createMimeMessage( sendEmailAccount, receiveMailAccount);
}
/**
* 创建一封只包含文本的简单邮件
*
* @param session 和服务器交互的会话
* @param sendMail 发件人邮箱
* @param receiveMail 收件人邮箱
* @return
* @throws Exception
*/
public static void createMimeMessage(String sendMail, String receiveMail) throws Exception {
// 1. 创建参数配置, 用于连接邮件服务器的参数配置
Properties props = new Properties();
props.setProperty("mail.transport.protocol","smtp");// 使用的协议(JavaMail规范要求)
props.setProperty("mail.smtp.host","smtp.163.com"); // 发件人的邮箱的 SMTP 服务器地址
props.setProperty("mail.smtp.auth", "true"); // 需要请求认证
// 2. 根据配置创建会话对象, 用于和邮件服务器交互
Session session = Session.getInstance(props);
session.setDebug(true); // 设置为debug模式, 可以查看详细的发送 log
// 3. 创建一封邮件
MimeMessage message = new MimeMessage(session);
// 4. From: 发件人(昵称有广告嫌疑,避免被邮件服务器误认为是滥发广告以至返回失败,请修改昵称)
message.setFrom(new InternetAddress(sendMail, "发件人的昵称", "UTF-8"));
// 5. To: 收件人(可以增加多个收件人、抄送、密送)
// CC:抄送人,BCC:密送
message.setRecipient(MimeMessage.RecipientType.TO, new InternetAddress(receiveMail, "XX用户", "UTF-8"));
//message.setRecipient(MimeMessage.RecipientType.CC, new InternetAddress("xxxx@qq.com", "XX用户", "UTF-8"));
// 6. Subject: 邮件主题(标题有广告嫌疑,避免被邮件服务器误认为是滥发广告以至返回失败,请修改标题)
message.setSubject("这是邮箱标题", "UTF-8");
// 7. Content: 邮件正文(可以使用html标签)(内容有广告嫌疑,避免被邮件服务器误认为是滥发广告以至返回失败,请修改发送内容)
message.setContent("正文...", "text/html;charset=utf-8");
// 8. 设置发件时间
message.setSentDate(new Date());
// 9. 保存设置
message.saveChanges();
// 10. 根据 Session 获取邮件传输对象
Transport transport = session.getTransport();
// 邮箱账号 和 密码 连接邮件服务器, 这里认证的邮箱必须与 message 中的发件人邮箱一致, 否则报错
// 5. 使用
//
// PS_01: 成败的判断关键在此一句, 如果连接服务器失败, 都会在控制台输出相应失败原因的 log,
// 仔细查看失败原因, 有些邮箱服务器会返回错误码或查看错误类型的链接, 根据给出的错误
// 类型到对应邮件服务器的帮助网站上查看具体失败原因。
//
// PS_02: 连接失败的原因通常为以下几点, 仔细检查代码:
// (1) 邮箱没有开启 SMTP 服务;
// (2) 邮箱密码错误, 例如某些邮箱开启了独立密码;
// (3) 邮箱服务器要求必须要使用 SSL 安全连接;
// (4) 请求过于频繁或其他原因, 被邮件服务器拒绝服务;
// (5) 如果以上几点都确定无误, 到邮件服务器网站查找帮助。
//
// PS_03: 仔细看log, 认真看log, 看懂log, 错误原因都在log已说明。
//11.连接SMTP服务器
transport.connect(sendEmailAccount, sendEmailPwd);
// 12. 发送邮件, 发到所有的收件地址, message.getAllRecipients() 获取到的是在创建邮件对象时添加的所有收件人, 抄送人, 密送人
transport.sendMessage(message,message.getAllRecipients());
// 13. 关闭连接
transport.close();
}
}