Filter过滤器

所谓的过滤.指的是过滤请求.

有时我们进行后段项目开发时,有些请求,需要特定的条件才能操作;我们可以通过Filter,来过滤不满足的用户操作.

例如:

用于的个人中心, 应该是登录  后才可以查看的.
当用户请求个人中心页面时, 我们就可以编写过滤器, 将未登录的所有用户过滤掉, 并重定向至登录页面.

采用了面向切面编程思想(AOP).


过滤器的使用

编写home.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
System.out.println("JSP正在执行");
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
一二三四五 , 上山打老虎
</body>
</html>

1.编写一个类,实现Filter接口


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package demo1;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class ParamFilter implements Filter {
/**
* 当过滤器即将销毁时, 执行
*/
@Override
public void destroy() {
}

/**
* 当发生匹配过滤地址的请求时, doFilter执行
* 过滤器执行在web的所有资源之前 , 默认此方法是拦截请求的,
* 如果需要放行 , 需编写: chain.doFilter(request,response);
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
if(req.getParameter("a")==null) {
resp.setContentType("text/html;charset=utf-8");
//拦截
resp.getWriter().append("<h1>很遗憾, 参数错误, 无法访问</h1>");
}else {
//放行
chain.doFilter(request, response);
}
}

/**
* 当过滤器初始化时, 执行
*/
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}

2.通过web.xml或注解的方式,配置过滤器的过滤地址(home.jsp).

web.xml

1
2
3
4
5
6
7
8
<filter>
<filter-name>pf</filter-name>
<filter-class>demo1.ParamFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>pf</filter-name>
<url-pattern>/home.jsp</url-pattern>
</filter-mapping>

注解

1
在ParamFilter类的类体上注解@WebFilter("/home.jsp")

过滤器链


当多个过滤器,过滤地址重复时,就形成了过滤器链.用户的请求,需要过滤器链中的所有过滤器放行,才可以正常访问.

过滤器链执行的顺序:

1
2
3
web.xml中的顺序:按照web.xml中配置的先后顺序, 来执行.
注解配置的顺序:按照类名的自然排序,顺序执行.
web.xml中配置的过滤器,一定执行在注解配置过滤器的前面.

在web下新建login.jsp,并输入

1
2
3
4
5
6
7
8
<body>
<h3>用户登录</h3>
<form action="login.do" method="POST">
<input placeholder="请输入帐号" name="uname"><br>
<input placeholder="请输入密码" name="upass" type="password"><br>
<button>登录</button>
</form>
</body>

在src下新建loginservlet.class,并输入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
* Servlet implementation class LoginServlet
*/
@WebServlet("/login.do")
public class LoginServlet extends HttpServlet {

/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String uname = request.getParameter("uname");
String upass = request.getParameter("upass");
if(uname.equals("嘿嘿嘿") && upass.equals("123")) {
//登录成功
response.getWriter().append("恭喜你, 登录成功了");
}else {
//登录失败
response.getWriter().append("很遗憾, 登录失败了");
}

}

/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}

在src下新建过滤器CharSetFilter.class,并输入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

/**
* Servlet Filter implementation class CharSetFilter
*/
@WebFilter("*.do")
public class CharSetFilter implements Filter {
/**
* Default constructor.
*/
public CharSetFilter() {
// TODO Auto-generated constructor stub
}

/**
* @see Filter#destroy()
*/
public void destroy() {
// TODO Auto-generated method stub
}

/**
* @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
*/
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("正在调整编码");
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
chain.doFilter(request, response);
}

/**
* @see Filter#init(FilterConfig)
*/
public void init(FilterConfig fConfig) throws ServletException {
// TODO Auto-generated method stub
}
}

在src下新建过滤器NullFilter.class,并输入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
* Servlet Filter implementation class NullFilter
*/
@WebFilter("/login.do")
public class NullFilter implements Filter {
/**
* Default constructor.
*/
public NullFilter() {
// TODO Auto-generated constructor stub
}

/**
* @see Filter#destroy()
*/
public void destroy() {
// TODO Auto-generated method stub
}

/**
* @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
*/
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("正在过滤, 参数是否不存在");
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
String uname = req.getParameter("uname");
String upass = req.getParameter("upass");

//login.do?uname=&upass=
//login.do
if(uname==null || upass==null || "".equals(uname) || "".equals(upass)) {
resp.getWriter().append("<script>alert('帐号密码, 必须传递');window.location.href='login.jsp'</script>");
}else {
chain.doFilter(request, response);
}
}

/**
* @see Filter#init(FilterConfig)
*/
public void init(FilterConfig fConfig) throws ServletException {
// TODO Auto-generated method stub
}
}

此时访问login.do是输入表单后,先执行过滤器CharSetFilter,在执行NullFilter,因为是一首字母排序.

如果在web.xml里添加NullFilter的过滤地址,则先执行的是NullFilter

Listener


监听器,监听是服务器的一些事件.

两类事件:

