Spring HandlerInterceptor、Spring BeanPostProcessor、MyBatis TypeHandler

Spring HandlerInterceptor、BeanPostProcessor、MyBatis TypeHandler 行业级源码深度剖析与实战

一、引言

多接口+抽象类+责任链/策略模式是现代Java主流框架的基石。本文以Spring HandlerInterceptor、BeanPostProcessor、MyBatis TypeHandler为例,逐环节、逐行剖析其主流程、源码设计、内核机制、最佳实践与高阶应用。


二、主流程流程图与环节分布

MyBatis
TypeHandler.setParameter
Java对象
SQL执行
TypeHandler.getResult
Java对象返回
Spring Bean生命周期
BeanPostProcessor.postProcessBeforeInitialization
实例化Bean
初始化Bean
BeanPostProcessor.postProcessAfterInitialization
Bean注册完成
Spring MVC
HandlerInterceptor.preHandle
HTTP请求
Controller方法
HandlerInterceptor.postHandle
视图渲染
HandlerInterceptor.afterCompletion
响应返回

三、Spring HandlerInterceptor 源码剖析

3.1 责任链环节拆解

1. preHandle
方法签名
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
内部逻辑
  • 处理请求前的预处理逻辑(如鉴权、日志、限流)
  • 返回false则中断后续流程
源码片段与详细注释

HandlerInterceptor.java 源码

public interface HandlerInterceptor {
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        // 默认返回true,表示继续执行后续链路
        return true;
    }
    // 省略其他方法...
}
案例:登录拦截
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
    String token = request.getHeader("Authorization");
    if (token == null || !isValid(token)) {
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        return false;
    }
    return true;
}
速记口诀

“前置拦截做校验,false短路链路断。”


2. postHandle
方法签名
void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception;
内部逻辑
  • 控制器执行后,渲染视图前的处理(如数据加工、日志)
源码片段
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
        @Nullable ModelAndView modelAndView) throws Exception {
    // 默认空实现
}
案例:统一给视图添加全局属性
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                       ModelAndView modelAndView) {
    if (modelAndView != null) {
        modelAndView.addObject("globalVar", "value");
    }
}
速记口诀

“控制之后视图前,加工数据全局用。”


3. afterCompletion
方法签名
void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception;
内部逻辑
  • 请求处理完成后(包括异常),用于资源清理、异常统计
源码片段
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
        @Nullable Exception ex) throws Exception {
    // 默认空实现
}
案例:统计接口耗时
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
    long start = (Long)request.getAttribute("startTime");
    long duration = System.currentTimeMillis() - start;
    log.info("耗时: {}ms", duration);
}
速记口诀

“请求结束再清理,异常监控不可弃。”


3.4 行级源码剖析

以Spring的DispatcherServlet部分源码为例:

// org.springframework.web.servlet.DispatcherServlet#doDispatch
for (HandlerInterceptor interceptor : mappedHandler.getInterceptors()) {
    if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
        // 拦截器返回false,终止流程
        triggerAfterCompletion(...);
        return;
    }
}
// handler.invoke()
for (HandlerInterceptor interceptor : interceptors) {
    interceptor.postHandle(...);
}
triggerAfterCompletion(...);

注释:Spring内部遍历所有拦截器,依次调用preHandle,只要有一个返回false即短路。


3.5 参考资料


四、Spring BeanPostProcessor 源码剖析

4.1 生命周期钩子环节拆解

1. postProcessBeforeInitialization
方法签名
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
内部逻辑
  • Bean初始化方法调用前执行(@PostConstruct之前)
源码片段

BeanPostProcessor.java 源码

default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    return bean; // 默认返回原Bean
}
案例:自动代理
public Object postProcessBeforeInitialization(Object bean, String beanName) {
    if(bean instanceof SomeService) {
        // 包装为代理对象
        return Proxy.newProxyInstance(...);
    }
    return bean;
}
速记口诀

“初始化前可增强,返回原Bean最安全。”


2. postProcessAfterInitialization
方法签名
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
内部逻辑
  • Bean初始化之后执行(AOP代理点)
