Spring MVC过滤器-超类

本文解析了Spring框架中三种过滤器的实现方式:GenericFilterBean、OncePerRequestFilter和AbstractRequestLoggingFilter。阐述了它们之间的继承关系及各自的功能,帮助读者理解如何在Spring中自定义过滤器。

        Spring的web包中中有很多过滤器,这些过滤器位于org.springframework.web.filter并且理所当然地实现了javax.servlet.Filter,不过实现的方式有以下几类:

        (1) 直接实现Filter,这一类过滤器只有CompositeFilter;

        (2) 继承抽象类GenericFilterBean,该类实现了javax.servlet.Filter,这一类的过滤器只有一个,即DelegatingFilterProxy;

        (3) 继承抽象类OncePerRequestFilter,该类为GenericFilterBean的直接子类,这一类过滤器包括CharacterEncodingFilter、HiddenHttpMethodFilter、HttpPutFormContentFilter、RequestContextFilter和ShallowEtagHeaderFilter;

        (4) 继承抽象类AbstractRequestLoggingFilter,该类为OncePerRequestFilter的直接子类,这一类过滤器包括CommonsRequestLoggingFilter、Log4jNestedDiagnosticContextFilter和ServletContextRequestLoggingFilter。

        本文要讲述的,即是GenericFilterBean、OncePerRequestFilter和AbstractRequestLoggingFilter。

        GenericFilterBean

        抽象类GenericFilterBean实现了javax.servlet.Filter、org.springframework.beans.factory.BeanNameAware、org.springframework.context.EnvironmentAware、org.springframework.web.context.ServletContextAware、org.springframework.beans.factory.InitializingBean和org.springframework.beans.factory.DisposableBean五个接口,作用如下:

        (1) Filter,实现过滤器;

        (2) BeanNameAware,实现该接口的setBeanName方法,便于Bean管理器生成Bean;

        (3) EnvironmentAware,实现该接口的setEnvironment方法,指明该Bean运行的环境;

        (4) ServletContextAware,实现该接口的setServletContextAware方法,指明上下文;

        (5) InitializingBean,实现该接口的afterPropertiesSet方法,指明设置属性生的操作;

        (6) DisposableBean,实现该接口的destroy方法,用于回收资源。

        GenericFilterBean的工作流程是:init-doFilter-destory,其中的init和destory在该类中实现,doFilter在具体实现类中实现。init的代码如下:

/**
	 * Standard way of initializing this filter.
	 * Map config parameters onto bean properties of this filter, and
	 * invoke subclass initialization.
	 * @param filterConfig the configuration for this filter
	 * @throws ServletException if bean properties are invalid (or required
	 * properties are missing), or if subclass initialization fails.
	 * @see #initFilterBean
	 */
	public final void init(FilterConfig filterConfig) throws ServletException {
		Assert.notNull(filterConfig, "FilterConfig must not be null");
		if (logger.isDebugEnabled()) {
			logger.debug("Initializing filter '" + filterConfig.getFilterName() + "'");
		}

		this.filterConfig = filterConfig;

		// Set bean properties from init parameters.
		try {
			PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);
			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
			ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());
			bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment));
			initBeanWrapper(bw);
			bw.setPropertyValues(pvs, true);
		}
		catch (BeansException ex) {
			String msg = "Failed to set bean properties on filter '" +
			    filterConfig.getFilterName() + "': " + ex.getMessage();
			logger.error(msg, ex);
			throw new NestedServletException(msg, ex);
		}

		// Let subclasses do whatever initialization they like.
		initFilterBean();

		if (logger.isDebugEnabled()) {
			logger.debug("Filter '" + filterConfig.getFilterName() + "' configured successfully");
		}
	}

        该方法来自于javax.servlet.Filter,即过滤器的初始化,它的主要工作集中于以下几行代码:

