java web 之 listen 与 filter

Posted 叶碎夜

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java web 之 listen 与 filter相关的知识,希望对你有一定的参考价值。

一、Listener监听器

Javaweb开发中的监听器,是用于监听web常见对象 HttpServletRequest HttpSession ServletContext

监听它们的创建与销毁属性变化 以及session绑定javaBean

1监听机制

  • 事件  就是一个事情
  • 事件源  产生这个事情的源头
  • 监听器  用于监听指定的事件的对象
  • 注册监听 要想让监听器可以监听到事件产生,必须对其进行注册。

2Javaweb开发中常见监听器

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监听器的快速入门

关于创建一个监听器的步骤

  1. 创建一个类,实现指定的监听器接口
  2. 重写接口中的方法
  3. web.xml文件中对监听器进行注册。
  4. ServletContext对象的创建与销毁

3.1关于域对象创建与销毁的演示

这个对象是在服务器启动时创建的,在服务器关闭时销毁的。

HttpSession对象的创建与销毁

HttpSession session=request.getSession();

Session销毁 的方法

  1. 默认超时  30分钟
  2. 关闭服务器
  3. invalidate()方法
  4. 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、步骤:

  1. 创建一个类实现Filter接口
  2. 重写接口中方法  doFilter方法是真正过滤的。
  3. 在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的配置对象,它可以完成下列功能

  1. 获取Filtr名称
  2. 获取Filter初始化参数
  3. 获取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 很惨)

  1. 当用户登陆成功后,判断是否勾选了自动登陆,如果勾选了,就将用户名与密码持久化存储到cookie
  2. 做一个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中怎样可以对一个方法进行功能增强?

  1. 继承
  2. 装饰设
    1. 创建一个类让它与被装饰类实现同一个接口或继承同一个父类
    2. 在装饰类中持有一个被装饰类的引用
    3. 重写要增强的方法

我们可以用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)的工作原理和代码演示

JavaWeb开发之Listener&Filter

servlet filter listener interceptor 知识点

Java Web之过滤器

javaEE中错误提示 Exception starting filter BackServletFilter java.lang.ClassNotFoundException: tmall.filt

java web学习: 三大组件之servlet基础