Java Web过滤器与监听器

过滤器

过滤器是服务器端的组件,用于对来到服务器的请求和服务器返回的响应进行过滤操作。例如通过过滤器判断用户是否登录从而执行不同操作,或者请求到不存在页面时返回报错信息等。过滤器在服务器服务启动时就被加载生成了,之后用户和服务器之间的请求和响应都要经过过滤器

1、创建过滤器

创建一个filter类需要实现Filter接口的三个方法,init()是过滤器的初始化方法,web容器创建过滤器后调用该方法从web.xml文件中读取过滤器参数。doFilter()完成过滤操作,当request请求到达过滤器后执行doFilter()操作,对请求进行处理:转发、重定向或者传给下一个过滤器,执行完相关操作后返回的response将经过Filter返回给用户。destroy()在Web容器销毁实例前调用,释放过滤器所占用的资源。

public class firstFilter implements Filter {
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("创建第一个过滤器");
    }

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("第一个过滤器执行过滤之前");
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println("第一个过滤操作之后");
    }

    public void destroy() {
        System.out.println("销毁第一个过滤器");
    }
}

在web.xml文件中对过滤器进行注册,配置如下:

如下注册一个名为FirstFilter的过滤器,其类在filter.myFilter,并定义对应的mapping对所有url路径进行过滤。

    <filter>
        <filter-name>FirstFilter</filter-name>
        <filter-class>filter.myFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>FirstFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

还可以通过注解的方式对过滤器进行 注册,不需要手动在web.xml文件中进行配置,注解会在部署时被web容器自动配置注册为过滤器。如下所示为通过注解方式等价的方式

@WebFilter(filterName = "firstFilter",value = {"/*"},dispatcherTypes = {DispatcherType.REQUEST})
public class firstFilter implements Filter {
    ......
}

注解WebFilter常用属性如下:

2、过滤器链

当所访问的url定义了多个过滤器时,用户request请求将按照在web.xml中注册先后顺序依次经过多个过滤器,访问结束之后再嵌套返回response,最后返回给用户。这个顺序类似于栈操作,先进后出。

例如定义两个过滤器的链执行结果:

3、过滤器分类

在配置过滤器时的<dispatcher>选项可以选择对四

种类型的页面请求进行过滤,分别是request、forward、include、error,不同类型的过滤器只会对本类型的请求进行过滤。

request请求是<dispatcher>默认的过滤类型,它会对页面的request请求进行过滤,直接请求或者重定向都是request类型。

forward会对转发类型的页面请求进行过滤,通过forwar()函数或者<jsp:forward>动作的均属于转发操作,如下所示,在index.jsp页面中调用forward()函数转发到regPage.jsp页面,定义针对regPage.jsp页面转发操作的过滤器forwardFilter,当用户以转发操作访问regPage.jsp页面时就会经过该过滤器,并在控制台输出:转发过滤器

//在index.jsp页面进行转发到regPage.jsp

servletRequest.getRequestDispatcher("regPage.jsp").forward(servletRequest,servletResponse);

//定义转发过滤器
public class forwardFilter implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println("转发过滤器");
        chain.doFilter(req, resp);
    }

    public void init(FilterConfig config) throws ServletException {

    }

}
//注册转发过滤器,针对regPage.jsp页面的转发类型请求进行过滤
    <filter>
        <filter-name>ForwardFilter</filter-name>
        <filter-class>filter.forwardFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>ForwardFilter</filter-name>
        <url-pattern>/regPage.jsp</url-pattern>
        <dispatcher>FORWARD</dispatcher>
    </filter-mapping>

类似地,include会对页面的包含行为进行过滤,使用getDispatcher().include()或者<jsp:include>的操作均是包含行为。

error类型是指页面发生错误之后跳转到指定页面前进行的过滤器,如下所示定义页面在发生404错误后跳转到exception.jsp页面,在这之前经过ERROR类型过滤器errorFilter

    <error-page>
        <error-code>404</error-code>
        <location>/exception.jsp</location>
    </error-page>
    <filter>
        <filter-name>errorFilter</filter-name>
        <filter-class>filter.errorFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>errorFilter</filter-name>
        <url-pattern>/exception.jsp</url-pattern>
        <dispatcher>ERROR</dispatcher>
    </filter-mapping>

