Java过滤器详情指南
Java过滤器详解摘要 Java过滤器(Filter)是Web应用的核心组件,用于预处理请求和后处理响应。主要特点包括请求拦截、响应处理、链式调用和生命周期管理。与拦截器相比,过滤器基于函数回调,仅用于Web应用,执行顺序更靠前。源码分析揭示了Filter接口的核心方法、FilterChain的链式调用机制以及ApplicationFilterChain的内部实现原理。过滤器通过配置灵活、执行高效
Java过滤器详情指南
目录
简介
什么是Java过滤器
Java过滤器(Filter)是Java Web应用中的一个重要组件,它可以在请求到达Servlet之前和响应返回客户端之前对请求和响应进行预处理和后处理。
核心特性
特性 | 说明 |
---|---|
请求拦截 | 在请求到达Servlet之前进行拦截和处理 |
响应处理 | 在响应返回客户端之前进行处理 |
链式调用 | 多个过滤器可以组成过滤器链,按顺序执行 |
生命周期管理 | 由容器管理,随Web应用启动而创建,随应用关闭而销毁 |
配置灵活 | 支持注解配置和web.xml配置两种方式 |
过滤器与拦截器的区别
对比项 | Filter | Interceptor |
---|---|---|
实现方式 | 基于函数回调 | 基于反射机制 |
使用范围 | 只能用于Web应用 | 可以用于Web应用、Spring应用等 |
获取资源 | 只能获取ServletContext | 可以获取Spring容器中的资源 |
执行顺序 | 在Interceptor之前执行 | 在Filter之后执行 |
异常处理 | 不能使用Spring的异常处理机制 | 可以使用Spring的异常处理机制 |
源码分析
1. Filter接口源码
public interface Filter {
/**
* 过滤器初始化方法,在Web应用启动时调用
* @param filterConfig 过滤器配置对象
* @throws ServletException 初始化异常
*/
default void init(FilterConfig filterConfig) throws ServletException {
}
/**
* 过滤器核心方法,处理请求和响应
* @param request 请求对象
* @param response 响应对象
* @param chain 过滤器链
* @throws IOException IO异常
* @throws ServletException Servlet异常
*/
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException;
/**
* 过滤器销毁方法,在Web应用关闭时调用
*/
default void destroy() {
}
}
2. FilterChain接口源码
public interface FilterChain {
/**
* 调用过滤器链中的下一个过滤器
* 如果没有下一个过滤器,则调用目标资源(Servlet)
* @param request 请求对象
* @param response 响应对象
* @throws IOException IO异常
* @throws ServletException Servlet异常
*/
void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException;
}
3. FilterConfig接口源码
public interface FilterConfig {
/**
* 获取过滤器名称
* @return 过滤器名称
*/
String getFilterName();
/**
* 获取ServletContext对象
* @return ServletContext对象
*/
ServletContext getServletContext();
/**
* 获取初始化参数值
* @param name 参数名
* @return 参数值
*/
String getInitParameter(String name);
/**
* 获取所有初始化参数名
* @return 参数名枚举
*/
Enumeration<String> getInitParameterNames();
}
4. ApplicationFilterChain源码分析
public final class ApplicationFilterChain implements FilterChain {
// 过滤器数组
private ApplicationFilterConfig[] filters;
// 当前执行的过滤器索引
private int pos = 0;
// 过滤器链中过滤器的数量
private int n = 0;
// Servlet对象
private Servlet servlet = null;
// 请求对象
private Request request = null;
// 响应对象
private Response response = null;
/**
* 过滤器链的核心执行方法
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
// 内部doFilter方法,传递this作为FilterChain
internalDoFilter(request, response);
}
/**
* 内部doFilter方法
*/
private void internalDoFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
// 如果还有过滤器需要执行
if (pos < n) {
// 获取当前过滤器
ApplicationFilterConfig filterConfig = filters[pos++];
Filter filter = null;
try {
// 获取过滤器实例
filter = filterConfig.getFilter();
// 调用过滤器的doFilter方法,传递this作为FilterChain
filter.doFilter(request, response, this);
} catch (IOException | ServletException | RuntimeException e) {
// 异常处理
throw e;
} catch (Throwable e) {
// 其他异常包装为ServletException
throw new ServletException("Filter execution failed", e);
}
return;
}
// 所有过滤器执行完毕,调用Servlet的service方法
try {
if (servlet != null) {
servlet.service(request, response);
}
} catch (IOException | ServletException e) {
throw e;
} catch (Throwable e) {
throw new ServletException("Servlet execution failed", e);
}
}
}
5. 过滤器执行流程
设计模式
1. 责任链模式(Chain of Responsibility)
模式说明
过滤器链是责任链模式的典型应用,每个过滤器都有机会处理请求,并且可以将请求传递给下一个过滤器。
实现分析
// 责任链模式的核心实现
public class ApplicationFilterChain implements FilterChain {
private ApplicationFilterConfig[] filters;
private int pos = 0;
@Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if (pos < filters.length) {
// 获取当前过滤器
ApplicationFilterConfig filterConfig = filters[pos++];
Filter filter = filterConfig.getFilter();
// 调用当前过滤器,传递this作为下一个处理器
filter.doFilter(request, response, this);
} else {
// 所有过滤器处理完毕,调用目标资源
servlet.service(request, response);
}
}
}
模式优势
- 解耦: 发送者和接收者解耦
- 灵活性: 可以动态组合过滤器
- 扩展性: 新增过滤器不影响现有代码
2. 模板方法模式(Template Method)
模式说明
Filter接口定义了过滤器执行的标准流程,子类通过实现doFilter方法来定义具体的处理逻辑。
实现分析
// 模板方法模式
public interface Filter {
// 模板方法:定义执行流程
default void init(FilterConfig filterConfig) throws ServletException {}
// 抽象方法:子类必须实现
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException;
// 模板方法:定义销毁流程
default void destroy() {}
}
// 具体实现类
public class LoggingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 前置处理
long startTime = System.currentTimeMillis();
System.out.println("请求开始时间: " + startTime);
// 调用下一个处理器
chain.doFilter(request, response);
// 后置处理
long endTime = System.currentTimeMillis();
System.out.println("请求处理时间: " + (endTime - startTime) + "ms");
}
}
3. 装饰器模式(Decorator)
模式说明
过滤器可以对请求和响应进行装饰,添加额外的功能而不改变原有的接口。
实现分析
// 装饰器模式:请求装饰器
public class RequestDecorator extends HttpServletRequestWrapper {
private final HttpServletRequest request;
public RequestDecorator(HttpServletRequest request) {
super(request);
this.request = request;
}
@Override
public String getParameter(String name) {
String value = request.getParameter(name);
// 添加装饰逻辑:参数值过滤
if (value != null) {
value = value.replaceAll("<script>", "").replaceAll("</script>", "");
}
return value;
}
}
// 装饰器模式:响应装饰器
public class ResponseDecorator extends HttpServletResponseWrapper {
private final HttpServletResponse response;
public ResponseDecorator(HttpServletResponse response) {
super(response);
this.response = response;
}
@Override
public PrintWriter getWriter() throws IOException {
// 添加装饰逻辑:响应内容压缩
return new PrintWriter(new GZIPOutputStream(response.getOutputStream()));
}
}
4. 策略模式(Strategy)
模式说明
不同的过滤器实现不同的处理策略,可以根据配置动态选择使用哪个过滤器。
实现分析
// 策略接口
public interface FilterStrategy {
void process(HttpServletRequest request, HttpServletResponse response);
}
// 具体策略:日志记录
public class LoggingStrategy implements FilterStrategy {
@Override
public void process(HttpServletRequest request, HttpServletResponse response) {
System.out.println("记录请求日志: " + request.getRequestURI());
}
}
// 具体策略:权限验证
public class AuthStrategy implements FilterStrategy {
@Override
public void process(HttpServletRequest request, HttpServletResponse response) {
String token = request.getHeader("Authorization");
if (token == null || !validateToken(token)) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
}
}
// 策略上下文
public class FilterContext {
private FilterStrategy strategy;
public void setStrategy(FilterStrategy strategy) {
this.strategy = strategy;
}
public void execute(HttpServletRequest request, HttpServletResponse response) {
strategy.process(request, response);
}
}
5. 工厂模式(Factory)
模式说明
FilterConfig负责创建和管理过滤器实例,这是工厂模式的应用。
实现分析
// 过滤器工厂
public class FilterFactory {
private static final Map<String, Filter> filterCache = new ConcurrentHashMap<>();
public static Filter createFilter(String filterClassName) throws Exception {
// 先从缓存中获取
Filter filter = filterCache.get(filterClassName);
if (filter != null) {
return filter;
}
// 通过反射创建实例
Class<?> filterClass = Class.forName(filterClassName);
filter = (Filter) filterClass.newInstance();
// 放入缓存
filterCache.put(filterClassName, filter);
return filter;
}
}
// 在FilterConfig中的应用
public class ApplicationFilterConfig implements FilterConfig {
private final Filter filter;
public ApplicationFilterConfig(String filterClassName) throws Exception {
// 使用工厂创建过滤器实例
this.filter = FilterFactory.createFilter(filterClassName);
}
public Filter getFilter() {
return filter;
}
}
项目应用场景
1. 身份认证和授权
场景描述
在Web应用中,需要对用户进行身份验证和权限控制,确保只有授权用户才能访问特定资源。
实现示例
@WebFilter(urlPatterns = "/*")
public class AuthenticationFilter implements Filter {
private static final Set<String> PUBLIC_URLS = Set.of(
"/login", "/register", "/public", "/static", "/css", "/js", "/images"
);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String requestURI = httpRequest.getRequestURI();
// 检查是否为公开URL
if (isPublicUrl(requestURI)) {
chain.doFilter(request, response);
return;
}
// 检查用户是否已登录
HttpSession session = httpRequest.getSession(false);
if (session == null || session.getAttribute("user") == null) {
// 重定向到登录页面
httpResponse.sendRedirect(httpRequest.getContextPath() + "/login");
return;
}
// 检查用户权限
User user = (User) session.getAttribute("user");
if (!hasPermission(user, requestURI)) {
httpResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
return;
}
// 继续执行过滤器链
chain.doFilter(request, response);
}
private boolean isPublicUrl(String uri) {
return PUBLIC_URLS.stream().anyMatch(uri::startsWith);
}
private boolean hasPermission(User user, String uri) {
// 根据用户角色和权限检查是否有访问权限
return user.getRoles().stream()
.anyMatch(role -> role.hasPermission(uri));
}
}
2. 请求日志记录
场景描述
记录所有HTTP请求的详细信息,包括请求时间、处理时间、请求参数等,用于系统监控和问题排查。
实现示例
@WebFilter(urlPatterns = "/*")
public class LoggingFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(LoggingFilter.class);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// 记录请求开始时间
long startTime = System.currentTimeMillis();
// 记录请求信息
String requestURI = httpRequest.getRequestURI();
String method = httpRequest.getMethod();
String remoteAddr = httpRequest.getRemoteAddr();
String userAgent = httpRequest.getHeader("User-Agent");
logger.info("请求开始 - URI: {}, Method: {}, RemoteAddr: {}, UserAgent: {}",
requestURI, method, remoteAddr, userAgent);
try {
// 继续执行过滤器链
chain.doFilter(request, response);
// 记录响应信息
int status = httpResponse.getStatus();
long duration = System.currentTimeMillis() - startTime;
logger.info("请求完成 - URI: {}, Status: {}, Duration: {}ms",
requestURI, status, duration);
} catch (Exception e) {
// 记录异常信息
long duration = System.currentTimeMillis() - startTime;
logger.error("请求异常 - URI: {}, Duration: {}ms, Error: {}",
requestURI, duration, e.getMessage(), e);
throw e;
}
}
}
3. 字符编码处理
场景描述
统一处理请求和响应的字符编码,避免中文乱码问题。
实现示例
@WebFilter(urlPatterns = "/*")
public class CharacterEncodingFilter implements Filter {
private String encoding = "UTF-8";
private boolean forceEncoding = true;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
String encodingParam = filterConfig.getInitParameter("encoding");
if (encodingParam != null) {
this.encoding = encodingParam;
}
String forceEncodingParam = filterConfig.getInitParameter("forceEncoding");
if (forceEncodingParam != null) {
this.forceEncoding = Boolean.parseBoolean(forceEncodingParam);
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 设置请求编码
if (this.forceEncoding || request.getCharacterEncoding() == null) {
request.setCharacterEncoding(this.encoding);
}
// 设置响应编码
if (this.forceEncoding || response.getCharacterEncoding() == null) {
response.setCharacterEncoding(this.encoding);
}
// 继续执行过滤器链
chain.doFilter(request, response);
}
}
4. 跨域请求处理
场景描述
处理跨域请求,设置必要的CORS头部信息,允许不同域名的客户端访问API。
实现示例
@WebFilter(urlPatterns = "/*")
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
// 设置CORS头部
httpResponse.setHeader("Access-Control-Allow-Origin", "*");
httpResponse.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
httpResponse.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With");
httpResponse.setHeader("Access-Control-Max-Age", "3600");
// 处理预检请求
HttpServletRequest httpRequest = (HttpServletRequest) request;
if ("OPTIONS".equalsIgnoreCase(httpRequest.getMethod())) {
httpResponse.setStatus(HttpServletResponse.SC_OK);
return;
}
// 继续执行过滤器链
chain.doFilter(request, response);
}
}
5. 请求限流
场景描述
对API请求进行限流控制,防止恶意攻击和系统过载。
实现示例
@WebFilter(urlPatterns = "/api/*")
public class RateLimitFilter implements Filter {
private final Map<String, RateLimiter> limiters = new ConcurrentHashMap<>();
private final int maxRequestsPerMinute = 100;
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String clientIp = getClientIp(httpRequest);
// 获取或创建限流器
RateLimiter limiter = limiters.computeIfAbsent(clientIp,
k -> RateLimiter.create(maxRequestsPerMinute / 60.0)); // 每秒允许的请求数
// 尝试获取令牌
if (!limiter.tryAcquire()) {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setStatus(HttpServletResponse.SC_TOO_MANY_REQUESTS);
httpResponse.getWriter().write("请求过于频繁,请稍后再试");
return;
}
// 继续执行过滤器链
chain.doFilter(request, response);
}
private String getClientIp(HttpServletRequest request) {
String xForwardedFor = request.getHeader("X-Forwarded-For");
if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
return xForwardedFor.split(",")[0].trim();
}
return request.getRemoteAddr();
}
}
6. 响应压缩
场景描述
对响应内容进行压缩,减少网络传输量,提高页面加载速度。
实现示例
@WebFilter(urlPatterns = "/*")
public class CompressionFilter implements Filter {
private static final Set<String> COMPRESSIBLE_TYPES = Set.of(
"text/html", "text/xml", "text/plain", "text/css", "text/javascript",
"application/json", "application/xml", "application/javascript"
);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// 检查是否支持压缩
String acceptEncoding = httpRequest.getHeader("Accept-Encoding");
if (acceptEncoding == null || !acceptEncoding.contains("gzip")) {
chain.doFilter(request, response);
return;
}
// 检查内容类型是否可压缩
String contentType = httpResponse.getContentType();
if (contentType == null || !isCompressible(contentType)) {
chain.doFilter(request, response);
return;
}
// 创建压缩响应包装器
GZIPResponseWrapper gzipResponse = new GZIPResponseWrapper(httpResponse);
try {
// 继续执行过滤器链
chain.doFilter(request, gzipResponse);
} finally {
// 完成压缩
gzipResponse.finish();
}
}
private boolean isCompressible(String contentType) {
return COMPRESSIBLE_TYPES.stream().anyMatch(contentType::contains);
}
}
// GZIP响应包装器
class GZIPResponseWrapper extends HttpServletResponseWrapper {
private GZIPOutputStream gzipOutputStream;
private PrintWriter printWriter;
public GZIPResponseWrapper(HttpServletResponse response) {
super(response);
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
if (gzipOutputStream == null) {
gzipOutputStream = new GZIPOutputStream(getResponse().getOutputStream());
}
return new ServletOutputStream() {
@Override
public boolean isReady() {
return true;
}
@Override
public void setWriteListener(WriteListener writeListener) {
}
@Override
public void write(int b) throws IOException {
gzipOutputStream.write(b);
}
};
}
@Override
public PrintWriter getWriter() throws IOException {
if (printWriter == null) {
printWriter = new PrintWriter(new OutputStreamWriter(gzipOutputStream, "UTF-8"));
}
return printWriter;
}
public void finish() throws IOException {
if (printWriter != null) {
printWriter.close();
}
if (gzipOutputStream != null) {
gzipOutputStream.close();
}
}
}
面试高频点
1. 基础概念
Q1: 什么是Java过滤器?它的作用是什么?
A:
- 定义: Java过滤器是Java Web应用中的一个组件,用于在请求到达Servlet之前和响应返回客户端之前进行预处理和后处理
- 作用:
- 请求拦截和预处理
- 响应后处理
- 身份认证和授权
- 日志记录
- 字符编码处理
- 跨域处理等
Q2: 过滤器的生命周期是什么?
A:
// 过滤器生命周期
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 1. 初始化阶段:Web应用启动时调用一次
// 用于初始化过滤器资源
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 2. 过滤阶段:每次请求都会调用
// 前置处理
chain.doFilter(request, response);
// 后置处理
}
@Override
public void destroy() {
// 3. 销毁阶段:Web应用关闭时调用一次
// 用于释放过滤器资源
}
}
Q3: 过滤器链的执行顺序是怎样的?
A:
- 执行顺序: 按照web.xml中配置的顺序,或者@WebFilter注解的order属性值
- 执行流程:
- 过滤器1前置处理 → 过滤器2前置处理 → … → Servlet处理 → 过滤器N后置处理 → … → 过滤器1后置处理
- 关键点: 前置处理按顺序执行,后置处理按逆序执行
2. 配置和实现
Q4: 如何配置过滤器?有哪些配置方式?
A:
// 方式1: 注解配置
@WebFilter(
urlPatterns = "/*",
filterName = "myFilter",
initParams = {
@WebInitParam(name = "encoding", value = "UTF-8")
},
order = 1
)
public class MyFilter implements Filter {
// 过滤器实现
}
// 方式2: web.xml配置
<filter>
<filter-name>myFilter</filter-name>
<filter-class>com.example.MyFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Q5: 如何实现一个自定义过滤器?
A:
public class CustomFilter implements Filter {
private String filterName;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterName = filterConfig.getFilterName();
System.out.println("过滤器 " + filterName + " 初始化完成");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// 前置处理
System.out.println("过滤器 " + filterName + " 开始处理请求: " + httpRequest.getRequestURI());
// 调用下一个过滤器或Servlet
chain.doFilter(request, response);
// 后置处理
System.out.println("过滤器 " + filterName + " 完成处理请求: " + httpRequest.getRequestURI());
}
@Override
public void destroy() {
System.out.println("过滤器 " + filterName + " 销毁完成");
}
}
3. 高级特性
Q6: 过滤器与拦截器的区别是什么?
A:
对比项 | Filter | Interceptor |
---|---|---|
实现方式 | 基于函数回调 | 基于反射机制 |
使用范围 | 只能用于Web应用 | 可以用于Web应用、Spring应用等 |
获取资源 | 只能获取ServletContext | 可以获取Spring容器中的资源 |
执行顺序 | 在Interceptor之前执行 | 在Filter之后执行 |
异常处理 | 不能使用Spring的异常处理机制 | 可以使用Spring的异常处理机制 |
Q7: 如何实现过滤器的动态配置?
A:
public class DynamicFilter implements Filter {
private volatile FilterConfig filterConfig;
private volatile Map<String, String> dynamicParams = new ConcurrentHashMap<>();
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
// 加载初始配置
loadDynamicParams();
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 动态获取配置参数
String maxRequests = getDynamicParam("maxRequests", "100");
String timeWindow = getDynamicParam("timeWindow", "60");
// 根据动态配置执行逻辑
// ...
chain.doFilter(request, response);
}
private String getDynamicParam(String key, String defaultValue) {
return dynamicParams.getOrDefault(key, defaultValue);
}
private void loadDynamicParams() {
// 从数据库或配置文件加载动态参数
// 可以通过定时任务或事件驱动方式更新
}
// 提供动态更新配置的方法
public void updateConfig(String key, String value) {
dynamicParams.put(key, value);
}
}
4. 性能优化
Q8: 如何优化过滤器的性能?
A:
public class OptimizedFilter implements Filter {
// 1. 使用缓存减少重复计算
private final Map<String, Object> cache = new ConcurrentHashMap<>();
// 2. 使用线程安全的集合
private final Set<String> excludeUrls = ConcurrentHashMap.newKeySet();
// 3. 避免在doFilter中创建大量对象
private final ThreadLocal<SimpleDateFormat> dateFormat =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String requestURI = httpRequest.getRequestURI();
// 4. 快速路径:排除不需要处理的URL
if (excludeUrls.contains(requestURI)) {
chain.doFilter(request, response);
return;
}
// 5. 使用缓存减少重复计算
String cacheKey = "auth_" + requestURI;
Boolean isAuthorized = (Boolean) cache.get(cacheKey);
if (isAuthorized == null) {
isAuthorized = checkAuthorization(httpRequest, requestURI);
cache.put(cacheKey, isAuthorized);
}
if (!isAuthorized) {
((HttpServletResponse) response).setStatus(HttpServletResponse.SC_FORBIDDEN);
return;
}
chain.doFilter(request, response);
}
private boolean checkAuthorization(HttpServletRequest request, String uri) {
// 权限检查逻辑
return true;
}
}
Q9: 如何处理过滤器中的异常?
A:
public class ExceptionHandlingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
try {
// 执行过滤器逻辑
chain.doFilter(request, response);
} catch (AuthenticationException e) {
// 处理认证异常
handleAuthenticationException((HttpServletResponse) response, e);
} catch (AuthorizationException e) {
// 处理授权异常
handleAuthorizationException((HttpServletResponse) response, e);
} catch (Exception e) {
// 处理其他异常
handleGenericException((HttpServletResponse) response, e);
}
}
private void handleAuthenticationException(HttpServletResponse response, AuthenticationException e)
throws IOException {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"error\":\"认证失败\",\"message\":\"" + e.getMessage() + "\"}");
}
private void handleAuthorizationException(HttpServletResponse response, AuthorizationException e)
throws IOException {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"error\":\"权限不足\",\"message\":\"" + e.getMessage() + "\"}");
}
private void handleGenericException(HttpServletResponse response, Exception e)
throws IOException {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"error\":\"系统异常\",\"message\":\"请稍后重试\"}");
// 记录日志
logger.error("过滤器执行异常", e);
}
}
5. 实际应用
Q10: 在Spring Boot中如何使用过滤器?
A:
// 方式1: 实现Filter接口
@Component
public class SpringBootFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 过滤器逻辑
chain.doFilter(request, response);
}
}
// 方式2: 使用FilterRegistrationBean配置
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<MyFilter> myFilterRegistration() {
FilterRegistrationBean<MyFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new MyFilter());
registration.addUrlPatterns("/*");
registration.setOrder(1);
registration.setName("myFilter");
return registration;
}
}
// 方式3: 使用@WebFilter注解
@WebFilter(urlPatterns = "/*")
public class WebFilter implements Filter {
// 过滤器实现
}
最佳实践
1. 过滤器设计原则
// 单一职责原则:每个过滤器只负责一个功能
public class AuthenticationFilter implements Filter {
// 只负责身份认证
}
public class LoggingFilter implements Filter {
// 只负责日志记录
}
public class EncodingFilter implements Filter {
// 只负责字符编码
}
// 开闭原则:对扩展开放,对修改关闭
public abstract class AbstractFilter implements Filter {
protected abstract void doPreFilter(HttpServletRequest request, HttpServletResponse response);
protected abstract void doPostFilter(HttpServletRequest request, HttpServletResponse response);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// 前置处理
doPreFilter(httpRequest, httpResponse);
// 继续执行过滤器链
chain.doFilter(request, response);
// 后置处理
doPostFilter(httpRequest, httpResponse);
}
}
2. 性能优化实践
public class PerformanceOptimizedFilter implements Filter {
// 使用缓存减少重复计算
private final LoadingCache<String, Boolean> permissionCache = Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(5, TimeUnit.MINUTES)
.build(key -> checkPermission(key));
// 使用线程池处理异步任务
private final ExecutorService executorService = Executors.newFixedThreadPool(10);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String requestURI = httpRequest.getRequestURI();
// 异步记录访问日志
executorService.submit(() -> logAccess(requestURI));
// 使用缓存检查权限
String cacheKey = getCacheKey(httpRequest);
Boolean hasPermission = permissionCache.get(cacheKey);
if (!hasPermission) {
((HttpServletResponse) response).setStatus(HttpServletResponse.SC_FORBIDDEN);
return;
}
chain.doFilter(request, response);
}
private String getCacheKey(HttpServletRequest request) {
return request.getRemoteAddr() + ":" + request.getRequestURI();
}
private boolean checkPermission(String key) {
// 权限检查逻辑
return true;
}
private void logAccess(String uri) {
// 异步记录访问日志
logger.info("访问URI: {}", uri);
}
}
3. 异常处理实践
public class RobustFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(RobustFilter.class);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
try {
// 前置处理
doPreProcess(request, response);
// 执行过滤器链
chain.doFilter(request, response);
// 后置处理
doPostProcess(request, response);
} catch (Exception e) {
// 异常处理
handleException(request, response, e);
}
}
private void doPreProcess(ServletRequest request, ServletResponse response) {
// 前置处理逻辑
}
private void doPostProcess(ServletRequest request, ServletResponse response) {
// 后置处理逻辑
}
private void handleException(ServletRequest request, ServletResponse response, Exception e) {
logger.error("过滤器执行异常", e);
// 根据异常类型进行不同处理
if (e instanceof SecurityException) {
handleSecurityException(response, (SecurityException) e);
} else if (e instanceof IllegalArgumentException) {
handleIllegalArgumentException(response, (IllegalArgumentException) e);
} else {
handleGenericException(response, e);
}
}
private void handleSecurityException(ServletResponse response, SecurityException e) {
// 处理安全异常
try {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
httpResponse.getWriter().write("访问被拒绝");
} catch (IOException ex) {
logger.error("写入响应失败", ex);
}
}
private void handleIllegalArgumentException(ServletResponse response, IllegalArgumentException e) {
// 处理参数异常
try {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST);
httpResponse.getWriter().write("请求参数错误");
} catch (IOException ex) {
logger.error("写入响应失败", ex);
}
}
private void handleGenericException(ServletResponse response, Exception e) {
// 处理通用异常
try {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
httpResponse.getWriter().write("系统内部错误");
} catch (IOException ex) {
logger.error("写入响应失败", ex);
}
}
}
总结
1. 学习路径图
2. 核心优势
优势 | 说明 |
---|---|
请求拦截 | 在请求到达Servlet之前进行拦截和处理 |
响应处理 | 在响应返回客户端之前进行处理 |
链式调用 | 多个过滤器可以组成过滤器链,按顺序执行 |
生命周期管理 | 由容器管理,随Web应用启动而创建,随应用关闭而销毁 |
配置灵活 | 支持注解配置和web.xml配置两种方式 |
3. 适用场景
场景 | 说明 |
---|---|
身份认证 | 用户登录验证、权限控制 |
日志记录 | 请求日志、访问统计 |
字符编码 | 统一处理请求和响应编码 |
跨域处理 | 设置CORS头部信息 |
请求限流 | 防止恶意攻击和系统过载 |
响应压缩 | 减少网络传输量 |
4. 注意事项
注意点 | 说明 |
---|---|
执行顺序 | 注意过滤器的执行顺序,前置处理按顺序,后置处理按逆序 |
异常处理 | 合理处理过滤器中的异常,避免影响正常业务流程 |
性能考虑 | 避免在过滤器中执行耗时操作,合理使用缓存 |
资源管理 | 及时释放过滤器占用的资源,避免内存泄漏 |
5. 学习建议
通过系统学习Java过滤器,可以:
- 理解Web架构 - 深入理解Web应用的请求处理流程
- 掌握设计模式 - 学习责任链、装饰器等设计模式的实际应用
- 提升开发能力 - 掌握Web应用开发中的常见问题解决方案
- 增强系统性能 - 学会使用过滤器优化系统性能
实践建议: 在实际项目中多应用过滤器技术,通过实践加深理解,逐步掌握其高级特性和最佳实践。记住:过滤器是Web应用的重要基础设施,合理使用可以大大提升系统的健壮性和性能。
更多推荐
所有评论(0)