Listener监听器
javaweb监听器介绍
1.监听web对象创建与销毁的监听器
ServletContextListener
HttpSessionListener
ServletRequestListener
2.监听web对象属性变化
ServletContextAttributeListener
HttpSessionAttributeListener
ServletRequestAttributeListener
3.监听session绑定javaBean
HttpSessionBindingListener
HttpSessionActivationListener
1. javaweb监听器创建步骤
Ø 创建一个类,实现指定的监听器接口
Ø 重写接口中的方法.
Ø 在web.xml文件中配置监听
监听对象创建销毁
ServletContextListener在系统启动时,调用contextInitialized()方法
HttpSessionListener在访问jsp页面或者servlet创建session时候,调用sessionCreated()
session时间到了,或者invalidate()时候,调用sessionDestroyed()
ServletRequestListener在访问页面的时候,调用requestInitialized();
监听对象属性变化
ServletRequestAttributeListener在添加属性的时候调用(注意第一次添加后会调用替换方法,因为request自动加了一些属性)
监听session绑定和活化.钝化
HttpSessionBindingListener绑定在javaBean上,如果此对象绑定了HttpSessions就会调用绑定方法(不需要xml配置,因为他不虚启动)
案列:定时销毁Session(可以直接设置session存留时间,这个案例是定时调度Timer的教程)
1.创建HttpSessionListener,当session对象创建时,就将这个session对象装入一个集合中
2.将List<HttpSession>保存到ServletContext域中。
3.HttpSession中有一个方法getLastAccessedTime();他可以获得session对象最后使用的时间。
4.使用invalidate方法销毁
创建实现ServletContextListener接口
public class MyServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("ServletContext对象创建了");
ServletContext context = servletContextEvent.getServletContext();
//创建一个集合用于存储所有的session对象 注意:需要加锁,并发的创建session添加时候,会有问题
List<HttpSession> sessions = Collections.synchronizedList(new ArrayList<HttpSession>());
//把集合放到application域中
context.setAttribute("sessions",sessions);
//服务器启动就执行此方法,所以在这里加上计时器对象
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
//Iterator和listIterator都可以删除,区别在于listIterator可以倒序遍历,可以添加元素,而Iterator不能
ListIterator<HttpSession> iterator = sessions.listIterator();
while (iterator.hasNext()){
HttpSession next = iterator.next();
long l = next.getLastAccessedTime();
if(System.currentTimeMillis()-l>5000){//如果时间大于5秒,把session销毁
next.invalidate();//使之无效
iterator.remove();
}
}
}
},5000,5000);//延迟2秒后执行,每间隔5秒执行一次
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("ServletContext对象删除了");
}
}
创建实现HttpSessionListener接口
public class ServletSessionListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
//得到当前创建的session
HttpSession session = httpSessionEvent.getSession();
//得到application对象中的list集合,session中有getServletContext()方法
ServletContext application = session.getServletContext();
List<HttpSession> sessions = (List<HttpSession>) application.getAttribute("sessions");
//把当前创建的session对象添加到集合中
boolean add = sessions.add(session);
System.out.println(session.getId()+" : "+(add==true?"添加成功":"添加失败"));
}
@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
System.out.println("session销毁了");
}
}
并且在xml配置信息
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<listener>
<listener-class>com.mck.entity.MyServletContextListener</listener-class>
</listener>
<listener>
<listener-class>com.mck.entity.ServletSessionListener</listener-class>
</listener>
</web-app>
Filter过滤器(重要)
如果有相同的过滤器拦截了某一个请求,则FilterChain会调用下一个过滤器
调用循序是xml的<filter-mapping>摆放的上下顺序!!(方法回执,先里面后外面)
Filter生命周期
Servlet生命周期:
实例化》》初始化》》服务》》销毁
当服务器启动,会创建filter对象,并调用init方法,只调用一次
当访问资源时,路径与filter的拦截路径匹配时,会执行filter中的doFilter,这个方法是真正拦截操作的方法
当服务器关闭时,会调用filter的destroy方法来进行销毁
FilterConfig类
用于获取filter初试化的参数
Filter实战(通过拦截器解决doGet提交和doPost提交乱码问题)
dopost方式通过拦截器
设置req.setContextType("text/html;charset=utf-8")
chain放行后,所有被拦截的servletl类都被设置了
doget方式通过包装设计模式(包装设计模式可以理解方法套一层皮,核心是改不了的,但是可以加额外的功能,如设置编码)
先了解两种模式
包装设计模式:1.实现被包装类相同的接口,2.被包装类作为参数传递包装类中(通过构造器),3.对需要重写的方法改动,不需要重写的,原样调用即可
适配器设计模式:适配器实现原理同上,区别在于,适配器实现接口所有的方法,需要重写方法直接调用即可。
思路:
1.通过拦截器得到HttpServletRequest
2.创建一个Myrequest类继承HttpServletRequestWrapper类(按照包装设计模式来说,应该实现HttpServletRequest接口,确实是,但是实现接口后发现,你只需要改动一个方法,但是却需要实现100多个其它方法,很麻烦,所以继承HttpServletRequestWrapper,这个类帮你实现了HttpServletRequest接口,并且没有改动方法,你只需要把需要改动的方法重写即可,这就是适配器模式(适配这个字就很方便,哈哈))
3.代码贴上来(包装类MyRequest)
public class MyRequest extends HttpServletRequestWrapper {
HttpServletRequest request;
public MyRequest(HttpServletRequest request){
//HttpServletRequestWrapper适配器模式需要一个对象,原理和包装类一样
//区别在于适配器模式把传递的对象接口的方法全部给实现了,不需要重新实现接口中方法,并且,需要那个方法,就单独调用重写那个方法
super(request);
this.request=request;
}
//重写方法
public String getParameter(String name) {
Map<String, String[]> map = getParameterMap();
return map.get(name)[0];
}
//重写方法
public String[] getParameterValues(String name) {
Map<String, String[]> map = getParameterMap();
return map.get(name);
}
//防止多次执行后又乱码了。
private boolean flag=true;
//源头
public Map<String, String[]> getParameterMap() {
Map<String, String[]> map = request.getParameterMap();//调用传递过来的request参数的getParameterMap()方法
if(flag) {//防止多次执行乱码,多次调用getParameter或者别的方法,就会坏变好变坏
for (Map.Entry<String, String[]> e : map.entrySet()) {
String[] value = e.getValue();//注意这里是引用类型 value指向实际堆内存地址改动可变
for (int i = 0; i < value.length; i++) {
try {
value[i] = new String(value[i].getBytes("iso-8859-1"), "UTF-8");//转码
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
}
}
}
flag=false;
}
return map;
}
}
4.再Filter中把request传递到MyRequest中,传递给其他页面,这时候其他页面就用的是包装类型,而不是原有的request
好了就这么多,跟黑马老师课程来的,觉得好就记录在网上供大家分享