// 从properties文件中获取值,这里是web.xml
PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);
// 设置bean适配器
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
// 设置上下文,这里的servletContext的设定继承自ServletContextAware的setter
ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());
// 将上下文信息和环境信息设置到bean适配器中,这里的environment来自于EnvironmentAware的setter
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment));
// 初始化bean适配器
initBeanWrapper(bw);
// 将从properties中获取的资源放置到bean适配器
bw.setPropertyValues(pvs, true);
// 初始化bean
initFilterBean();

        其中initFilterBean方法在两个位置起作用,一处是上文所述的init方法,另一处是afterPropertiesSet方法,在调用该方法前,需要保证用于Filter的所有的bean都已被设置,该方法由子类实现。

        GenericFilterBean中包含一个内部私有类FilterConfigPropertyValues,主要用于将web.xml中定义的init-param的值取出。

        OncePerRequestFilter

        抽象类oncePerRequestFilter继承自GenericFilterBean,它保留了GenericFilterBean中的所有方法并对之进行了扩展,在oncePerRequestFilter中的主要方法是doFilter,代码如下:

/**
	 * This <code>doFilter</code> implementation stores a request attribute for
	 * "already filtered", proceeding without filtering again if the
	 * attribute is already there.
	 * @see #getAlreadyFilteredAttributeName
	 * @see #shouldNotFilter
	 * @see #doFilterInternal
	 */
	public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
			throw new ServletException("OncePerRequestFilter just supports HTTP requests");
		}
		HttpServletRequest httpRequest = (HttpServletRequest) request;
		HttpServletResponse httpResponse = (HttpServletResponse) response;
                // 调用GenericFilterBean的getFilterName方法返回已过滤的属性名
		String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
		if (request.getAttribute(alreadyFilteredAttributeName) != null || shouldNotFilter(httpRequest)) {
			// 未调用该过滤器或已过滤
			filterChain.doFilter(request, response);
		}
		else {
			// 进行过滤
			request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
			try {
				doFilterInternal(httpRequest, httpResponse, filterChain);
			}
			finally {
				// Remove the "already filtered" request attribute for this request.
				request.removeAttribute(alreadyFilteredAttributeName);
			}
		}
	}

        在doFilter方法中,doFilterInternal方法由子类实现,主要作用是规定过滤的具体方法。

        AbstractRequestLoggingFilter

        AbstractRequestLoggingFilter继承了OncePerRequestFilter并实现了其doFilterInternal方法,该方法代码如下:

/**
	 * Forwards the request to the next filter in the chain and delegates down to the subclasses to perform the actual
	 * request logging both before and after the request is processed.
	 *
	 * @see #beforeRequest
	 * @see #afterRequest
	 */
	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
		if (isIncludePayload()) {
                        // 若日志中包含负载,则重置request
			request = new RequestCachingRequestWrapper(request);
		}
                // 过滤前执行的方法
		beforeRequest(request, getBeforeMessage(request));
		try {
                        // 执行过滤
			filterChain.doFilter(request, response);
		}
		finally {
                        // 过滤后执行的方法
			afterRequest(request, getAfterMessage(request));
		}
	}

        doFilter方法中的beforeRequest和afterRequest方法由子类实现,RequestCachingRequestWrapper为AbstractRequestLoggingFilter的内部内,主要作用是重置request。

        区别

         我们在使用过滤器时,通常没必要知道GenericFilterBean、OncePerRequestFilter和AbstractRequestLoggingFilter,但不防碍我们了解这几个类,就上文所述,AbstractRequestLoggingFilter继承自OncePerRequestFilter,OncePerRequestFilter继承自GenericFilterBean,所以我们知道,genericFilterBean是任何类型的过滤器的一个比较方便的超类,这个类主要实现的就是从web.xml文件中取得init-param中设定的值,然后对Filter进行初始化(当然,其子类可以覆盖init方法)。

         OncePerRequestFilter继承自GenericFilterBean,那么它自然知道怎么去获取配置文件中的属性及其值,所以其重点不在于取值,而在于确保在接收到一个request后,每个filter只执行一次,它的子类只需要关注Filter的具体实现即doFilterInternal。

        AbstractRequestLoggingFilter是对OncePerRequestFilter的扩展,它除了遗传了其父类及祖先类的所有功能外,还在doFilterInternal中决定了在过滤之前和之后执行的事件,它的子类关注的是beforeRequest和afterRequest。

        总体来说,这三个类分别执行了Filter的某部分功能,当然,具体如何执行由它们的子类规定,若你需要实现自己的过滤器,也可以根据上文所述继承你所需要的类。