4、加载配置信息

在过滤器init()函数可以加载web.xml文件中<init-param>标签的配置信息。

例如当一个网站过滤器loginFilter需要对用户是否登录进行判断,在用户登录之后将保存信息到session中,在过滤器中对session进行判断,如果已经登录则可以继续访问其他页面,反之如果没有登录则需要跳转到login.jsp页面进行登录。但是过滤器不应该对login.jsp和登陆失败的页面fail.jsp页面进行再次过滤,这样就陷入了死循环。因此把不需要过滤的例外页面以初始化参数的形式保存在web.xml中,在过滤器init()函数中加载,并在doFilter()中进行判断当前路径是否属于例外,如果是则放行请求。

为了防止中文乱码,需要在页面中设置request编码格式,但是在每个页面中都设置过于麻烦,可以在过滤器中对request编码进行设置,这样只要经过过滤器的页面的编码就都会生效。可以在web.xml文件中对编码方式进行指定,之后再加载到过滤器中。

    <filter>
        <filter-name>loginFilter</filter-name>
        <filter-class>filter.loginFilter</filter-class>
        <init-param> <!--设置不需要过滤的路径-->
            <param-name>passPath</param-name>
            <param-value>login.jsp;fail.jsp</param-value>
        </init-param>
        <init-param>    <!--设置编码方式-->
            <param-name>charset</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>loginFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
public class loginFilter implements Filter {
    private FilterConfig config;        //设置配置参数变量

    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;

        //读取配置文件中的编码并设置
        String charset = config.getInitParameter("charset");
        if (charset != null)
            request.setCharacterEncoding(charset);

        //判断路径是否属于配置文件中规定的路径,如果是则无需验证,放行请求
        String passPath = config.getInitParameter("passPath");
        String[] paths = passPath.split(";");
        for (String path : paths) {                
            if (request.getRequestURI().indexOf(path) != -1) {
                chain.doFilter(req, resp);
                return;
            }
        }

        //检查session是否有用户名信息,有代表已经登录可以访问,否则跳转到登录页面
        if (request.getSession().getAttribute("username") != null)
            chain.doFilter(req, resp);
        else
            response.sendRedirect("login.jsp");
    }

    public void init(FilterConfig config) throws ServletException {
        this.config = config;     //在初始化时加载配置参数
    }

}

监听器

监听器是Servlet中定义的一种特殊类,用于监听ServletContext、HttpSession和HttpRequest域对象(分别对应jsp的application、session、request三个域)创建、属性修改或者销毁等事件,并且在事件发生前后执行操作。监听器常用于监听客户端的请求与服务器端的操作,例如统计在线人数、访问量、加载初始化信息等。

1、创建监听器

监听器本质上是一个Java的servlet类,只不过它实现了一些Listerner接口的方法,例如实现ServletContextListener接口的initialized和destroyed方法,这两个方法分别对应在servlet创建、销毁时进行调用

public class FirstListener implements ServletContextListener{
    // Servlet要求保留无参构造器
    public FirstListener() {
    }

    // web应用servlet部署时调用此方法
    public void contextInitialized(ServletContextEvent sce) {     
    }

    // servlet销毁时调用
    public void contextDestroyed(ServletContextEvent sce) {
    }
}

接下来需要在web.xml对监听器进行注册,该监听器位于listener包下的FirstListener:

    <listener>
        <listener-class>listener.FirstListener</listener-class>
    </listener>

在servlet3.0以后不需要注册,而是采用注解的方式,在监听器类声明前添加一行:@WebListener("描述")

web应用按照在web.xml文件中的注册顺序启动监听器,并且先启动监听器,再启动过滤器,最后加载servlet

2、监听器的分类

1、监听对象的创建和销毁

按照监听的对象分为监听整个应用application、监听session、监听request的三种监听器,对应的需要实现ServletContextListener、HttpSessionListenter、ServletRequestListener三个接口,每个接口都有initialized()和destroyed()方法来对对应对象的创建和销毁进行监听。通过方法传入参数对象的getXxx()方法可以获取当前调用的对象,例如se.getSession()可以获取当前的session对象,进而通过监听器操作对象。

