Filter & Listener

一 监听器的概述

监听器就是一个实现了特定接口的Java类,用于监听另一个Java类的方法调用或属性的改变。当被监听对象发生上述事件后,监听器某个方法将会立即被执行。

即用来监听其他对象的变化,主要应用在图形化界面开发上。

事件源      专门产生事件的组件

事件       由事件源所产生的动作或者事情

监听器      专门处理事件源所产生的事件

注册/绑定监听器  让监听器时刻监听事件源是否有指定事件产生,如果产生指定事件,则调用监听器处理

在Servlet中定义了多种类型的监听器,用于监听ServletContext、HttpSession和ServletRequest这三个域对象。

分类

一类:监听三个域对象的创建和销毁(三个)

二类:监听三个域对象的属性变更(属性添加、移除、替换)(三个)

扫描二维码关注公众号,回复: 4725592 查看本文章

三类:监听HttpSession中JavaBean的状态改变(钝化、活化、绑定、解除绑定)(两个)

二 简单监听器使用演示

public class MyFrame extends JFrame {

    public static void main(String args[]){
        //1 创建事件源
        MyFrame myFrame = new MyFrame();
        myFrame.setBounds(300, 200, 500, 200);
        myFrame.setVisible(true);
        //3 绑定监听器
        myFrame.addWindowListener(new MyWindowListener());
        
    }
}

//2 创建监听器
class MyWindowListener implements WindowListener{

    @Override
    public void windowOpened(WindowEvent e) {
        System.out.println("窗口打开...");
    }

    @Override
    public void windowClosing(WindowEvent e) {
        System.out.println("窗口关闭...");
        //关闭虚拟机
        System.exit(0);
    }

    @Override
    public void windowClosed(WindowEvent e) {
        
    }

    @Override
    public void windowIconified(WindowEvent e) {
        
    }

    @Override
    public void windowDeiconified(WindowEvent e) {
        
    }

    @Override
    public void windowActivated(WindowEvent e) {
        
    }

    @Override
    public void windowDeactivated(WindowEvent e) {
        
    }

}

 三 ServletContextListener监听器的使用

1  在web项目中编写事件源

public class MyServletContextListener implements ServletContextListener{

    @Override
    public void contextDestroyed(ServletContextEvent arg0) {
        System.out.println("ServletContext销毁了");
    }

    @Override
    public void contextInitialized(ServletContextEvent arg0) {
        System.out.println("ServletContext创建了");
    }

}

 2 在web.xml中配置监听器

  <listener>
      <listener-class>com.my.listener.MyServletContextListener</listener-class>
  </listener>

ServletContextListener企业用途

1 加载框架的配置文件:Spring框架提供了一个核心监听器ContextLoaderListener。

2 定时任务调度

四 HttpSessionListener监听器的使用

HttpSession创建和销毁

创建:服务器端第一次调用getSession()方法时候

销毁:

非正常关闭服务器(正常关闭服务器session会被序列化)

Session过期(默认过期时间30分钟)

手动调用session.invalidate()方法

访问HTML是否创建Session :不会

访问JSP是否创建Session    :会

访问Servlet是否创建Session:不会(默认没有调用getSession方法)

public class MyHttpSessionListener implements HttpSessionListener{

    @Override
    public void sessionCreated(HttpSessionEvent arg0) {
        System.out.println("HttpSession被创建了...");
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent arg0) {
        System.out.println("HttpSession被销毁了...");
    }

}
   <listener>
      <listener-class>com.my.listener.MyHttpSessionListener</listener-class>
  </listener>

 五 ServletRequestListener监听器的使用

ServletRequest对象的创建和销毁
  创建:客户端向服务器发送一次请求,服务器就会创建request对象
  销毁:服务器对这次请求作出了响应后,request对象就销毁了

访问HTML页面是否创建请求对象 :会

访问JSP页面是否创建请求对象    :会

访问Servlet是否创建请求对象      :会

public class MyServletRequestListener implements ServletRequestListener{

    @Override
    public void requestDestroyed(ServletRequestEvent arg0) {
        System.out.println("ServletRequest创建了...");
        
    }

    @Override
    public void requestInitialized(ServletRequestEvent arg0) {
        System.out.println("ServletRequest销毁了...");
        
    }
}

 监听器的web.xml配置:略,参考上面

 六 监听器的应用案例:统计当前在线人数

创建事件源,web.xml中配置监听器,制作jsp页面,开启服务器,访问jsp页面

public class MyServletContextListener implements ServletContextListener{

    @Override
    public void contextDestroyed(ServletContextEvent context) {
        System.out.println("ServletContext销毁了");
    }

