Listener && Filter

一、Listener监听器
1、javaweb开发中的监听器,是用于监听web常见对象
2、HttpServletRequest HttpSession ServletContext
3、监听它们的创建与销毁,属性变化,session绑定javaBean
4、监听机制
     事件 就是一个事情
     事件源 产生这个事情的源头
     监听器 用于监听指定的事件的对象
     注册监听 要想让监听器可以监听到事件产生,必须对其进行注册
5、javaweb开发中常见监听器
  • 监听域对象的创建与销毁
    • 监听ServletContext创建与销毁 ServletContextListener
    • 监听HttpSession创建与销毁 HttpSessionListener
    • 监听HttpServletRequest创建与销毁 ServletRequestListener
  • 监听域对象的属性变化
    • 监听ServletContext属性变化 ServletContextAttributeListener
    • 监听HttpSession属性变化 HttpSessionAttributeListener
    • 监听HttpServletRequest属性变化 ServletRequestAttributeListener
  • 监听Session绑定javaBean
    • 它是用于监听javaBean对象是否绑定到了session域 HttpSessionBindingListener
    • 它是用于监听javaBean对象的活化与钝化 HttpSessionActivationListener (服务器停止后Session数据内否恢复)
6、监听器的快速入门
  • 关于创建一个监听器的步骤
    • 创建一个类,实现指定的监听器接口
    • 重写接口中的方法
    • 在web.xml文件中对监听器进行注册
7、关于域对象创建与销毁的演示
     1、ServletContext对象的创建与销毁
     这个对象是在服务器启动时创建的,在服务器关闭时销毁的
<listener>
            <listener-class>com.itheima.listener.MyServletContextListener</listener-class>
      </listener>
public class MyServletContextListener implements ServletContextListener{
       public void contextInitialized(ServletContextEvent sce ) {
            System. out .println( "ServletContext对象创建了" );
      }
       public void contextDestroyed(ServletContextEvent sce ) {
            System. out .println( "ServletContext对象销毁了" );
            
      }
}
     2、HttpSession对象的创建与销毁
     HttpSession session = request.getSession();
     Session 销毁:
          1、默认超时 30分钟
          2、关闭服务器
          3、invalidate()方法
          4、setMaxInactiveInterval(int interval) 可以设置超时时间
     问题:直接访问一个jsp页面时,是否会创建session?
     会创建,因为我们默认情况下时可以在jsp页面中直接使用session内置对象的
<listener>
            <listener-class>com.itheima.listener.MyHttpSessionListener</listener-class>
      </listener>
public class MyHttpSessionListener implements HttpSessionListener {
       public void sessionCreated(HttpSessionEvent se ) {
            System. out .println( "HttpSession对象创建了" );
      }
       public void sessionDestroyed(HttpSessionEvent se ) {
            System. out .println( "HttpSession对象销毁了" );
      }
}
     3、HttpServletRequest创建与销毁
     Request对象是发送请求服务器就会创建它,当响应产生时,request对象就会销毁
<listener>
            <listener-class>com.itheima.listener.MyServletRequestListener</listener-class>
      </listener>
public class MyServletRequestListener implements ServletRequestListener {
       public void requestDestroyed(ServletRequestEvent sre ) {
            System. out .println( "ServletRequest销毁了" );
      }
       public void requestInitialized(ServletRequestEvent sre ) {
            System. out .println( "ServletRequest创建 了" );
      }
}
8、演示Request域对象中属性变化
     在java的监听机制中,它的监听器中的方法都是有参数的,参数就是事件对象,而我们可以通过事件对象直接获取事件源。
public class MyServletRequestListener implements
            ServletRequestAttributeListener {
       public void attributeAdded(ServletRequestAttributeEvent srae ) {
            System. out .println( "ServletRequest添加属性了" );
      }
       public void attributeRemoved(ServletRequestAttributeEvent srae ) {
            System. out .println( "ServletRequest移除属性了" );
      }
       public void attributeReplaced(ServletRequestAttributeEvent srae ) { //参数代表事件源对象
            System. out .println( "ServletRequest替换属性了" );
            System.out.println(srae.getName()+"\t"+srae.getValue());
            
      }
}
9、演示session绑定javaBean
     1、javaBean对象自动感知被绑定到session中
     HttpSessionBindingListener 这个接口是由javaBean实现的,并且不需要在web.xml文件中注册