1
2
1.服务器中组件的生命周期相关事件.
2.域对象中数据的变化事件 .
  • ServletContextListener

    用于监听Servlet上下文的创建与销毁.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    import javax.servlet.ServletContextEvent;
    import javax.servlet.ServletContextListener;
    import javax.servlet.annotation.WebListener;

    @WebListener
    public class MyServletContextListener implements ServletContextListener {
    /**
    * 当上下文对象即将销毁时,方法执行
    * 上下文对象销毁的时机,是服务器关闭或项目被卸载.所以我们常在这里进行全局资源释放的操作.
    */
    public void contextDestroyed(ServletContextEvent arg0) {
    System.out.println("项目关闭了");
    }

    /**
    * 当上下文对象被创建初始化时,这个方法执行
    * 因为上下文对象创建的时机是服务器中项目启动时,所以我们常在这里进行全局资源的初始化操作.
    */
    public void contextInitialized(ServletContextEvent arg0) {
    System.out.println("项目启动了");
    }
    }
  • ServletContextAttributeListener

    用于监听ServletContext中属性的变化

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    import javax.servlet.ServletContextAttributeEvent;
    import javax.servlet.ServletContextAttributeListener;
    import javax.servlet.annotation.WebListener;

    /**
    *
    */
    @WebListener
    public class MyServletContextAttributeListener implements ServletContextAttributeListener {
    /**
    * 当向上下文中 添加属性时, 代码执行
    */
    public void attributeAdded(ServletContextAttributeEvent e) {
    //从事件对象中, 得到存储的键和值
    String key = e.getName();
    Object value = e.getValue();
    System.out.println("监听到ServletContext中数据的增加:"+key+" --> "+value);
    }
    /**
    * 当从上下文中 移除属性时, 代码执行
    */
    public void attributeRemoved(ServletContextAttributeEvent e) {
    //从事件对象中, 得到被移除的键和值
    String key = e.getName();
    Object value = e.getValue();
    System.out.println("监听到ServletContext中数据的移除:"+key+" --> "+value);
    }

    /**
    * 当操作上下文对象 存储数据 ,发生替换时, 代码执行.
    */
    public void attributeReplaced(ServletContextAttributeEvent e) {
    //从事件对象中, 得到被替换掉的旧的键和值
    String key = e.getName();
    Object oldValue = e.getValue();
    System.out.println("监听到ServletContext中数据的替换, 旧数据:"+key+" --> "+oldValue);
    //从事件对象中, 得到上下文对象
    Object newValue = e.getServletContext().getAttribute(key);
    System.out.println("监听到ServletContext中数据的替换, 新数据:"+key+" --> "+newValue);
    }
    }
  • HttpSessionListener

    用于监听session的创建与销毁.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    import javax.servlet.annotation.WebListener;
    import javax.servlet.http.HttpSessionEvent;
    import javax.servlet.http.HttpSessionListener;
    import java.util.ArrayList;
    import java.util.Random;

    @WebListener
    public class MySessionListener implements HttpSessionListener {
    private static int count = 0;
    private static Random r = new Random();
    ArrayList<Integer> nums = new ArrayList<>();
    public void sessionCreated(HttpSessionEvent arg0) {
    //30-60的随机数字
    int num = r.nextInt(31)+30;
    count+=num;
    nums.add(num);
    }

    public void sessionDestroyed(HttpSessionEvent arg0) {
    int num = nums.remove(nums.size()-1);
    count-=num;
    }

    public static int getCount() {
    return count;
    }
    }
  • HttpSessionAttributeListener

    用于监听session中的数据的增加 ,删除, 修改.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    import javax.servlet.annotation.WebListener;
    import javax.servlet.http.HttpSessionAttributeListener;
    import javax.servlet.http.HttpSessionBindingEvent;
    import java.util.ArrayList;

    /**
    *
    */
    @WebListener
    public class MyHttpSessionAttributeListener implements HttpSessionAttributeListener {
    private static ArrayList<Object> data = new ArrayList<>();

    public static ArrayList<Object> getData() {
    return data;
    }

    /**
    * 当session中存储数据时 , 方法执行
    */
    public void attributeAdded(HttpSessionBindingEvent e) {
    //获取新增加的数据
    String name = e.getName();
    Object value = e.getValue();

    // 当登录成功时, 通常我们会将用户名存储在session中, 用于会话跟踪.
    if("username".equals(name)) {
    //当一个用户名存储到session时, 我们就将这个用户名 加入到集合中
    data.add(value);
    }
    System.out.println("新用户登录, 用户名已存储");
    }

    /**
    * 当从session中移除数据时, 方法执行
    */
    public void attributeRemoved(HttpSessionBindingEvent e) {
    //获取删除的数据
    String name = e.getName();
    Object value = e.getValue();
    if("username".equals(name)) {
    //当一个用户名存储到session时, 我们就将这个用户名 加入到集合中
    data.remove(value);
    }
    System.out.println("用户退出登录, 用户名从列表中移除");
    }

    /**
    * 当向session中设置键值对, 由于键重复, 导致值被替换时 , 方法执行
    */
    public void attributeReplaced(HttpSessionBindingEvent e) {
    //获取被替换的旧数据
    String name = e.getName();
    Object oldValue = e.getValue();
    Object newValue = e.getSession().getAttribute(name);
    data.remove(oldValue);
    data.add(newValue);
    System.out.println("用户更换帐号登录, 用户名从列表中更新");
    }
    }