    @Override
    public void contextInitialized(ServletContextEvent context) {
        System.out.println("ServletContext创建了");
        //服务器启动时初始化一个count存入ServletContext
        context.getServletContext().setAttribute("count", 0);
    }

}
public class MyHttpSessionListener implements HttpSessionListener{

    @Override
    public void sessionCreated(HttpSessionEvent newSession) {
        //上线人数+1
        HttpSession httpSession = newSession.getSession();
        System.out.println(httpSession.getId() + "上线了...");
        Integer count = (Integer) httpSession.getServletContext().getAttribute("count");
        count++;
        httpSession.getServletContext().setAttribute("count", count);
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent newSession) {
        //下1线人数-1
        HttpSession httpSession = newSession.getSession();
        System.out.println(httpSession.getId() + "下线了...");
        Integer count = (Integer) httpSession.getServletContext().getAttribute("count");
        count--;
        httpSession.getServletContext().setAttribute("count", count);
    }

}
<listener>
      <listener-class>com.my.listener.MyServletContextListener</listener-class>
  </listener>
   <listener>
      <listener-class>com.my.listener.MyHttpSessionListener</listener-class>
  </listener>
  
  <session-config>
      <session-timeout>1</session-timeout>
  </session-config>
session-timeout单位:分钟
<body>
在线人数:${count}
</body>

 七 监听三个域对象属性变更的监听器

(Attribute:属性)

ServletContextAttributeListener     监听ServletContext对象中的属性变更(属性添加,替换,移除)

HttpSessionAttributeListener    监听HttpSession对象中的属性变更(属性添加,替换,移除)

ServletRequestAttributeListener    监听ServletRequest对象中的属性变更(属性添加,替换,移除)

监听器的编写:

public class MyHttpSessionAttributeLitener implements HttpSessionAttributeListener{

    @Override
    public void attributeAdded(HttpSessionBindingEvent arg0) {
        System.out.println("向session中添加了属性...");
    }

    @Override
    public void attributeRemoved(HttpSessionBindingEvent arg0) {
        System.out.println("从session中移除了属性...");
    }

    @Override
    public void attributeReplaced(HttpSessionBindingEvent arg0) {
        System.out.println("从session中替换了属性...");
    }

}

web.xml中监听器的配置:略

jsp测试页面:

<body>

<%
session.setAttribute("name", "user1");//添加属性
session.setAttribute("name", "user2");//替换属性
session.removeAttribute("name");//移除属性
%>

</body>

 八 监听HttpSession中Java类状态改变的监听器

保存在Session域中的Java类可以有多种状态:

1 绑定到session中

2 从session中解除绑定

3 随session对象持久化到一个存储设备中(钝化)

4 随session对象从一个存储设备中恢复(活化)

Servlet对方中定义了两个特殊的监听的接口来帮助Java类了解自己在Session域中的状态:

HttpSessionBindingListener接口    监听Java类在HttpSession中绑定和解除绑定的状态

HttpSessionActivationListener接口     监听HttpSession中Java类的钝化和活化

  实现这两个接口的类不需要在web.xml中进行配置

用途:当访问页面的用户过多,创建的session对象过多,占用内存资源,此时可以设置session活跃时间,超过时间无活动的session可以持久化到硬盘中,节省内存资源

使用演示:

1 创建bean对象,提供set/get方法,实现HttpSessionBindingListener/HttpSessionActivationListener接口

2 在jsp中<% new一个bean对象,setName,再将bean对象set到session中%>,访问页面,即完成了bean对象的绑定

3 让bean对象实现序列化接口Serializable,此时访问jsp页面后,关闭服务器,session对象即被序列化到tomcat本地文件夹下,再次启动服务器,即被反序列化到session中

配置session的序列化和反序列化:context.xml

配置位置:

tomcat/conf/context.xml           所有tomcat下虚拟主机和虚拟目录下的工程都会序列化session

tomcat/conf/Catalina/localhost/context.xml    localhost虚拟主机下的所有项目会序列化session

工程/META-INF/context.xml         当前工程才会序列化session

<Context>
<Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1">
    <Store className="org.apache.catalina.session.FileStore" directory="路径"/>
</Manager>
</Context>
maxIdleSwap:Session不活动的最长时间,超过该时间,Session Manager 将会把该Session对象转移到Session Store中,该Session将不在内存中。1 代表1分钟

 九 Filter概述

Filter即过滤器,过滤客户端向服务器发送的请求,是Servlet技术中最实用的技术,通过Filter技术,对web服务器所管理的资源(JSP,Servlet,静态图片或静态html文件)进行拦截,从而实现一些特殊的功能。

使用:

1 编写一个类实现Filter接口