public class User implements Serializable, HttpSessionBindingListener{
       private String name ;
       private int age ;
public void valueBound(HttpSessionBindingEvent event ) {
            System. out .println( "user对象被绑定了" );
      }
       public void valueUnbound(HttpSessionBindingEvent event ) {
            System. out .println( "user对象解除被绑定了" );
      }
      
      
}
     2、javabean对象可以活化或钝化到session中
     HttpSessionActivationListener 如果javaBean实现了这个接口,那么当我们正常关闭服务器时,session中javaBean对象就回被钝化到我们指定的文件中。当下一次再启动服务器,因为我们已经将对象写入到文件中,这是就会自动将javaBean对象活化到session中
  <%
       //session.invalidate();//使session销毁
       /*request.setAttribute("name", "tom");
      request.setAttribute("name", "jerry");
      request.removeAttribute("name"); */
      
      session.setAttribute( "u" , new User());
      session.removeAttribute( "u" );
 %>

     我们还需要个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>
10、定时销毁session
     1、怎样可以将每一个创建的session全都保存起来?
     我们可以做一个HttpSessionListener,当session对象创建时,就将这个session对象装入到一个集合中
     将集合List<HttpSession>保存到ServletContext域中
     2、怎么样可以判断session过期了?
     在HttpSession中有一个方法public long getLastAccessedTime()
     它可以得到session对象最后使用的时间
     可以使用invalidate方法销毁
     我们上面的操作需要使用任务调度功能
     在java中有一个Timer定时器类
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); //2000 执行任务前的延迟时间  1000 执行各后续任务之间的时间间隔
            
      }
}
    程序在使用时,需要考虑并发问题,因为我们在web中,它一定是一个多线程的,那么我们的程序对集合进行了添加,还有移除操作。
public class MyServletContextListener implements ServletContextListener {
       public void contextInitialized(ServletContextEvent sce ) {
             //通过事件源对象得到事件源(ServletContext)
            ServletContext application = sce .getServletContext();
             //创建一个集合用于存储所有session对象
             final List<HttpSession> list = Collections.synchronizedList( new ArrayList<HttpSession>());
            
             //把集合放到application域中
             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){ //如果时间大于5秒,把session销毁
                                    System. out .println( "session移除了" + session .getId());
                                     session .invalidate(); //把session销毁
                                     //list.remove(session);//从集合中移除
                                     iterator.remove();
                              }
                              
                        }
                         /*for (HttpSession session : list) {
                              long l = System.currentTimeMillis()-session.getLastAccessedTime();
                              if(l>5000){//如果时间大于5秒,把session销毁
                                    session.invalidate();//把session销毁
                                    list.remove(session);//从集合中移除
                              }
                        }*/
                  }
            }, 2000, 5000); //延迟2秒后执行,每间隔5秒执行一次
      }
       public void contextDestroyed(ServletContextEvent sce ) {
      }
}
public class MySessionListener implements HttpSessionListener {
       public void sessionCreated(HttpSessionEvent se ) {
            HttpSession session = se .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 se ) {
             // TODO Auto-generated method stub
      }
}
二、Filter过滤器(重要)
1、javaweb中的过滤器可以拦截所有访问web资源的请求或响应操作
2、Filter快速入门
  • 步骤
    • 创建一个类实现Filter接口
    • 重写接口中方法,doFilter方法是真正过滤的
    • 在web.xml文件中配置
    • 注意:在Filter的doFilter方法内如果没有执行chain.doFilter(request,response)那么资源是不会被访问到的
public class MyFilter implements Filter{
       public MyFilter(){
            System. out .println( "MyFilter实例化了" );
      }
       public void init(FilterConfig filterConfig ) throws ServletException {
            System. out .println( "MyFilter初始化了" );
            
      }
       public void doFilter(ServletRequest request , ServletResponse response ,
                  FilterChain chain ) throws IOException, ServletException {
            System. out .println( "MyFilter1拦截开始了" );
            
             //放行
             chain .doFilter( request , response );
            
            System.out.println("拦截结束了");
      }
       public void destroy() {
            System. out .println( "MyFilter销毁了" );
      }
}
<filter>
      <filter-name>MyFilter</filter-name>
      <filter-class>com.itheima.filter.MyFilter</filter-class>