源码片段
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    return bean;
}
案例:AOP切面织入
public Object postProcessAfterInitialization(Object bean, String beanName) {
    if (需要AOP增强) {
        return aopProxy.createProxy(bean);
    }
    return bean;
}
速记口诀

“初始化后织切面,增强Bean最方便。”


4.3 行级源码剖析

以Spring Bean工厂源码片段为例:

// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean
Object wrappedBean = bean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
    wrappedBean = processor.postProcessBeforeInitialization(wrappedBean, beanName);
}
invokeInitMethods(beanName, wrappedBean, mbd);
for (BeanPostProcessor processor : getBeanPostProcessors()) {
    wrappedBean = processor.postProcessAfterInitialization(wrappedBean, beanName);
}

注释:Spring会链式调用所有BeanPostProcessor,前后两次,形成增强链。


4.4 参考资料


五、MyBatis TypeHandler 源码剖析

5.1 类型适配环节拆解

1. setParameter
方法签名
void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
内部逻辑
  • Java对象类型 -> JDBC参数
  • 负责将Java类型转为JDBC驱动支持的类型
源码片段

TypeHandler.java 源码

void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
案例:枚举转数据库int
public void setParameter(PreparedStatement ps, int i, OrderStatus parameter, JdbcType jdbcType) throws SQLException {
    ps.setInt(i, parameter.getCode());
}
速记口诀

“Java转JDBC,写入参数需适配。”


2. getResult
方法签名
T getResult(ResultSet rs, String columnName) throws SQLException;
T getResult(ResultSet rs, int columnIndex) throws SQLException;
T getResult(CallableStatement cs, int columnIndex) throws SQLException;
内部逻辑
  • JDBC类型 -> Java对象类型
  • 负责将数据库结果集转换为业务对象
源码片段
OrderStatus getResult(ResultSet rs, String columnName) throws SQLException {
    int code = rs.getInt(columnName);
    return OrderStatus.of(code);
}
速记口诀

“结果转Java,类型安全要把握。”


5.3 行级源码剖析

以MyBatis的BaseTypeHandler为例:

// org.apache.ibatis.type.BaseTypeHandler
@Override
public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
    if (parameter == null) {
        if (jdbcType == null) {
            throw new TypeException("...");
        }
        ps.setNull(i, jdbcType.TYPE_CODE);
    } else {
        setNonNullParameter(ps, i, parameter, jdbcType);
    }
}

注释:空值处理后委托setNonNullParameter,子类只需实现非空逻辑。


5.4 参考资料


六、调试与优化技巧

  • HandlerInterceptor:
    • 日志打印每个环节,链路顺序可通过@Order注解控制
  • BeanPostProcessor:
    • 仅对指定Bean增强,避免全局副作用
  • TypeHandler:
    • 明确类型映射,开启MyBatis日志追踪参数与结果

七、业务场景举例

  • HandlerInterceptor:API网关统一鉴权、灰度流量打标
  • BeanPostProcessor:自动注入分布式事务代理、统一监控埋点
  • TypeHandler:数据库JSON字段与Java对象互转、加密字段自动解密

八、设计模式与架构演进

  • 责任链模式:拦截器、处理器链式调用
  • 模板方法模式:抽象类提供默认实现,子类重写核心逻辑
  • 策略/适配器模式:TypeHandler多类型适配
  • 分布式演进:拦截器链与处理器链可分布式部署,支持幂等、降级

九、权威资料与参考文献


十、系统性总结&归纳

通过对Spring HandlerInterceptor、BeanPostProcessor、MyBatis TypeHandler源码主流程的每个环节细化剖析,我们掌握了多接口+抽象类+责任链/策略/模板方法等主流设计模式的精髓。理解其具体方法、源码实现、业务场景、扩展性与分布式演进,有助于在实际开发中打造高可维护、高扩展、高性能的企业级系统。


终极口诀:
“前拦截,后增强,类型适配链;接口分明易扩展,源码深究见真章。”


如需完整业务代码,请参考上述官方GitHub源码与文档。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北漂老男人

防秃基金【靠你的打赏续命】

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值