Listener & Filter一.Listener
1. listener介绍
Javaweb中的监听器是用于监听web常见对象HttpServletRequest,HttpSession,ServletContext
作用:
监听web对象的创建与销毁
监听web对象的属性变化
监听session绑定javaBean操作
监听机制的相关概念
事件----一件事情
事件源---产生这件事情的源头
注册监听---将监听器与事件绑定,当事件产生时,监听器可以知道,并进行处理。
监听器---对某件事情进行处理监听的一个对象
2. web监听器介绍
javaweb监听器介绍
监听web对象创建与销毁的监听器
ServletContextListener
HttpSessionListener
ServletRequestListener
监听web对象属性变化
ServletContextAttributeListener
HttpSessionAttributeListener
ServletRequestAttributeListener
监听session绑定javaBean
HttpSessionBindingListener
HttpSessionActivationListener(活化或者钝化)
javaweb监听器创建步骤
a. 创建一个类,实现指定的监听器接口
b. 重写接口中的方法.
c. 在web.xml文件中配置监听
<listener>
<listener-class>全限定名</listener-class>
</listener>
3. 演示监听器的创建与销毁
ServletContext对象的创建与销毁监听
ServletContext对象的创建与销毁分析
ServletContext对象是服务器开启时创建。服务器关闭时销毁。
监听ServletContext对象的创建与销毁
HttpSession对象的创建与销毁监听
HttpSession对象的创建与销毁分析
session对象创建:取决于请求中是否有jsessinid,如果有,可能会获取一个已经存在的session对象。如果没有,会创建一个新的session对象.
session的销毁:
超时(默认超时30分钟或者通过setMaxInactiveInterval(int)手动设置超时时间)
关闭服务器
invalidate()方法
监听HttpSession对象的创建与销毁
HttpServletRequest对象的创建与销毁监听
Ø HttpServletRequest对象的创建与销毁分析
request对象是发送请求时创建,当响应产生时,销毁.
Ø 监听HttpServletRequest对象的创建与销毁
4. 演示监听属性的变化
javaweb中监听属性变化API介绍
ServletContextAttributeListener
ServletRequestAttributeListener
HttpSessionAttributeListener
演示监听session中属性变化
5. 演示监听session绑定javabean对象
HttpSessionBindingListener监听器功能介绍
使javaBean对象在被绑定到会话或从会话中取消对它的绑定时得到通知
HttpSessionActivationListener监听器功能介绍
绑定到会话的对象可以侦听通知它们会话将被钝化和会话将被激活的容器事件
注意事项
这两个监听器比较特殊,它是由javaBean来实现的,并且不需要在web.xml文件中注册监听.
javaBean必须是序列化的.
演示HttpSessionBindingListener功能
javaBean自动感知绑定到session中以及从session中移除.
public void valueBound(HttpSessionBindingEvent event) {
System.out.println("绑定了user对象到了session");
}
public void valueUnbound(HttpSessionBindingEvent event) {
System.out.println("从session中移除了user对象");
}
演示HttpSessionActivationListener功能
钝化:当我们正常关闭服务器时,session对象会被保存到服务器文件中
活化:如果之前进行了钝化操作,当正常启动服务器时会从文件中将session读取出来使用
a. bean1.jsp中向session存储一个user对象
b. bean2.jsp中从session中获取user对象的name属性
c. User类实现接口HttpSessionActivationListener,可以监听到活化与钝化操作
d. 做一个配置文件,来设定当活化与钝化操作时文件的位置.代码如下
在meta-inf目录下创建一个context.xml文件
<Context>
<Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1">
<Store className="org.apache.catalina.session.FileStore" directory="itcast"/>
</Manager>
</Context>
通过以上配置会在tomcat的work目录下新建一个名称为itcast的文件夹.
此时需要使用session中的对象的时候,才会活化session
6. 案例-定时删除过时订单分析
功能描述
若一个订单从下单开始超过30分钟未支付,则删除该订单。
分析
为了这个操作我们需要拿到订单的下单时间和支付状态.然后判断订单是否超过30分钟未支付,若未支付则取消该订单,想实现此功能,还需要使用任务调度功能.要求在项目一启动的时候就可以扫描订单.比如每分钟查找一下,将满足条件的删除掉.
在java中有一个Timer定时器类
步骤分析
创建一个ServletContext创建与销毁监听器,在ServletContext对象创建时,启动定时扫描器.
在定时器内部实现查询订单及删除订单操作..
7. 案例-定时删除过时订单实现
publicvoid contextInitialized(ServletContextEvent sce) {
//一旦服务器启动 该定时器就开始扫描
Timer t=new Timer();
t.schedule(new TimerTask() {
@Override
publicvoid run() {
//完成订单查询和删除操作即可
System.out.println(".........");
}
}, 1000, 2000);//延迟1秒开始执行,每2秒执行一次
}
二.Filter
1. filter介绍
filter介绍及其作用介绍
Filter是sun公司中servlet2.3后增加的一个新功能.
Servlet规范中三个技术 Servlet Listener Filter
在javaEE中定义了一个接口 javax.servlet.Filter来描述过滤器
作用:
通过Filter可以拦截访问web资源的请求与响应操作.
WEB开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。
常用api
2. filter入门案例
创建步骤:
编写filter
a. 创建一个类实现javax.servlet.Filter接口
b. 重写接口方法
编写配置文件
a. 注册filter
b. 绑定路径
Filter在web.xml文件中配置的目的:
配置拦截什么样的资源。
Filter初始化
<filter>
<filter-name>demo1Filter</filter-name>
<filter-class>cn.itcast.web.filter.Demo1Filter</filter-class>
</filter>
<filter-mapping>
<filter-name>demo1Filter</filter-name>
<url-pattern>/demo1</url-pattern>
</filter-mapping>
拦截分析:
注意:
在Filter的doFilter方法内如果没有执行
那么资源是不会被访问到的。
FilterChain功能介绍
FilterChain 是 servlet 容器为开发人员提供的对象,它提供了对某一资源的已过滤请求调用链的视图。过滤器使用 FilterChain 调用链中的下一个过滤器,如果调用的过滤器是链中的最后一个过滤器,则调用链末尾的资源。
3. filter链与生命周期
filter链介绍
多个Filter对同一个资源进行了拦截,那么当我们在开始的Filter中执行 chain.doFilter(request,response)时,是访问下一下Filter,直到最后一个Filter执行时,它后面没有了Filter,才会访问web资源。
如果有多个Filter形成了Filter链,那么它们的执行顺序是怎样确定的?
它们的执行顺序取决于<filter-mapping>在web.xml文件中配置的先后顺序。
filter生命周期
当服务器启动,会创建Filter对象,并调用init方法,只调用一次.
当访问资源时,路径与Filter的拦截路径匹配,会执行Filter中的doFilter方法,这个方法是真正拦截操作的方法.
当服务器关闭时,会调用Filter的destroy方法来进行销毁操作.
4. FilterConfig介绍
Filter功能介绍
在Filter中的init方法上有一个参数叫FilterConfig ,是Filter的配置对象
作用:
获取初始化参数
获取filter的名称
获取全局管理者(SerlvetContext对象)
常用api
5. filter配置详解
Filter基本配置介绍
<filter>
<filter-name>filter名称</filter-name>
<filter-class>filter类全名</filter-class>
</filter>
<filter-mapping>
<filter-name>filter名称</filter-name>
<url-pattern>映射路径</url-pattern>
</filter-mapping>
url-pattern配置
完全匹配
要求必须以"/"开始.
目录匹配
要求必须以"/"开始,以*结束.
扩展名匹配
不能以"/"开始,以*.xxx结束.例如:*.jsp *.do
关于servlet-name配置
针对于servlet拦截的配置 <servlet-name>配置
在Filter中它的url-pattern配置项上有一个标签
<servlet-name>它用于设置(指定)当前Filter拦截哪一个servlet。
是通过servlet的name来确定的。
关于dispatcher配置
可以取的值有 REQUEST FORWARD ERROR INCLUDE
作用:
当以什么方式去访问web资源时,进行拦截操作.
REQUEST 当是从浏览器直接访问资源,或是重定向到某个资源时进行拦截方式配置的 它也是默认值
FORWARD 它描述的是请求转发的拦截方式配置
ERROR 如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。
INCLUDE 如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用
三.Filter案例
1. 案例-自动登录功能分析
之前的登录流程:
login.jsp-->loginServlet-->UserService-->UserDao
现在分析下自动登录的原理
a.当用户登录成功之后,判断一下用户是否勾选了自动登录,若勾选了,将用户名和密码通过cookie持久化到浏览器上.
b.做一个filter,filter的作用为:判断cookie中是否有用户名和密码,若有拿过来调用service完成登录操作
c.filter放行
思考问题:
已经登录成功了是否还需要自动登录.
若访问的登录和注册页面,是否还需要自动登录
2. 案例-自动登录功能实现
publicvoid doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//1.强转
HttpServletRequest req=(HttpServletRequest) request;
HttpServletResponse resp=(HttpServletResponse) response;
//2.逻辑
//首先判断session中是否有user 若没有再继续操作
User user = (User) req.getSession().getAttribute("user");
if(user==null){//session中没有用户 需要继续操作
//获取请求路径 若是login和regist的不需要自动登录
String uri = req.getRequestURI();
String contextPath = req.getContextPath();
String path=uri.substring(contextPath.length());
//System.out.println(path);
if(!("/login.jsp".equals(path)||"/login".equals(path)||"/regist.jsp".equals(path)||"/regist".equals(path))){
//查找是否有自动登录的cookie
Cookie c = CookieUtils.getCookieByName("autoLogin", req.getCookies());
if(c!=null){
//若有 调用service 自动登录
//拿到用户名和密码
String username=c.getValue().split("-")[0];
String password=c.getValue().split("-")[1];
//调用service登录
user = new UserService().login(username, password);
//若登录成功 将user放入session中
if(user!=null){
req.getSession().setAttribute("user", user);
System.out.println("自动登录..........");
}
}
}
}
//3.放行
chain.doFilter(req, resp);
}
注意:
cookie中不能存放中文,若出现中文还需编码解决
3. md5加密
md5加密介绍
Message Digest Algorithm MD5(中文名为消息摘要算法第五版)为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护。
主流编程语言普遍已有MD5实现
mysql中的md5加密实现
通过 md5(值|字段)实现
例如:
update user set password=md5(password);
java中的md5加密实现
/**
* 使用md5的算法进行加密
*/
public static String md5(String plainText) {
byte[] secretBytes = null;
try {
secretBytes = MessageDigest.getInstance("md5").digest(plainText.getBytes());
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("没有md5这个算法!");
}
String md5code = new BigInteger(1, secretBytes).toString(16);
for (int i = 0; i < 32 - md5code.length(); i++) {
md5code = "0" + md5code;
}
return md5code;
}
4. java中对方法进行增强介绍
继承
装饰者模式(包装)
动态代理(基础加强讲)
装饰模式实现步骤:
a.装饰类与被装饰类要继承同一个父类或实现同一个接口。
b.在装饰类中重写方法,进行功能增强
c.在装饰类中持有一个被装饰类的对象
D.不需要加强的方法调用原来的方法
5. 案例-编码过滤器分析
简易版本(只针对post请求)
编写一个filter,在filter中使用一句话
request.setCharacterEncoding(“utf-8”);
通用版本
思路:在Filter中对request进行功能增强,让它处理了乱码问题,再将request传递到servlet中,这样在servlet中获取请求参数就不会乱码。
如何进行功能加强?三种方式(上面刚说过)
在EncodingFilter中已经创建了一个MyReqeust,它是一个HttpServletRequest的装饰类,而我们在chain.doFilter(MyRequest,response);也就是在servlet中使用的request其实是装饰类。
对于我们通过reqeust对象获取请求参数有三种方式:
getParameter(name) getParameterValues(name) getParameterMap()
我们不需要将这三个都进行编码处理,只需要对getParameterMap进行乱码处理,而getParameter及getParameteValues可以依赖于getParametreMap的实现
6. 案例-编码过滤器实现
class MyRequest extends HttpServletRequestWrapper{
private HttpServletRequest request;
privatebooleanflag=true;
public MyRequest(HttpServletRequest request) {
super(request);
this.request=request;
}
@Override
public String getParameter(String name) {
if(name==null || name.trim().length()==0){
returnnull;
}
String[] values = getParameterValues(name);
if(values==null || values.length==0){
returnnull;
}
return values[0];
}
@Override
/**
* hobby=[eat,drink]
*/
public String[] getParameterValues(String name) {
if(name==null || name.trim().length()==0){
returnnull;
}
Map<String, String[]> map = getParameterMap();
if(map==null || map.size()==0){
returnnull;
}
return map.get(name);
}
@Override
/**
* map{ username=[tom],password=[123],hobby=[eat,drink]}
*/
public Map<String,String[]> getParameterMap() {
/**
* 首先判断请求方式
* 若为post request.setchar...(utf-8)
* 若为get 将map中的值遍历编码就可以了
*/
String method = request.getMethod();
if("post".equalsIgnoreCase(method)){
try {
request.setCharacterEncoding("utf-8");
returnrequest.getParameterMap();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}elseif("get".equalsIgnoreCase(method)){
Map<String,String[]> map = request.getParameterMap();
if(flag){
for (String key:map.keySet()) {
String[] arr = map.get(key);
//继续遍历数组
for(int i=0;i<arr.length;i++){
//编码
try {
arr[i]=new String(arr[i].getBytes("iso8859-1"),"utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
flag=false;
}
//需要遍历map 修改value的每一个数据的编码
return map;
}
returnsuper.getParameterMap();
}
}