java web 之 listen 与 filter
Posted 叶碎夜
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java web 之 listen 与 filter相关的知识,希望对你有一定的参考价值。
一、Listener监听器
Javaweb开发中的监听器,是用于监听web常见对象 HttpServletRequest HttpSession ServletContext
监听它们的创建与销毁、属性变化 以及session绑定javaBean
1、监听机制
- 事件 就是一个事情
- 事件源 产生这个事情的源头
- 监听器 用于监听指定的事件的对象
- 注册监听 要想让监听器可以监听到事件产生,必须对其进行注册。
2、Javaweb开发中常见监听器
2.1、监听域对象的创建与销毁
- 监听ServletContext创建与销毁 ServletContextListener
- 监听HttpSession创建与销毁 HttpSessionListener
- 监听HttpServletRequest创建与销毁 ServletRequestListener
2.2、监听域对象的属性变化
- 监听ServletContext属性变化 ServletContextAttributeListener
- 监听HttpSession属性变化 HttpSessionAttributeListener
- 监听HttpServletRequest属性变化 ServletRequestAttributeListener
2.3、监听session绑定javaBean
它是用于监听javaBean对象是否绑定到了session域 HttpSessionBindingListener
它是用于监听javaBean对象的活化与钝化 HttpSessionActivationListener
3、监听器的快速入门
关于创建一个监听器的步骤
- 创建一个类,实现指定的监听器接口
- 重写接口中的方法
- 在web.xml文件中对监听器进行注册。
- ServletContext对象的创建与销毁
3.1、关于域对象创建与销毁的演示
这个对象是在服务器启动时创建的,在服务器关闭时销毁的。
HttpSession对象的创建与销毁
HttpSession session=request.getSession();
Session销毁 的方法
- 默认超时 30分钟
- 关闭服务器
- invalidate()方法
- setMaxInactiveInterval(int interval) 可以设置超时时间
问题:直接访问一个jsp页面时,是否会创建session?
会创建,因为我们默认情况下是可以在jsp页面中直接使用session内置对象的。
HttpServletRequest创建与销毁
Request对象是发送请求服务器就会创建它,当响应产生时,request对象就会销毁。
3.2、演示了Request域对象中属性变化
在java的监听机制中,它的监听器中的方法都是有参数的,参数就是事件对象,而我们可以通过事件对象直接获取事件源。
3.3、演示session绑定javaBean
1、javaBean对象自动感知被绑定到session中.
HttpSessionBindingListener 这个接口是由javaBean实现的,并且不需要在web.xml文件中注册.
2、javabean对象可以活化或钝化到session中。
HttpSessionActivationListener如果javaBean实现了这个接口,那么当我们正常关闭服务器时,session中的javaBean对象就会被钝化到我们指定的文件中。
当下一次在启动服务器,因为我们已经将对象写入到文件中,这时就会自动将javaBean对象活化到session中。
我们还需要个context.xml文件来配置钝化时存储的文件
在meta-inf目录下创建一个context.xml文件
<Context> <Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1"> <Store className="org.apache.catalina.session.FileStore" directory="it315"/> </Manager> </Context>
案例-定时销毁session
1、怎样可以将每一个创建的session全都保存起来?
我们可以做一个HttpSessionListener,当session对象创建时,就将这个session对象装入到一个集合中.
将集合List<HttpSession>保存到ServletContext域中。
2、怎样可以判断session过期了?
在HttpSession中有一个方法public long getLastAccessedTime()
它可以得到session对象最后使用的时间,可以使用invalidate方法销毁。
ps(我们上面的操作需要使用任务调度功能.)在java中有一个Timer定时器类
package com.timer; import java.util.Date; import java.util.Timer; import java.util.TimerTask; public class TestTimer { public static void main(String[] args) { Timer t = new Timer(); t.schedule(new TimerTask() { @Override public void run() { System.out.println(new Date().toLocaleString()); } }, 2000, 1000); } }
关于三个域对象获取
如果在Servlet中要获取request,在方法上就有,request.getSession() getServletContext();
如果我们有request对象了, request.getSession() request.getSession().getServletCotnext();
public class MySessionListener implements HttpSessionListener { public void sessionCreated(HttpSessionEvent arg0) { HttpSession session = arg0.getSession(); //得到application中的list集合 ServletContext application = session.getServletContext(); //得到session对象,并放入list集合 List<HttpSession> list =(List<HttpSession>) application.getAttribute("sessions"); list.add(session); System.out.println("添加了"+session.getId()); } public void sessionDestroyed(HttpSessionEvent arg0) { // TODO Auto-generated method stub } }
程序在使用时,需要考虑并发问题,因为我们在web中,它一定是一个多线程的,那么我们的程序对集合进行了添加,还有移除操作。具体在MyServletContextListener的方法中如下
public class MyServletContextListener implements ServletContextListener { public void contextDestroyed(ServletContextEvent arg0) { } public void contextInitialized(ServletContextEvent arg0) { //通過事件原對象得到事件源 ServletContext application = arg0.getServletContext(); //创建一个集合 存储所有session对象 final List<HttpSession> list = Collections.synchronizedList(new ArrayList<HttpSession>()); application.setAttribute("sessions", list); //创建一个计时器对象 Timer t = new Timer(); t.schedule(new TimerTask() { @Override public void run() { System.out.println("开始扫描"); for (Iterator iterator = list.iterator(); iterator.hasNext();) { HttpSession session = (HttpSession) iterator.next(); long l = System.currentTimeMillis() - session.getLastAccessedTime(); if(l > 5000){ System.out.println("session移除了"+session.getId()); session.invalidate(); iterator.remove(); } } } }, 2000, 5000); } }
二、Filter
二、Filter过滤器(重要)
Javaweb中的过滤器可以拦截所有访问web资源的请求或响应操作。
1.1、步骤:
- 创建一个类实现Filter接口
- 重写接口中方法 doFilter方法是真正过滤的。
- 在web.xml文件中配置
注意:在Filter的doFilter方法内如果没有执行chain.doFilter(request,response),那么资源是不会被访问到的。
1.2、FilterChain
FilterChain 是 servlet 容器为开发人员提供的对象,它提供了对某一资源的已过滤请求调用链的视图。过滤器使用 FilterChain 调用链中的下一个过滤器,如果调用的过滤器是链中的最后一个过滤器,则调用链末尾的资源。
问题:怎样可以形成一个Filter链?
只要多个Filter对同一个资源进行拦截就可以形成Filter链
问题:怎样确定Filter的执行顺序?
由<filter-mapping>来确定
1.3、Filter生命周期
Servlet生命周期:
实例化 --》 初始化 --》 服务 --》 销毁
- 1 当服务器启动,会创建Filter对象,并调用init方法,只调用一次.
- 2 当访问资源时,路径与Filter的拦截路径匹配,会执行Filter中的doFilter方法,这个方法是真正拦截操作的方法.
- 3 当服务器关闭时,会调用Filter的destroy方法来进行销毁操作.
1.4、FilterConfig
在Filter的init方法上有一个参数,类型就是FilterConfig.
FilterConfig它是Filter的配置对象,它可以完成下列功能
- 获取Filtr名称
- 获取Filter初始化参数
- 获取ServletContext对象。
问题:怎样在Filter中获取一个FIlterConfig对象?
1 package com.itheima.filter; 2 3 import java.io.IOException; 4 5 import javax.servlet.Filter; 6 import javax.servlet.FilterChain; 7 import javax.servlet.FilterConfig; 8 import javax.servlet.ServletException; 9 import javax.servlet.ServletRequest; 10 import javax.servlet.ServletResponse; 11 12 public class MyFilterConfigTest implements Filter{ 13 14 private FilterConfig filterConfig; 15 16 public void init(FilterConfig filterConfig) throws ServletException { 17 this.filterConfig = filterConfig; 18 } 19 20 public void doFilter(ServletRequest request, ServletResponse response, 21 FilterChain chain) throws IOException, ServletException { 22 //通过FilterConfig对象获取到配置文件中的初始化信息 23 String encoding = filterConfig.getInitParameter("encoding"); 24 System.out.println(encoding); 25 request.setCharacterEncoding(encoding); 26 //放行 27 chain.doFilter(request, response); 28 } 29 30 public void destroy() { 31 // TODO Auto-generated method stub 32 } 33 }
如下 web.xml配置
<filter>
<filter-name>MyFilterConfigTest</filter-name>
<filter-class>com.itheima.filter.MyFilterConfigTest</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>MyFilterConfigTest</filter-name>
<servlet-name>ServletDemo2</servlet-name>
</filter-mapping>
1.5、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>
关于其它配置
1.<url-pattern>
完全匹配 以”/demo1”开始,不包含通配符*
目录匹配 以”/”开始 以*结束
扩展名匹配 *.xxx 不能写成/*.xxx
2.<servlet-name>
它是对指定的servlet名称的servlet进行拦截的。
3.<dispatcher>
可以取的值有 REQUEST FORWARD ERROR INCLUDE 根据跳转方式拦截
它的作用是:当以什么方式去访问web资源时,进行拦截操作.
1.REQUEST 当是从浏览器直接访问资源,或是重定向到某个资源时进行拦截方式配置的 它也是默认值
2.FORWARD 它描述的是请求转发的拦截方式配置
3.ERROR 如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。
4.INCLUDE 如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用
三、自动登录 (PS bean的属性名和数据库里的字段名千万要一样哦 不然会emmm 很惨)
- 当用户登陆成功后,判断是否勾选了自动登陆,如果勾选了,就将用户名与密码持久化存储到cookie
- 做一个Filter,对需要自动登陆的资源进行拦截
首写要在登录Servlet中登录成功时把登录的信息保存在cookie里,并设置存活时间
if(null != user){ String autologin = request.getParameter("autologin"); Cookie cookie = new Cookie("user", user.getUsername()+"&"+user.getPassword()); cookie.setPath("/"); if(autologin != null){// on / null cookie.setMaxAge(60*60*24*7); }else{ cookie.setMaxAge(0); } response.addCookie(cookie); request.getSession().setAttribute("user", user); request.getRequestDispatcher("/home.jsp").forward(request, response); }
然后设置好filter类 记住要在web.xml里面配置 如下 为 doFilter代码
public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException { //1、转换对象 HttpServletRequest req = (HttpServletRequest) arg0; String uri = req.getRequestURI(); String path = req.getContextPath(); path = uri.substring(path.length()); if(!("/login.jsp".equals(path)||"/servlet/loginServlet".equals(path))){ User user = (User) req.getSession().getAttribute("user"); if(user == null){ //2、处理业务 Cookie[] cookies = req.getCookies(); String username = ""; String password = ""; for (int i = 0;cookies!=null && i < cookies.length; i++) { if("user".equals(cookies[i].getName())){ String value = cookies[i].getValue(); String[] values = value.split("&"); username = values[0]; password = values[1]; } } UserService us = new UserService(); User u = us.findUser(username, password); if(u != null){ req.getSession().setAttribute("user", u); } } } //3、放行 arg2.doFilter(arg0, arg1); }
ps 当访问别的页面时通过判断session时减少访问数据库的操作
四、MD5加密
在mysql中可以对数据进行md5加密 Md5(字段)
UPDATE USER SET PASSWORD=MD5(PASSWORD);
需要一个MD5Utils工具类
package com.util; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class MD5Utils { /** * 使用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; } }
这样在loginservlet调用一下方法转化一下就ok了。
五、全局的编码过滤器
之前做的操作,只能对post请求是ok
怎样可以做成一个通用的,可以处理post,get所有的请求的?
在java中怎样可以对一个方法进行功能增强?
- 继承
- 装饰设
- 创建一个类让它与被装饰类实现同一个接口或继承同一个父类
- 在装饰类中持有一个被装饰类的引用
- 重写要增强的方法
我们可以用getparameterMap这个方法来实现getparameter和getparametervalues两个方法 即MyFilter 最终版本
package com.filter; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; public class MyFilter implements Filter{ public void destroy() { // TODO Auto-generated method stub } public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) arg0; req = new MyRequest(req); arg2.doFilter(req, arg1); } public void init(FilterConfig arg0) throws ServletException { // TODO Auto-generated method stub } } class MyRequest extends HttpServletRequestWrapper{ private HttpServletRequest request; public MyRequest(HttpServletRequest request) { super(request); this.request = request; } /*@Override public String getParameter(String name) { name = request.getParameter(name); try { return new String(name.getBytes("iso-8859-1"),"UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return null; }*/ @Override public String getParameter(String name) { Map<String, String[]> map = getParameterMap(); return map.get(name)[0]; } @Override 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(); if(flag){ for (Map.Entry<String, String[]> m : map.entrySet()) { String[] values = m.getValue(); for (int i = 0; i < values.length; i++) { try { values[i] = new String(values[i].getBytes("iso-8859-1"),"UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } } flag =false; } return map; } }
以上是关于java web 之 listen 与 filter的主要内容,如果未能解决你的问题,请参考以下文章
Java三大器之监听器(Listener)的工作原理和代码演示
servlet filter listener interceptor 知识点
javaEE中错误提示 Exception starting filter BackServletFilter java.lang.ClassNotFoundException: tmall.filt