<filter>
<filter-mapping>
      <filter-name>MyFilter</filter-name>
      <url-pattern>/*</url-pattern>
</filter-mapping>
  • FilterChain
    • FilterChain是servlet容器为开发人员提供的对象,它提供了对某一资源的已过滤请求调用链的视图。过滤器使用FilterChain调用链中的下一个过滤器,如果调用的过滤器是链中的最后一个过滤器,则调用链末尾的资源。
    • 问题:怎么样可以形成一个Filter链?只要多个Filter对同一资源进行拦截就可以形成Filter链
    • 问题:怎样确定Filter的执行顺序?由<filter-mapping>来确定
  • Filter生命周期
    • Servlet生命周期:实例化、初始化、服务、销毁
    • Filter生命周期:
      • 当服务器启动,会创建Filter对象,并调用init方法,只调用一次
      • 当访问资源时,路径与Filter的拦截路径匹配,会执行Filter中的doFilter方法,这个方法是真正拦截操作的方法
      • 当服务器关闭时,会调用Filter的destroy方法来进行销毁操作
  • FilterConfig
    • 在Filter的init方法上有一个参数,类型就是FilterConfig
    • FilterConfig它是Filter的配置对象,它可以完成下列功能
      • 获取Filter名称
      • 获取Filter初始化参数
      • 获取ServletContext对象
      • String getFilterName()
      • String getInitParameter(String name)
      • Enumeration getInitParameterNames()
      • ServletContext getServletContext()
    • 怎样在Filter中获取一个FilterConfig对象
    • public class MyFilterConfigTest implements Filter{
             private FilterConfig filterConfig ;
             public void init(FilterConfig filterConfig ) throws ServletException {
                   this . filterConfig = filterConfig ;
            }
             public void doFilter(ServletRequest request , ServletResponse response ,
                        FilterChain chain ) throws IOException, ServletException {
                   //通过FilterConfig对象获取到配置文件中的初始化信息
                  String encoding = filterConfig .getInitParameter( "encoding" );
                  System. out .println( encoding );
                   request .setCharacterEncoding( encoding );
                   //放行
                   chain .doFilter( request , response );
            }
             public void destroy() {
                   // TODO Auto-generated method stub
                  
            }
      }
    • <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>
  • 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()方法访问时,那么该过滤器将被调用,除此以外,过滤器不会被调用
三、自动登录
1、创建库与表
     create database day17
     use day17
     create table user{
          id int primary key auto_increment
          username varchar(100)
          password varchar(100)
     }
     insert into user values(null,"tom","123");
2、自动登录功能实现
     1、当用户登录成功后,判断是否勾选了自动登录,如果勾选了,就将用户名与密码持久化存储到cookie中
     2、做一个Filter,对需要自动登录的资源进行拦截
3、问题
     1、如果用户想要登录操作,还需要自动登录吗?
     2、如果用户已经登陆了,还需要自动登录吗?
4、AutoFilter.java
public class AutoLoginFilter implements Filter {
       public void init(FilterConfig filterConfig ) throws ServletException {
             // TODO Auto-generated method stub
      }
       public void doFilter(ServletRequest request , ServletResponse response ,
                  FilterChain chain ) throws IOException, ServletException {
             // 1、转换两个对象HttpServletRequest,HttpServletResponse
            HttpServletRequest req = (HttpServletRequest) request ;
            HttpServletResponse resp = (HttpServletResponse) response ;
            String uri = req .getRequestURI(); // /day17_02_autologin/login.jsp
            String path = req .getContextPath(); // /day17_02_autologin
             path = uri .substring( path .length()); // /login.jsp
             //如果请求的资源不是login.jsp,也不是/servlet/loginServlet,才往下执行
             if (!( "/login.jsp" .equals( path ) || "/servlet/loginServlet" .equals( path ))) {
                  User user = (User) req .getSession().getAttribute( "user" );
                   //如果session得到了user对象,说明已经登录过或自动登录过。
                   //那么请求下一个资源时就不用执行自动登录了。
                   //用户没有登录过,我们才执行自动登录
                   if ( user == null ) {
                        System. out .println( "aaaaaaaaaaaaaaa" );
                         // 2、处理业务
                         // 得到cookies数组
                        Cookie[] cookies = req .getCookies();
                        String username = "" ;
                        String password = "" ;
                         // 从数组中找到想要的user对象的信息
                         for ( int i = 0; cookies != null && i < cookies . length ; i ++) {
                               if ( "user" .equals( cookies [ i ].getName())) {
                                    String value = cookies [ i ].getValue(); // tom&123
                                    String[] values = value .split( "&" );
                                     username = values [0];
                                     password = values [1];
                              }
                        }
                         // 登录操作
                        UserService us = new UserService();
                        User u = us .findUser( username , password );
                         // 如果登录成功,把用户信息存到session中
                         if ( u != null ) {
                               req .getSession().setAttribute( "user" , u );
                        }
                  }
            }
             // 3、放行
             chain .doFilter( request , response );
      }
       public void destroy() {
             // TODO Auto-generated method stub
      }
}
5、AutoLoginServlet.java
public class LoginServlet extends HttpServlet {
       public void doGet(HttpServletRequest request , HttpServletResponse response )
                   throws ServletException, IOException {
            
            String username = request .getParameter( "username" );
            String password = request .getParameter( "password" );
             //MD5加密
             password = MD5Utils.md5( password );
            UserService us = new UserService();
            User user = us .findUser( username , password );
            
             if ( user != null ){
                  String autologin = request .getParameter( "autologin" );
                  Cookie cookie = new Cookie( "user" , user .getUsername()+ "&" + user .getPassword());
                   cookie .setPath( "/" );
                   if ( autologin != null ){ //要把用户信息保存到cookie中
                         cookie .setMaxAge(60*60*24*7);
                  } else { //要清除cookie对象的数据
                         cookie .setMaxAge(0);
                  }
                   response .addCookie( cookie ); //把cookie对象保存到客户端
                  
                   request .getSession().setAttribute( "user" , user );
                   request .getRequestDispatcher( "/home.jsp" ).forward( request , response );
            } else {
                   request .setAttribute( "msg" , "用户名或密码错误,请重新登录!" );
                   request .getRequestDispatcher( "/login.jsp" ).forward( request , response );
            }
            
      }
       public void doPost(HttpServletRequest request , HttpServletResponse response )
                   throws ServletException, IOException {
            doGet( request , response );
      }
}
四、MD5加密
1、在mysql中可以对数据进行md5加密
   md5(字段)
   update user set password = md5(password);
2、在java中也提供了md5加密
     
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 ;
      }
}
五、全局的编码过滤器
1、分析:我们之前做的操作,只能对post请求是满足的
    req.setCharacterEncoding("utf-8");//只对post方式满足
2、怎么可以做成一个通用的,可以处理post,get所有的请求的?
   在java中怎样可以对一个方法进行功能增强?
     1、继承
     2、装饰设计模式
          1、创建一个类让它与被装饰类实现同一个接口或继承同一个父类
          2、在装饰类中持有一个被装饰类的引用
          3、重写要增强的方法
     问题:我们获取请求参数有以下方法:
          1、getParameter
          2、getParameterValues
          3、getParameterMap
     这三个方法都可以获取请求参数
     分析后,我们知道getParameter与getParameterValue方法可以依赖于getParamterMap方法来实现
3、
// 实现与被包装对象相同的接口
// 定义一个与被包装类相对象的引用
// 定义一个构造方法,把被包装对象传过来
// 对于不需要改写方法,直接调用
// 对于无需要改写方法,写自己的方法体
class MyRequest extends HttpServletRequestWrapper {
      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;
       @Override
       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 ;
      }
}
4、过滤器
public class MyFilter implements Filter {
       public void init(FilterConfig filterConfig ) throws ServletException {
      }
       public void doFilter(ServletRequest request , ServletResponse response ,
                  FilterChain chain ) throws IOException, ServletException {
            HttpServletRequest req = (HttpServletRequest) request ;
             // 解决post方式
             // req.setCharacterEncoding("UTF-8");
             req = new MyRequest( req );
             chain .doFilter( req , response );
      }
       public void destroy() {
      }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值