例如通过Servlet监听器获取初始化参数,并将其设置为servlet的全局参数,这样在之后整个application中都可以得到该参数

    <context-param>
        <param-name>initParam</param-name>
        <param-value>初始化参数</param-value>
    </context-param>
    @Override   //servlet创建时调用
    public void contextInitialized(ServletContextEvent sce) {
        //获取servlet初始化参数
        String initParam =sce.getServletContext().getInitParameter("initParam");
        //设置全局servlet参数
        sce.getServletContext().setAttribute("init",initParam);
    }

    @Override    //servlet销毁时调用
    public void contextDestroyed(ServletContextEvent sce) {

    }

如下为session和request监听器的创建和销毁时的监听

package listener;

import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;


public class FirstListener implements HttpSessionListener, ServletRequestListener {

    //servlet要求保留空构造器
    public FirstListener() {
    }

    @Override       //创建session时调用
    public void sessionCreated(HttpSessionEvent se) {
        se.getSession();
    }

    @Override       //session销毁时调用
    public void sessionDestroyed(HttpSessionEvent se) {

    }

    @Override       //request对象创建时调用
    public void requestDestroyed(ServletRequestEvent sre) {

    }

    @Override       //request销毁时调用
    public void requestInitialized(ServletRequestEvent sre) {

    }
}

2、监听属性的改变

用于监听Servlet、Session、Request中的对象属性发生改变时进行调用,其继承的接口为ServletContextAttributeListener,
        HttpSessionAttributeListener, ServletRequestAttributeListener,这三个接口均需要实现三个方法:attributeAdded(),attributeRemoved(),attributeReplaced(),只不过传入的参数对象不同,例如Servlet对应传入的参数对象为ServletContextAttributeEvent scae。其余的用法都类似。例如下面通过request属性监听器来监听其属性创建、改变、删除操作

package listener;

import javax.servlet.*;

public class AttributeListener implements ServletRequestAttributeListener {

    // Public constructor is required by servlet spec
    public AttributeListener() {
    }

    @Override
    public void attributeAdded(ServletRequestAttributeEvent srae) {
        System.out.println("request添加属性:"+srae.getName());
    }

    @Override
    public void attributeRemoved(ServletRequestAttributeEvent srae) {
        System.out.println("request删除属性:"+srae.getName());
    }

    @Override
    public void attributeReplaced(ServletRequestAttributeEvent srae) {
        System.out.println("request修改属性:"+srae.getName());
    }
}

//--------------------listener.jsp页面创建并修改request属性-----------------
    <%
        request.setAttribute("request属性","request参数");
        request.setAttribute("request属性","request参数2");
    %>

Session的绑定接口HttpSessionBindingListener、钝化接口HttpSessionActivationListener不需要创建专门的监听器,而是可以在普通JavaBean类中继承和实现,例如一个Student类实现了这两个接口,则当一个Student对象被绑定/解绑到session中时,就会触发对应的方法,同理该对象被钝化/活化时也会触发。如下为Student类:

package modules;

import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import javax.servlet.http.HttpSessionEvent;

public class Student implements HttpSessionBindingListener, HttpSessionActivationListener {                  
    private String name;                
    private int age;

    public Student() {                   
    }

    public String getName() {          
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public void valueBound(HttpSessionBindingEvent event) {
        System.out.println("Session绑定User对象" + event.getName());
    }

    @Override
    public void valueUnbound(HttpSessionBindingEvent event) {
        System.out.println("Session解绑User对象" + event.getName());
    }

    @Override
    public void sessionWillPassivate(HttpSessionEvent se) {
        System.out.println("Session钝化对象:" + se.getSource());
    }

    @Override
    public void sessionDidActivate(HttpSessionEvent se) {
        System.out.println("Session活化对象:" + se.getSource());
    }
}

//-----------------在jsp页面内绑定、解绑Student对象------------------------
    <%
        Student s1=new Student();
        session.setAttribute("student",s1);    //输出:Session绑定User对象student
        session.removeAttribute("student");     //输出:Session解绑User对象student
    %>

 

发布了124 篇原创文章 · 获赞 65 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/theVicTory/article/details/104349287