Java开发中,过滤器(Filter)和拦截器(Interceptor)是两种常用的功能扩展机制,它们可以对请求或响应进行预处理或后处理。尽管它们在某些场景下的作用类似,但在实现方式和应用场景上又有显著的区别。本文将详细讲解Java过滤器与拦截器的概念、实现方式、应用场景,并通过代码示例帮助理解。
一、Java过滤器(Filter)
1.1 什么是过滤器?
过滤器是一种Servlet技术,它主要用于在客户端请求到达Servlet之前、或者在Servlet生成响应返回客户端之前,对请求或响应进行预处理或后处理。过滤器的典型应用场景包括记录日志、权限验证、请求参数编码、压缩响应数据等。
1.2 过滤器的工作原理
过滤器的工作原理是基于Java EE中的Servlet规范。每个请求在到达Servlet之前,会先经过配置好的过滤器链,过滤器可以根据需要对请求进行修改或处理。处理完毕后,过滤器可以选择继续将请求传递给下一个过滤器或目标Servlet,也可以中断请求处理。
1.3 过滤器的生命周期
过滤器的生命周期包括初始化(init
)、执行(doFilter
)和销毁(destroy
)三个阶段:
init
: 过滤器初始化时调用,通常用于资源的初始化操作。doFilter
: 每个请求都会调用该方法,用于具体的请求或响应处理。destroy
: 当过滤器被销毁时调用,用于释放资源。
1.4 过滤器的实现步骤
要实现一个Java过滤器,通常需要以下几个步骤:
- 创建过滤器类:实现
javax.servlet.Filter
接口。 - 重写
init
、doFilter
和destroy
方法。 - 配置过滤器:在
web.xml
中配置过滤器,或者使用注解@WebFilter
。
1.5 代码示例
以下是一个简单的过滤器实现
package com.baidu.filter;
import com.alibaba.fastjson.JSONObject;
import com.baidu.pojo.Result;
import com.baidu.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Slf4j
@WebFilter(urlPatterns = "/*")
public class DemoFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
//1.获取请求参数url
String url = req.getRequestURI().toString();
log.info("请求路径:{}",url);
//2.判断url中是否包含login,包含则放行
if (url.contains("login")){
log.info("登录操作,放行");
chain.doFilter(request,response);
return;
}
//3,获取请求头当中的令牌
String jwt = req.getHeader("token");
//4.判断令牌是否存在,不存在则返回错误信息(未登录)
if(!StringUtils.hasLength(jwt)){
log.info("未登录");
Result error = Result.error("NOT_LOGIN");
//将相应的数据转换为json格式
String notlogin = JSONObject.toJSONString(error);
resp.getWriter().write(notlogin);
return;
}
//5.存在则校验令牌是否正确,正确则放行,不正确则返回错误信息(未登录)
try {
JwtUtils.parseJwt(jwt);
} catch (Exception e) {
e.printStackTrace();
log.info("令牌错误");
Result error = Result.error("NOT_LOGIN");
//将相应的数据转换为json格式
String notlogin = JSONObject.toJSONString(error);
resp.getWriter().write(notlogin);
return;
}
log.info("令牌正确,放行");
chain.doFilter(request,response);
}
@Override
public void destroy() {
Filter.super.destroy();
}
}
在上面的代码中,首先判断用户的请求是否为登录请求,如果是则放行,不是则判断是否有令牌,如果有令牌且令牌合法,则放行。
1.6 过滤器的应用场景
过滤器广泛应用于以下场景:
- 日志记录:记录每个请求的访问日志,包括请求路径、参数、响应时间等。
- 权限验证:在请求到达Servlet之前进行用户权限检查,如果没有权限可以直接返回错误响应。
- 请求参数编码:统一处理请求参数的字符编码,避免出现乱码。
- 响应数据压缩:在返回响应之前对数据进行GZIP压缩,减少传输的数据量。
二、Java拦截器(Interceptor)
2.1 什么是拦截器?
拦截器是一种AOP(面向切面编程)技术,主要用于在方法调用前后进行处理。Java中的拦截器通常与Spring框架一起使用,可以在控制器(Controller)方法执行前后或抛出异常时执行自定义逻辑。拦截器通常用于记录日志、权限校验、事务管理等。
2.2 拦截器的工作原理
拦截器是通过代理模式实现的,Spring MVC中的拦截器链类似于过滤器链。每个请求在到达Controller之前,都会经过配置的拦截器,拦截器可以根据需要对请求进行处理,甚至可以决定是否继续传递请求。
2.3 拦截器的生命周期
拦截器的生命周期与请求处理的阶段密切相关:
preHandle
: 在请求到达Controller方法之前调用,用于处理前置逻辑。如果返回false
,则请求不会继续传递。postHandle
: 在Controller方法执行之后,视图渲染之前调用,用于处理后置逻辑。afterCompletion
: 在整个请求处理完毕后调用,包括视图渲染完成或抛出异常时,用于资源清理或日志记录。
2.4 拦截器的实现步骤
要实现一个Java拦截器,通常需要以下几个步骤:
- 创建拦截器类:实现
HandlerInterceptor
接口。 - 重写
preHandle
、postHandle
和afterCompletion
方法。 - 配置拦截器:在Spring配置文件中配置拦截器,或者使用注解配置。
2.5 代码示例
以下是一个简单的拦截器实现,用于检查用户是否登录:
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 检查用户是否登录
Object user = request.getSession().getAttribute("user");
if (user == null) {
// 未登录,重定向到登录页面
response.sendRedirect("/login");
return false;
}
return true; // 继续执行下一个拦截器或目标方法
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,ModelAndView modelAndView) throws Exception {
// 处理后置逻辑
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,Exception ex) throws Exception {
// 资源清理或日志记录
}
}
在这个示例中,AuthInterceptor
拦截器会在每次请求时检查用户是否已经登录,如果未登录则重定向到登录页面。
package com.baidu.interceptor;
import com.alibaba.fastjson.JSONObject;
import com.baidu.pojo.Result;
import com.baidu.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
@Slf4j
public class LoginCheck implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {
//1.获取请求参数url
String url = req.getRequestURI().toString();
log.info("请求路径:{}",url);
//3,获取请求头当中的令牌
String jwt = req.getHeader("token");
//4.判断令牌是否存在,不存在则返回错误信息(未登录)
if(!StringUtils.hasLength(jwt)){
log.info("未登录");
Result error = Result.error("NOT_LOGIN");
//将相应的数据转换为json格式
String notlogin = JSONObject.toJSONString(error);
resp.getWriter().write(notlogin);
return false;
}
//5.存在则校验令牌是否正确,正确则放行,不正确则返回错误信息(未登录)
try {
JwtUtils.parseJwt(jwt);
} catch (Exception e) {
e.printStackTrace();
log.info("令牌错误");
Result error = Result.error("NOT_LOGIN");
//将相应的数据转换为json格式
String notlogin = JSONObject.toJSONString(error);
resp.getWriter().write(notlogin);
return false;
}
log.info("令牌正确,放行");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
配置类:
package com.baidu.config;
import com.baidu.interceptor.LoginCheck;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginCheck loginCheck;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginCheck).addPathPatterns("/**")
.excludePathPatterns("/login.html","/login","/css/**","/js/**","/images/**");
}
}
2.6 拦截器的应用场景
拦截器主要用于以下场景:
- 权限校验:在Controller方法执行之前进行用户权限检查,未授权的请求会被拦截。
- 日志记录:记录Controller方法的执行时间、请求参数和响应数据等。
- 事务管理:在方法执行前开启事务,在方法执行后提交事务,在异常时回滚事务。
- 全局异常处理:捕获Controller方法抛出的异常,并进行统一处理或记录。
三、过滤器与拦截器的区别
尽管过滤器和拦截器在某些场景下可以互换使用,但它们在实现方式、应用层次和使用场景上存在显著区别。
3.1 实现方式
- 过滤器基于Servlet规范,实现
javax.servlet.Filter
接口,工作在Servlet容器层级。 - 拦截器基于Spring框架,实现
HandlerInterceptor
接口,工作在Spring MVC框架层级。
3.2 应用层次
- 过滤器一般在整个Web应用的最外层进行处理,它可以过滤所有请求,包括静态资源(如CSS、JS、图片等)。
- 拦截器主要用于拦截Controller方法调用,通常只对动态请求起作用。
3.3 使用场景
- 过滤器适用于请求的全局预处理,例如日志记录、编码处理、跨域设置等。
- 拦截器适用于业务逻辑的前置或后置处理,例如权限验证、事务管理等。
3.4 代码配置
- 过滤器可以在
web.xml
中配置,也可以使用@WebFilter
注解。 - 拦截器一般在Spring的配置文件中配置,也可以使用
@Configuration
类中注册。
3.5 性能影响
- 过滤器在Servlet容器级别运行,通常性能开销较低,但由于处理的是所有请求,配置不当可能影响整体性能。
- 拦截器在Spring容器级别运行,性能开销取决于Spring容器的管理,通常与业务逻辑绑定紧密。
四、总结
通过以上分析可以看出,过滤器和拦截器在Java Web开发中各有其独特的作用,我们应该根据需求来选择。