JavaWeb 学习笔记 7:Filter
1.快速开始
使用过滤器的方式与 Servlet 类似,要实现一个Filter
接口:
@WebFilter("/*")
public class FirstFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("pre chain.doFilter");
// 放行请求
chain.doFilter(request, response);
System.out.println("after chain.doFilter");
}
public void destroy() {
}
}
这里 @WebFilter
指定的是过滤器拦截的路径规则,/*
是对所有请求进行拦截。
Fitler
接口有三个方法:
init
,过滤器初始化时执行doFilter
,拦截请求destroy
,过滤器销毁时执行
在doFilter
方法中,通常需要执行chain.doFilter()
方法放行请求,否则请求就不会正常被 Servlet 进行处理,直接被 Filter 阻断。
添加一个 JSP 以观察 Filter 的执行过程:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>Hello World!</h1>
<% System.out.println("hello.jsp..."); %>
</body>
</html>
请求这个 JSP 会看到如下输出:
pre chain.doFilter
hello.jsp...
after chain.doFilter
整个过程可以用下图表示:
2.Filter 拦截路径
Filter 可以拦截以下几种路径:
- 具体路径,比如
/jsp/hello.jsp
,只有访问这个 JSP 的请求会被拦截。 - 目录,比如
/jsp/*
,访问/jsp
这个目录下的所有资源路径都会被拦截。 - 后缀名拦截,比如
*.jsp
,左右访问后缀名为.jsp
的资源都会被拦截。 - 拦截全部,
/*
,访问任意资源都会被拦截。
3.过滤器链
一个 Web 应用可以配置多个过滤器,这些过滤器合称“过滤器链”。
下面看实际演示。
在应用中设置两个过滤器:
@WebFilter("/*")
public class Filter1 implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("before filter1 chain.doFilter");
chain.doFilter(request, response);
System.out.println("after filter1 chain.doFilter");
}
public void destroy() {
}
}
@WebFilter("/*")
public class Filter2 implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("before filter2 chain.doFilter");
chain.doFilter(request, response);
System.out.println("after filter2 chain.doFilter");
}
public void destroy() {
}
}
请求 JSP 页面可以看到如下输出:
before filter1 chain.doFilter
before filter2 chain.doFilter
hello.jsp...
after filter2 chain.doFilter
after filter1 chain.doFilter
如果使用注解配置过滤器,过滤器在过滤器链上的先后顺序由过滤器类名做字典排序决定。
4.案例:登录验证
使用过滤器可以对一些需要在多个 Servlet 中进行的统一处理进行简化。比如每个 Servlet 都需要的请求内容乱码处理或者响应报文添加统一的报文头等等。
这里展示如何使用过滤器实现登录验证功能。
@WebFilter("/*")
public class LoginFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 对登录相关请求和静态资源进行放行
String[] paths = new String[]{
"/user/login",
"/user/registry",
"/user/check_code",
"/css/",
"/imgs/",
"/index.jsp"
};
if (!(request instanceof HttpServletRequest &&
response instanceof HttpServletResponse)){
throw new RuntimeException("This is not web Application.");
}
HttpServletRequest hRequest = (HttpServletRequest) request;
HttpServletResponse hResponse = (HttpServletResponse) response;
String url = hRequest.getRequestURL().toString();
String prefix = "http://"+hRequest.getHeader("host")+hRequest.getContextPath();
for (String path: paths){
if (url.indexOf(prefix+path) == 0){
// 符合规则,放行
System.out.println("放行"+url);
chain.doFilter(hRequest, hResponse);
return;
}
}
// 检查是否登录,如果没有登录,重定向到登录页面
Object username = hRequest.getSession().getAttribute("username");
if (username == null){
hResponse.sendRedirect("/login-demo/user/login");
return;
}
// 已经登录,放行
chain.doFilter(hRequest, hResponse);
}
@Override
public void destroy() {
}
}
需要注意的是,这里除了需要通过 Session 判断是否登录状态,没有登录的让重定向到登录页面以外,还需要对于特殊路径进行放行,否则有些页面就无法正常显示。在我们这个示例中,需要非登录状态就可以访问的资源有:
- CSS
- 图片
- 登录相关的 Servlet
- 注册相关的 Servlet
- 返回验证码的 Servlet
- 首页(用于重定向到登录页)
对于这些特殊资源,这里简单地用字符串匹配的方式判断和处理。
5.Listener
Listener、Filter 和 Servlet 是 JavaWeb 的三大组件。
Listener 的用途是监听 Application\Session\Request 三个对象的创建、销毁或属性的修改和删除。
具体包含以下监听器:
利用 ServletContext
监听器,我们可以在 Servlet 容器创建后执行一些应用的初始化代码:
@WebListener
public class ApplicationRunner implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
// 这里执行一些应用的初始化工作
System.out.println("Web Application is already run...");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
// 这里执行应用退出时的清理工作
}
}
使用监听器很容易,只要实现相应的接口,并使用@WebListener
注解标记类即可。
本文的完整示例可以从这里获取。
6.参考资料
- 黑马程序员JavaWeb基础教程