<think>我们正在讨论软件开发中常见的两种命名:Processor 和 Handler。根据引用内容,我们可以总结如下: 引用[1]提到: - HandlerMapping 根据 request 找到相应的处理器(Handler)。 - Handler 有两种形式:基于的 Handler 和基于方法的 Handler(如常用的 Controller 方法)。 引用[2]提到: - Processor 提供文件扫描循环(核心处理逻辑),而具体的转换(如大写转换)和装饰(如HTML)通过继承和组合实现。 引用[3]提到: - 一个(DataHandler)可以多重继承多个(Logger, Validator, Processor),并遵循特定的方法查找顺序。 因此,我们可以对 Processor 和 Handler 进行如下区分: ### Processor(处理器) 1. **核心职责**:执行某种数据处理或转换,通常封装了具体的业务逻辑或算法。 2. **特点**: - 关注数据的输入、处理和输出(如文件扫描、数据转换)。 - 通常具有明确的处理步骤(如 `process()` 方法)。 3. **示例**: - 引用[2]中的 `Processor` 负责文件扫描循环,而具体的转换逻辑由子实现。 - 常见场景:数据转换、批处理任务、算法执行。 ### Handler(处理器) 1. **核心职责**:响应外部事件或请求,并协调其他组件完成操作。 2. **特点**: - 关注事件/请求的接收、路由和响应(如 HTTP 请求处理)。 - 通常与框架或系统级交互相关(如 MVC 中的 Controller)。 3. **示例**: - 引用[1]中的 `Handler` 处理用户请求,并由 `HandlerMapping` 路由到具体方法。 - 常见场景:事件驱动架构、Web 请求处理、消息队列消费。 --- ### 对比表格 | **特性** | **Processor** | **Handler** | |------------------|----------------------------------------|--------------------------------------| | **核心目标** | 数据处理/转换 | 事件/请求响应 | | **典型方法** | `process()`, `transform()` | `handle()`, `onEvent()` | | **依赖关系** | 依赖输入数据,输出处理结果 | 依赖事件源,输出响应或状态变更 | | **常见应用场景** | 批处理、数据流水线、算法实现 | Web请求处理、消息监听、事件回调 | | **设计模式** | 模板方法、策略模式 | 观察者模式、责任链模式 | --- ### 代码示例 #### Processor 示例(数据转换) ```java public abstract class DataProcessor { // 模板方法:定义处理流程 public final void process(Data input) { validate(input); Data transformed = transform(input); save(transformed); } protected abstract void validate(Data data); protected abstract Data transform(Data data); private void save(Data data) { /* 存储逻辑 */ } } // 具体实现:大写转换处理器 public class UppercaseProcessor extends DataProcessor { @Override protected void validate(Data data) { /* 验证逻辑 */ } @Override protected Data transform(Data data) { String content = data.getContent().toUpperCase(); return new Data(content); } } ``` #### Handler 示例(HTTP请求处理) ```java public class HttpRequestHandler { // 处理GET请求 public void handleGetRequest(HttpRequest request) { authenticate(request); String data = fetchData(request); sendResponse(200, data); } private void authenticate(HttpRequest request) { /* 认证逻辑 */ } private String fetchData(HttpRequest request) { /* 数据查询逻辑 */ } private void sendResponse(int status, String body) { /* 响应逻辑 */ } } ``` --- ### 设计模式关联 1. **Processor 常用模式**: - **模板方法模式**(引用[2]):`Processor` 定义处理流程,子实现具体步骤。 - **策略模式**:不同 `Processor` 实现可互换的转换逻辑。 2. **Handler 常用模式**: - **责任链模式**(引用[1]):多个 `Handler` 组成链,按顺序处理请求。 - **观察者模式**:`Handler` 监听事件并触发响应。 --- ### 实际应用场景 1. **Processor 场景**: - 日志数据清洗流水线(过滤、脱敏、聚合)。 - 图像处理(缩放、压缩、格式转换)。 2. **Handler 场景**: - Spring MVC 中的 `@Controller` 处理 HTTP 请求[^1]。 - 消息中间件(如 Kafka)的消息监听器。 --- ### 命名建议 -**主要关注数据转换**时,使用 `Processor`(如 `ImageProcessor`)。 -**主要关注事件/请求响应**时,使用 `Handler`(如 `PaymentHandler`)。 > **注意**:实际项目中两者可能重叠(如 `RequestProcessor` 同时处理请求和数据转换),此时应根据核心职责命名。 §§相关问题§§ 1. 在 Spring 框架中,`Handler` 和 `Controller` 有何区别? 2. 如何避免 `Processor` 因状态管理导致的线程安全问题?[^4] 3. 责任链模式在 `Handler` 设计中的具体实现方式? 4. 模板方法模式如何优化 `Processor` 的扩展性?</think>### Processor 和 Handler 命名的区别与用途 #### 📌 Processor 的核心特性 1. **数据处理为核心** Processor 专注于**数据转换和处理逻辑**,通常实现数据加工流水线: ```java class DataProcessor { public Data process(Data input) { // 数据清洗 → 转换 → 聚合 return transformedData; } } ``` - 典型场景:ETL 流程、文件解析、算法执行 - 设计特征:无状态(引用[4]),适合模板方法模式(引用[2]) 2. **继承驱动的扩展** 通过继承实现功能增强(引用[2][3]): ``` BasicProcessor → HTMLProcessor → CustomProcessor ``` 3. **批处理导向** 常包含循环处理结构(引用[2][5]): ```c while(ProcessorGet() != STATUS_FAIL) { // 引用[5] process_data(); } ``` #### 📌 Handler 的核心特性 1. **事件响应为核心** Handler 专注于**事件/请求的调度与响应**: ```java class RequestHandler { public Response handle(Request event) { // 路由 → 验证 → 执行 → 返回 return action.execute(); } } ``` - 典型场景:HTTP 请求处理、消息队列消费 - 设计特征:常结合责任链模式(引用[1]) 2. **组合优于继承** 更倾向使用组合集成功能(引用[2]): ``` Handler = Logger + Validator + Processor ``` 3. **动态路由能力** 支持运行时方法映射(引用[1]): ```java Map<String, Handler> handlers = new HashMap<>(); handlers.put("/api", new ApiHandler()); ``` --- ### 关键区别对比表 | **维度** | **Processor** | **Handler** | |-------------------|-----------------------------------|-----------------------------------| | **核心职责** | 数据转换/加工 | 事件调度/响应 | | **设计模式** | 模板方法模式(引用[2]) | 责任链模式(引用[1]) | | **扩展方式** | 继承为主(引用[3]) | 组合为主(引用[2]) | | **状态管理** | 严格无状态(引用[4]) | 可能包含会话状态 | | **典型方法名** | `process()`, `transform()` | `handle()`, `onEvent()` | | **执行模式** | 批处理循环(引用[5]) | 事件驱动 | | **线程安全要求** | 必须线程安全(引用[4]) | 常绑定特定线程 | --- ### 典型应用场景 #### Processor 适用场景 1. **数据流水线** ```python # 图像处理流水线 image_processor = ( ImageLoader() .pipe(ResizeProcessor()) .pipe(FilterProcessor()) ) ``` 2. **批处理系统** ```java // 银行交易批量处理 while(batchProcessor.hasNext()) { Transaction t = batchProcessor.next(); t.process(); // 无状态处理 } ``` #### Handler 适用场景 1. **Web 请求处理** ```java // Spring MVC 控制器 @RestController class UserHandler { @PostMapping("/users") public Response handleCreateUser(Request req) { // 组合验证器+处理器 } } ``` 2. **事件驱动架构** ```typescript // 消息队列监听 messageQueue.on("payment", (event) => { paymentHandler.handle(event); }); ``` --- ### 设计原则总结 1. **Processor 设计要点** - 保持纯函数特性(相同输入→相同输出) - 避免共享状态(使用 ThreadLocal 如需状态,引用[4]) - 通过继承实现变体(引用[3]) 2. **Handler 设计要点** - 采用"单一事件原则"(每个 Handler 只处理一事件) - 使用组合构建功能链(引用[2]): ```mermaid graph LR A[Handler] --> B[Logger] A --> C[Validator] A --> D[Processor] ``` - 支持动态路由配置(引用[1]) > 💡 **经验法则**:当包含 `processX()` 方法时命名为 Processor,包含 `handleX()` 方法时命名为 Handler。两者常协同工作:Handler 接收请求后委托给 Processor 执行具体逻辑(引用[2])。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值