2 在web.xml中对过滤器进行配置

public class FilterDemo1 implements Filter{

    @Override
    public void destroy() {
        
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        System.out.println("过滤器执行了...");
        //放行
        chain.doFilter(request, response);
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {
        
    }

}
<filter>
      <filter-name>FilterDemo1</filter-name>
      <filter-class>com.my.filter.FilterDemo1</filter-class>
  </filter>
  <filter-mapping>
      <filter-name>FilterDemo1</filter-name>
      <url-pattern>/*</url-pattern>
  </filter-mapping>

/* 代表全部拦截

十 FilterChain对象概述

FilterChain过滤器链:在一个web应用中,可以编写多个Filter,这些Filter组合起来称为是一个过滤器链

Web服务器根据Filter在web.xml文件中的注册顺序(mapping的配置顺序)依次调用过滤器

十一 Filter的生命周期

Filter的创建和销毁由web服务器负责。Web应用程序启动的时候,web服务器创建Filter的实例对象,并调用其init方法进行初始化(filter对象只会创建一次,init方法也只会执行一次)。

每次filter进行拦截的时候,都会执行doFilter的方法。

当服务器关闭,或应用从服务器中移除时,服务器会销毁Filter对象。

十二 FilterConfig对象概述

在Filter的init方法中有一个参数FilterConfig,FilterConfig对象作用是获取Filter的相关配置信息:

    1.获取初始化参数

            String getInitparameter(String name);

            Enumeration EnumerngetInitParameterNames();

    2.获取Filter的名称:    getFilterName();

    3.获取ServletContext对象:getServletContext();

十三 过滤器的相关配置

<url-pattern>的配置

完全路径匹配 :以/开始 比如/aaa /aaa/bbb

目录匹配 :  以/开始 以*结束 比如/* /aaa/* /aaa/bbb/*

扩展名匹配 : 以*开始 比如*.jsp *.do *.action

<servlet-name>的配置

  根据Servlet的配置名称拦截Servlet。

<dispatcher>的配置

默认的情况下过滤器会拦截请求。如果进行转发(需要拦截这次转发)。

dispatcher的取值

REQUEST :默认值。过滤器默认拦截的就是请求。

FORWARD:转发。

INCLUDE :页面包含的时候进行拦截

ERROR :页面出现全局错误页面跳转的时候进行拦截

<filter-mapping>   
    <filter-name>FilterDemo1</filter-name>   
    <url-pattern>/*</url-pattern>   
</filter-mapping>

此时过滤器将会拦截全部页面访问请求。因为这里没有制定任何的< dispatcher >元素,默认值是REQUEST。

<filter-mapping>   
    <filter-name>FilterDemo1</filter-name>   
    <filter-class>com.my.filter.FilterDemo1</filter-class>
    <dispatcher>INCLUDE</dispatcher>   
</filter-mapping>

此时只要是通过<jsp:include page="xxx.jsp" />,嵌入进来的页面,每嵌入的一个页面,都会走一次指定的过滤器

<filter-mapping>   
    <filter-name>FilterDemo1</filter-name>   
    <filter-class>com.my.filter.FilterDemo1</filter-class>
    <dispatcher>FOWARD</dispatcher>   
</filter-mapping>

此时只有从别的页面转发到当前页面,才会走指定的过滤器

 十四 过滤器应用一:权限验证过滤器

需求:登录成功后,重定向到成功页面。未登录直接访问台页面则进行拦截,并跳转到登录页面

<form action="${pageContext.request.contextPath}/userServlet" method="post">
    <table border="1" width="500">
        <tr>
            <td>用户名</td>
            <td><input type="text" name="username" value="${ cookie.remember.value }"/></td>
        </tr>
        <tr>
            <td>密码</td>
            <td><input type="password" name="password"/></td>
        </tr>
        <tr>
            <td colspan="2"><input type="submit" value="登录"/></td>
        </tr>
    </table>
</form>
public class UserModel {
    public User login(User user) throws SQLException {
        // 连接数据库:通过传入的用户名和密码去数据库中进行查询
        QueryRunner queryRunner = new QueryRunner(JDBCUtils.getDataSource());
        User existUser = queryRunner.query("select * from user where username = ? and password = ?",
                new BeanHandler<User>(User.class), user.getUsername(), user.getPassword());
        return existUser;
    }

}
public class UserServlet extends HttpServlet{
    protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException,IOException{
        try{
            //接收数据
            String userName = request.getParameter("userName");
            String password = request.getParameter("password");
            //封装数据
            User user = new User();
            user.setUserName(userName);
            user.setPassword(password);
            //处理数据
            UserModel userModel = new UserModel();
            User existUser = userModel.login(user);
            //根据处理结果跳转
            if(existUser == null){
                request.setAttribute("msg", "用户名或密码错误!");
                request.getRequestDispatcher("/login.jsp").forward(request, response);
            }else{
                
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
    }
}
public class LoginFilter implements Filter{

    @Override
    public void destroy() {
        
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        //判断用户是否登录
        HttpServletRequest req = (HttpServletRequest) request;
        User existUser = req.getSession().getAttribute("existUser");
        if(existUser == null){
            req.setAttribute("msg", "你还没有登录,没有权限!");
            req.getRequestDispatcher("/login.jsp").forward(req, response);
        }else{
            chain.doFilter(req, response);
        }
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {
        
    }

}

十五 过滤器应用二:通用字符集编码过滤器

网站向后台提交中文数据(GET/POST),需要调用request.getParameter();方法接收数据,此时无论是get还是post接收的数据都存在乱码。

所以需要增强request的getParameter()方法,增强的过程要写在过滤器中。

如何增强一个类中的方法?

继承:必须要能够控制这个类的构造。

装饰者:被增强的类和增强的类需要实现相同的接口,在增强的类中获得被增强的类的引用。

    缺点:接口中的方法过多,重写很多其他的方法。

动态代理:类需要实现接口

过滤器代码:

public class GenericEncodingFilter implements Filter{

    @Override
    public void destroy() {
        
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        //在过滤器中增强request对象,并将增强后的request对象传递给Servlet
        HttpServletRequest req = (HttpServletRequest) request;
        //增强req
        MyHttpServletRequest myReq = new MyHttpServletRequest(req);
        chain.doFilter(myReq, response);
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {
        
    }

}

 增强类代码:

public class MyHttpServletRequest extends HttpServletRequestWrapper{

    private HttpServletRequest request;
    public MyHttpServletRequest(HttpServletRequest request) {
        super(request);
        this.request = request;
    }
    
    //增强request.getParameter();方法
    public String getParameter(String name){
        //获得请求方式
        String method = request.getMethod();
        //根据psot/get请求方式进行相应的乱码处理
        if("GET".equalsIgnoreCase(method)){
            //get的请求方式
            String value = super.getParameter(name);
            try {
                value = new String(value.getBytes("ISO-8859-1"),"UTF-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            return value;
        }else if("PSOT".equalsIgnoreCase(method)){
            //post的请求方式
            try {
                request.setCharacterEncoding("UTF-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            
        }
        return super.getParameter(name);
    }

}

GenericEncodingFilter.java

/**
 * 解决get和post请求 全部乱码
 * 
 */
public class GenericEncodingFilter implements Filter {

