- [SpringBoot 优雅处理金额格式化:拦截器+自定义注解方案](#SpringBoot 优雅处理金额格式化:拦截器+自定义注解方案)

SpringBoot 优雅处理金额格式化:拦截器+自定义注解方案
在金融、电商等涉及金额的项目中,我们经常需要对 BigDecimal 类型的金额字段统一保留指定小数位数。如果在每个业务方法中手动调用 setScale 方法,会造成大量代码冗余,且难以统一维护。
本文将介绍一种基于 SpringBoot 拦截器 + 自定义注解 的方案,实现金额字段的自动格式化,让代码更简洁、更易维护。
一、核心思路
- 定义一个自定义注解
@AmountFormat,用于标记需要格式化的金额字段,并支持指定小数位数。 - 编写一个拦截器
AmountFormatInterceptor,在请求处理阶段,通过反射扫描被注解标记的字段。 - 对扫描到的
BigDecimal类型字段,按照注解指定的小数位数进行格式化处理。 - 注册拦截器,配置拦截路径,实现全局生效。
二、实现步骤
1. 创建自定义注解 @AmountFormat
该注解用于标记需要格式化的金额字段,支持通过 scale 属性指定小数位数,默认保留2位小数。
java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 金额格式化注解,用于标记需要统一处理小数位数的字段
*/
@Retention(RetentionPolicy.RUNTIME) // 注解在运行时生效,允许反射获取
@Target(ElementType.FIELD) // 注解仅作用于类的字段
public @interface AmountFormat {
/**
* 保留小数位数,默认2位
*/
int scale() default 2;
}
2. 编写金额格式化拦截器
拦截器实现 HandlerInterceptor 接口,在 preHandle 方法中完成核心逻辑:扫描目标类的字段,对被 @AmountFormat 标记的 BigDecimal 字段进行格式化。
java
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* 金额格式化拦截器,自动处理被@AmountFormat标记的字段
*/
public class AmountFormatInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 只处理 Controller 中的方法(HandlerMethod 类型)
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 获取目标类的实例
Object targetBean = handlerMethod.getBean();
// 获取目标类的所有字段
Field[] fields = targetBean.getClass().getDeclaredFields();
for (Field field : fields) {
// 判断字段是否被@AmountFormat注解标记
if (field.isAnnotationPresent(AmountFormat.class)) {
// 设置私有字段可访问
field.setAccessible(true);
// 获取字段的值
Object fieldValue = field.get(targetBean);
// 仅处理BigDecimal类型的字段
if (fieldValue instanceof BigDecimal) {
AmountFormat annotation = field.getAnnotation(AmountFormat.class);
int scale = annotation.scale();
// 格式化金额:四舍五入,保留指定小数位数
BigDecimal formattedValue = ((BigDecimal) fieldValue).setScale(scale, RoundingMode.HALF_UP);
// 将格式化后的值设置回字段
field.set(targetBean, formattedValue);
}
}
}
}
// 返回true,继续执行后续拦截器和Controller方法
return true;
}
}
关键说明:
preHandle方法在 Controller 方法执行前调用,保证格式化逻辑优先执行。- 通过反射获取字段值时,需要调用
field.setAccessible(true)来访问私有字段。 - 采用
RoundingMode.HALF_UP模式进行四舍五入,符合日常金额计算的需求。
3. 注册拦截器并配置拦截路径
通过配置类 WebConfig 实现 WebMvcConfigurer 接口,注册拦截器并设置拦截范围。
java
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* Web 配置类,用于注册拦截器
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册金额格式化拦截器
registry.addInterceptor(new AmountFormatInterceptor())
// 拦截所有请求
.addPathPatterns("/**")
// 排除不需要拦截的路径(可选)
.excludePathPatterns("/static/**", "/error");
}
}
4. 在实体类字段上使用注解
在需要格式化的 BigDecimal 类型金额字段上添加 @AmountFormat 注解,即可实现自动格式化。
java
import java.math.BigDecimal;
/**
* 商品实体类
*/
public class Product {
private Long id;
private String productName;
// 金额字段:默认保留2位小数
@AmountFormat
private BigDecimal price;
// 金额字段:自定义保留3位小数
@AmountFormat(scale = 3)
private BigDecimal discountPrice;
// 省略 getter/setter 方法
}
三、拦截器执行时机说明
SpringMVC 拦截器有三个核心方法,各自的执行时机不同:
- preHandle:在 Controller 方法执行前调用。本文的格式化逻辑放在此方法,保证业务逻辑处理的是格式化后的金额。
- postHandle:在 Controller 方法执行后、视图渲染前调用。适合对处理结果进行二次加工。
- afterCompletion:在整个请求结束后调用。适合做资源清理等收尾工作。
四、注意事项
- 字段类型限制 :拦截器仅对
BigDecimal类型的字段生效,其他类型(如Double)不会处理。建议金额字段统一使用BigDecimal,避免精度丢失。 - 反射权限问题 :必须调用
field.setAccessible(true),否则无法访问实体类的私有字段,会抛出IllegalAccessException异常。 - 拦截路径配置 :通过
addPathPatterns和excludePathPatterns精准控制拦截范围,避免拦截静态资源、错误页面等不需要处理的请求。 - 四舍五入模式 :本文使用
RoundingMode.HALF_UP,如果业务需要其他舍入模式(如向下取整),可以在注解中新增属性配置。
五、方案优势
- 解耦性强 :格式化逻辑与业务逻辑完全分离,无需在业务代码中重复编写
setScale方法。 - 维护成本低 :如果需要调整小数位数,只需修改注解的
scale参数,无需改动大量业务代码。 - 全局生效:拦截器配置后,所有标记注解的字段都会自动格式化,实现统一管控。
六、总结
通过 拦截器 + 自定义注解 的组合,我们可以优雅地解决项目中金额字段格式化的问题。这种方案不仅减少了代码冗余,还提高了代码的可维护性,特别适合金融、电商等对金额精度要求高的项目。
在实际开发中,我们还可以基于此思路扩展更多功能,比如对日期字段的统一格式化、对敏感字段的脱敏处理等。