    @Override
    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        // 转型为与协议相关对象
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        // 对request包装增强
        HttpServletRequest myrequest = new MyRequest(httpServletRequest);
        chain.doFilter(myrequest, response);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

}

// 自定义request对象
class MyRequest extends HttpServletRequestWrapper {

    private HttpServletRequest request;

    private boolean hasEncode;

    public MyRequest(HttpServletRequest request) {
        super(request);// super必须写
        this.request = request;
    }

    // 对需要增强方法 进行覆盖
    @Override
    public Map getParameterMap() {
        // 先获得请求方式
        String method = request.getMethod();
        if (method.equalsIgnoreCase("post")) {
            // post请求
            try {
                // 处理post乱码
                request.setCharacterEncoding("utf-8");
                return request.getParameterMap();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        } else if (method.equalsIgnoreCase("get")) {
            // get请求
            Map<String, String[]> parameterMap = request.getParameterMap();
            if (!hasEncode) { // 确保get手动编码逻辑只运行一次
                for (String parameterName : parameterMap.keySet()) {
                    String[] values = parameterMap.get(parameterName);
                    if (values != null) {
                        for (int i = 0; i < values.length; i++) {
                            try {
                                // 处理get乱码
                                values[i] = new String(values[i]
                                        .getBytes("ISO-8859-1"), "utf-8");
                            } catch (UnsupportedEncodingException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
                hasEncode = true;
            }
            return parameterMap;
        }

        return super.getParameterMap();
    }

    @Override
    public String getParameter(String name) {
        Map<String, String[]> parameterMap = getParameterMap();
        String[] values = parameterMap.get(name);
        if (values == null) {
            return null;
        }
        return values[0]; // 取回参数的第一个值
    }

    @Override
    public String[] getParameterValues(String name) {
        Map<String, String[]> parameterMap = getParameterMap();
        String[] values = parameterMap.get(name);
        return values;
    }

}

猜你喜欢

转载自www.cnblogs.com/createtable/p/10127528.html
今日推荐