当前位置: 首页 > news >正文

深入剖析Spring Boot / Spring 应用中可自定义的扩展点

概览

  • Web 层:HandlerInterceptorWebMvcConfigurer / WebMvcConfigurationSupportHandlerMethodArgumentResolverHandlerMethodReturnValueHandlerControllerAdvice/@ExceptionHandlerResponseBodyAdviceViewResolverLocaleResolver、CORS 配置、Static Resource Handler。

  • Message conversion:HttpMessageConverterAbstractHttpMessageConverter)、MappingJackson2HttpMessageConverter 自定义 ObjectMapperConverter/Formatter

  • Servlet / Filter:OncePerRequestFilterFilterRegistrationBeanHttpServletRequestWrapperHttpServletResponseWrapperServletContextInitializer

  • Bean & 容器:BeanPostProcessorBeanFactoryPostProcessorBeanDefinitionRegistryPostProcessorApplicationContextInitializerApplicationListenerEnvironmentPostProcessor@Configuration + @Conditional 扩展。

  • AOP / 横切:自定义注解 + @AspectMethodInterceptor)、AdvisorProxyFactoryBean

  • 事务 / 安全:自定义 PlatformTransactionManager/TransactionInterceptor、Spring Security 的 filter chain (SecurityFilterChain) 与 OncePerRequestFilter 的结合点。

  • 嵌入式容器:WebServerFactoryCustomizer / ConfigurableServletWebServerFactoryTomcatContextCustomizerUndertowDeploymentInfoCustomizer

  • Actuator / 监控:HealthIndicatorInfoContributorMeterBinder、自定义端点(@Endpoint)。

  • 错误处理 / 全局响应:ErrorController / ErrorAttributes / @ControllerAdvice、自定义 BasicErrorController 覆盖。

  • 其他:任务执行器 (TaskExecutor / TaskScheduler)、缓存(CacheManager 自定义)、消息(MessageConverter)、Multipart(MultipartResolver)等。


一、Web 层扩展(最常用、影响最大)

1. HandlerInterceptor(HandlerInterceptor / HandlerInterceptorAdapter)

作用:在 Spring MVC Handler 执行前/执行后/完成后插入逻辑(类似 AOP 但作用于请求处理链)。
场景:鉴权/鉴权失败跳转、请求链日志、请求限流、统计耗时、请求上下文初始化(把 token -> user 放入 ThreadLocal)等。

示例:记录请求耗时并在 header 返回

public class TimingInterceptor implements HandlerInterceptor {private static final Logger log = LoggerFactory.getLogger(TimingInterceptor.class);private static final String START = "startTime";@Overridepublic boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) {req.setAttribute(START, System.nanoTime());return true; // 继续处理}@Overridepublic void afterCompletion(HttpServletRequest req, HttpServletResponse res, Object handler, Exception ex) {Long start = (Long) req.getAttribute(START);if (start != null) {long ms = (System.nanoTime() - start) / 1_000_000;res.addHeader("X-Processing-Time-ms", Long.toString(ms));log.info("{} {} took {} ms", req.getMethod(), req.getRequestURI(), ms);}}
}

注册(使用 WebMvcConfigurer):

@Configuration
public class WebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new TimingInterceptor()).addPathPatterns("/api/**");}
}

注意

  • HandlerInterceptor 在 Spring MVC 层;如果你想在 Filter 之前就生效,需用 Servlet Filter。

  • 顺序:可以通过 registry.addInterceptor(...).order(int)(Spring Boot 2.6+)或使用 Ordered 接口控制。


2. WebMvcConfigurer vs WebMvcConfigurationSupport

区别

  • WebMvcConfigurer:通过实现接口的方法“补充”Spring Boot 的自动化配置(推荐)。

  • WebMvcConfigurationSupport:继承并覆盖 Spring MVC 的核心配置,如果你直接继承/声明它,Spring Boot 的很多自动配置(例如 WebMvcAutoConfiguration)不会生效——等于“接管”全部 MVC 配置(除非你手动恢复那些 bean)。

建议大多数场景使用 WebMvcConfigurer;只有在需要完全定制、替换 Spring MVC 默认行为(并清楚代价)时才继承 WebMvcConfigurationSupport

示例(使用 WebMvcConfigurer 配置静态资源与消息转换):

@Configuration
public class MyWebConfig implements WebMvcConfigurer {@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/files/**").addResourceLocations("file:/var/data/files/");}@Overridepublic void extendMessageConverters(List<HttpMessageConverter<?>> converters) {converters.add(0, new MyCustomMessageConverter());}
}

:误用 WebMvcConfigurationSupport 导致 @EnableAutoConfiguration 的静态资源、jackson auto-config 等失效。


3. HandlerMethodArgumentResolver(方法参数解析器)

作用:自定义 Controller 方法中的参数注入(例如 @CurrentUser User user 从 token解析后注入)。
场景:把 token 解出来并提供给 Controller,或者把通用的请求上下文/分页参数自动解析为对象。

示例:

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface CurrentUser {}public class CurrentUserArgumentResolver implements HandlerMethodArgumentResolver {@Overridepublic boolean supportsParameter(MethodParameter param) {return param.hasParameterAnnotation(CurrentUser.class) && param.getParameterType().equals(User.class);}@Overridepublic Object resolveArgument(MethodParameter param, ModelAndViewContainer mav, NativeWebRequest request, WebDataBinderFactory factory) {String token = request.getHeader("Authorization");return TokenService.parseUser(token); // 你的逻辑}
}

注册:

@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {resolvers.add(new CurrentUserArgumentResolver());
}

4. HandlerMethodReturnValueHandler / ResponseBodyAdvice(返回值处理 / 响应增强)

  • HandlerMethodReturnValueHandler:在 controller 返回之前自定义如何写入 response(更底层)。

  • ResponseBodyAdvice:针对 @ResponseBody / @RestController 的返回体统一拦截/包装(最常用)。

示例(ResponseBodyAdvice):给统一 API 格式包一层 {"code":0,"data":...}

@ControllerAdvice
public class ApiResponseAdvice implements ResponseBodyAdvice<Object> {@Overridepublic boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {return true; // 或更精确的条件}@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,Class<? extends HttpMessageConverter<?>> selectedConverterType,ServerHttpRequest request, ServerHttpResponse response) {if (body instanceof ApiResult) return body;return ApiResult.success(body);}
}

5. ControllerAdvice / 全局异常处理

作用:集中处理 Controller 中抛出的异常并统一响应。常用类:@ControllerAdvice + @ExceptionHandler 或继承 ResponseEntityExceptionHandler

示例:

@ControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(BusinessException.class)public ResponseEntity<ApiError> handle(BusinessException ex) {return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ApiError(ex.getCode(), ex.getMessage()));}
}

二、消息转换

1. 自定义 HttpMessageConverter

作用:控制 Controller(@RequestBody/@ResponseBody)如何从/向 HTTP body 进行序列化与反序列化。Spring 提供 AbstractHttpMessageConverter<T> 便于实现。

示例:支持自定义 media-type application/x-person 的 converter(演示)

public class PersonMessageConverter extends AbstractHttpMessageConverter<Person> {public PersonMessageConverter() {super(new MediaType("application", "x-person", StandardCharsets.UTF_8));}@Overrideprotected boolean supports(Class<?> clazz) {return Person.class.isAssignableFrom(clazz);}@Overrideprotected Person readInternal(Class<? extends Person> clazz, HttpInputMessage input) throws IOException {String body = StreamUtils.copyToString(input.getBody(), StandardCharsets.UTF_8);// parse body -> Personreturn Person.parse(body);}@Overrideprotected void writeInternal(Person person, HttpOutputMessage output) throws IOException {output.getBody().write(person.toCustomFormat().getBytes(StandardCharsets.UTF_8));}
}

注册到 MVC:

@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {converters.add(0, new PersonMessageConverter());
}

场景:需要支持二进制协议、自定义文本协议、兼容遗留系统格式等。

2. 定制 Jackson / ObjectMapper

  • 通过 Jackson2ObjectMapperBuilderCustomizer 或在 Spring Boot 中配置 JacksonAutoConfiguration 提供的 ObjectMapper Bean 修改规则(日期格式、忽略空字段、自定义序列化器/反序列化器)。

  • 也可通过 MappingJackson2HttpMessageConverter 的替换或添加自定义 JsonSerializer

示例(自定义 ObjectMapper):

@Bean
public Jackson2ObjectMapperBuilderCustomizer customizer() {return builder -> builder.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS).modules(new JavaTimeModule());
}

三、Filter / Servlet API 扩展(底层请求拦截)

1. OncePerRequestFilter(Spring 提供的便利 Filter)

作用:确保过滤器在一次请求生命周期中仅执行一次(适合 Spring Security、日志、Tracing)。
使用场景:做 request scope context、trace id 注入、请求体缓存等。

示例(请求 ID):

public class RequestIdFilter extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain)throws ServletException, IOException {String id = UUID.randomUUID().toString();MDC.put("requestId", id);try {res.addHeader("X-Request-Id", id);chain.doFilter(req, res);} finally {MDC.remove("requestId");}}
}

注册(Spring Boot):

@Bean
public FilterRegistrationBean<RequestIdFilter> requestIdFilter() {FilterRegistrationBean<RequestIdFilter> fr = new FilterRegistrationBean<>(new RequestIdFilter());fr.setOrder(Ordered.HIGHEST_PRECEDENCE);fr.addUrlPatterns("/api/*");return fr;
}

2. HttpServletRequestWrapper / HttpServletResponseWrapper

作用:包装请求/响应以改变输入(比如读取多次 request body、修改 header、对参数进行清洗、实现 gzip 响应等)。

示例(读取请求体多次:缓存 body)简化:

public class CachedBodyHttpServletRequest extends HttpServletRequestWrapper {private byte[] cachedBody;public CachedBodyHttpServletRequest(HttpServletRequest request) throws IOException {super(request);InputStream is = request.getInputStream();this.cachedBody = StreamUtils.copyToByteArray(is);}@Overridepublic ServletInputStream getInputStream() {ByteArrayInputStream bais = new ByteArrayInputStream(cachedBody);return new DelegatingServletInputStream(bais);}@Overridepublic BufferedReader getReader() {return new BufferedReader(new InputStreamReader(getInputStream(), StandardCharsets.UTF_8));}
}

常用于:验证签名(先缓存 request body 做签名校验,再传给后续处理),或做 body 修改(对敏感字段脱敏)。

注意:避免缓存超大 body 导致 OOM;对大文件要使用流式处理或限制。


四、Bean & 容器级扩展(影响整个应用)

1. BeanPostProcessor(Bean 实例化前后拦截)

作用:可以在 bean 初始化前后做修改(AOP 代理注入、注解处理、字段注入替代等)。Spring Boot 内部有很多重要实现(例如 AutowiredAnnotationBeanPostProcessorConfigurationClassPostProcessor 等)。

示例:自动为带 @Loggable 注解的 bean 生成代理记录方法调用:

public class LoggableBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) {if (bean.getClass().isAnnotationPresent(Loggable.class)) {return ProxyFactory.getProxy(bean.getClass(), (MethodInterceptor) invocation -> {long s = System.nanoTime();try { return invocation.proceed(); } finally {System.out.println(invocation.getMethod().getName() + " took " + (System.nanoTime()-s)/1_000_000 + "ms");}});}return bean;}
}

注册:

@Bean
public static LoggableBeanPostProcessor loggableBeanPostProcessor() {return new LoggableBeanPostProcessor();
}

注意BeanPostProcessor 必须是 static @Bean(或提前注册),以确保其能在其它 bean 初始化前起作用;且不要在 post-process 中创建依赖链导致循环依赖。

2. BeanFactoryPostProcessor / BeanDefinitionRegistryPostProcessor

作用:在 Spring 创建 bean 定义后、实例化前修改 bean 定义(例如动态注册 bean、替换 ClassName、修改构造参数)。适用于框架级扩展或动态插件注册。

示例:动态注册某个 bean:

public class DynamicRegistrar implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {RootBeanDefinition bd = new RootBeanDefinition(MyDynamicService.class);registry.registerBeanDefinition("myDynamicService", bd);}@Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {}
}

3. ApplicationContextInitializer / ApplicationListener / EnvironmentPostProcessor

  • ApplicationContextInitializer:在 ApplicationContext refresh 前修改上下文(比如注册 property source)。

  • EnvironmentPostProcessor:在 Spring Boot 启动、Environment 准备阶段调整 Environment(常用于外部配置的加载)。

  • ApplicationListener<ApplicationReadyEvent>:监听启动事件做初始化工作(例如预加载缓存)。

示例(EnvironmentPostProcessor,在 META-INF/spring.factories 中注册):

public class MyEnvProcessor implements EnvironmentPostProcessor {@Overridepublic void postProcessEnvironment(ConfigurableEnvironment env, SpringApplication app) {env.getPropertySources().addFirst(new MapPropertySource("my", Map.of("my.prop", "value")));}
}

注意EnvironmentPostProcessor 运行非常早,bean 尚未创建;适合配置层面修改,不适合访问 bean。


五、AOP / 事务 / 安全 扩展

1. 自定义注解 + Aspect(切面)

场景:实现横切逻辑(记录、限流、幂等、重试、指标等)。

示例(@Timed 注解 + Aspect):

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Timed {}@Aspect
@Component
public class TimedAspect {@Around("@annotation(Timed)")public Object around(ProceedingJoinPoint p) throws Throwable {long s = System.nanoTime();try { return p.proceed(); } finally {long ms = (System.nanoTime()-s)/1_000_000;System.out.println("timed: " + ms + "ms");}}
}

2. 事务拦截器

  • 通过 @TransactionalTransactionInterceptor 配合 Advisor 自定义事务切面行为(例如定制回滚规则、超时、事务传播)。

  • 也可以在 PlatformTransactionManager 级别扩展(实现自定义资源管理器)。

3. Spring Security 扩展点

  • SecurityFilterChain(最新推荐)来自定义安全链;或写 OncePerRequestFilter 插入到 filter chain 内做自定义认证(例如 JWT 验证)。

  • 方法级安全通过 @PreAuthorize / MethodSecurityExpressionHandler 扩展 expression 的能力。


六、嵌入式服务器 & 容器自定义

1. WebServerFactoryCustomizer / ConfigurableServletWebServerFactory

作用:定制嵌入式容器(Tomcat/Undertow/Jetty)行为:端口、协议、线程池、ssl、context path、access log、session config 等。

示例(Tomcat 自定义):

@Bean
public WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> webServerFactoryCustomizer() {return factory -> {if (factory instanceof TomcatServletWebServerFactory tomcat) {tomcat.addContextCustomizers(context -> {context.setSessionTimeout(30); // minutes});}};
}

2. TomcatContextCustomizer / UndertowDeploymentInfoCustomizer

用于更细粒度定制(Valve、管理器、限制等),适合性能/安全/日志集成。


七、Actuator / 监控 / 运行时扩展

1. HealthIndicator / InfoContributor / MeterBinder

场景:将自定义健康检查、信息、度量注入到 Actuator。

示例(HealthIndicator):

@Component
public class MyServiceHealthIndicator implements HealthIndicator {public Health health() {boolean ok = myService.check();return ok ? Health.up().build() : Health.down().withDetail("reason", "fail").build();}
}

2. 自定义端点(@Endpoint)

可以创建自定义 actuator endpoint (@Endpoint + @ReadOperation) 用于暴露业务诊断数据。


八、错误处理 / 全局响应与静态资源

1. ErrorController / ErrorAttributes

用途:自定义错误页面/响应格式或注入额外错误信息。可覆盖 BasicErrorController 或提供自定义 ErrorAttributes

示例(简单 ErrorAttributes):

@Component
public class AppErrorAttributes extends DefaultErrorAttributes {@Overridepublic Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {Map<String,Object> attrs = super.getErrorAttributes(webRequest, options);attrs.put("requestId", MDC.get("requestId"));return attrs;}
}

2. 静态资源处理

通过 ResourceHandlerRegistry 添加、缓存控制、版本控制(ResourceUrlEncodingFilterVersionResourceResolver


九、注册 & 优先级 / Ordering 机制(如何能力协作)

  • Filters:通过 FilterRegistrationBean.setOrder(...)@Order 控制。Servlet Container 的 filter 最早,Spring 的 handler interceptor 在 MVC 层。

  • Interceptors:InterceptorRegistry 的顺序决定拦截链。

  • BeanPostProcessor:必须尽早注册(通常声明为 static @Bean 或在 spring.factories/自动配置中注册)。

  • AOP/Advisor:可通过 @OrderOrdered 接口定制优先级。

  • MessageConverters:extendMessageConverters 插入(推荐)而不是 configureMessageConverters(会替换默认列表),除非你要完全控制。


十、常见陷阱与最佳实践

  1. 不要轻易继承 WebMvcConfigurationSupport,会覆盖 Spring Boot 的自动配置。

  2. Filter 与 Interceptor 的职责分明:Filter 负责底层请求(例如安全、CORS),Interceptor 负责 MVC 层逻辑(例如权限检查基于 Handler)。

  3. 避免在 BeanPostProcessor 中去创建或依赖延迟创建大量 bean(可能触发循环依赖或性能问题)。

  4. 对请求 body 做缓存要注意大小(使用流处理或限制最大大小)。

  5. AOT/native 环境:某些依赖运行时反射的插件/注入在 native image 下需要额外配置(reflection metadata)。自定义注解处理器生成静态配置更安全。

  6. 顺序管理:复杂系统多个 filter/interceptor/advices 时,务必写清楚 order 并记录理由(例如日志 filter 要在 security filter 之前或之后执行)。

  7. 错误处理:Global Exception Handler 要考虑 Spring Boot 的 ErrorController 行为与静态资源处理冲突场景。

  8. 性能:避免在 high-throughput 点做阻塞/同步 IO;对于密集调用点(如每个请求的复杂解析)考虑使用缓存或在请求入口做提前判定。


十一、精选完整示例(可复制的“真实样板”)

下面给出 5 个经常直接可用的样板:

  1. OncePerRequestFilter + HttpServletRequestWrapper(请求日志 + 缓存 body)

  2. HandlerMethodArgumentResolver(@CurrentUser)

  3. ResponseBodyAdvice(统一响应包装)

  4. AbstractHttpMessageConverter(自定义 media-type)

  5. BeanPostProcessor(基于注解生成代理)

1. OncePerRequestFilter + HttpServletRequestWrapper(请求日志 + 缓存 body)

需求: 我们希望在每次请求时记录请求体日志,并允许后续操作(如处理请求体)时能够多次读取请求体。

代码实现

1.1 创建 CachingRequestWrapper 来缓存请求体

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;public class CachingRequestWrapper extends HttpServletRequestWrapper {private byte[] cachedBody;public CachingRequestWrapper(HttpServletRequest request) throws IOException {super(request);// 缓存请求体this.cachedBody = request.getInputStream().readAllBytes();}@Overridepublic InputStream getInputStream() throws IOException {return new ByteArrayInputStream(cachedBody); // 返回缓存的请求体}public String getBodyAsString() {return new String(cachedBody);}
}

1.2 创建 RequestLoggingFilter 过滤器,记录请求日志

import org.springframework.web.filter.OncePerRequestFilter;import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;@WebFilter(urlPatterns = "/api/*")
public class RequestLoggingFilter extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {// 使用 CachingRequestWrapper 包装请求CachingRequestWrapper wrappedRequest = new CachingRequestWrapper(request);// 记录请求体日志String requestBody = wrappedRequest.getBodyAsString();log.info("Request Body: " + requestBody);// 继续执行请求处理filterChain.doFilter(wrappedRequest, response);}
}

1.3 注册 RequestLoggingFilter 到 Spring Boot 中

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class WebConfig {@Beanpublic FilterRegistrationBean<RequestLoggingFilter> loggingFilter() {FilterRegistrationBean<RequestLoggingFilter> registrationBean = new FilterRegistrationBean<>();registrationBean.setFilter(new RequestLoggingFilter());registrationBean.addUrlPatterns("/api/*"); // 只拦截 "/api/*" 路径的请求return registrationBean;}
}

2. HandlerMethodArgumentResolver(@CurrentUser)

需求: 自定义注解 @CurrentUser,自动注入当前登录用户信息。

代码实现

2.1 创建 @CurrentUser 注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface CurrentUser {
}

2.2 创建 CurrentUserArgumentResolver 实现

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.stereotype.Component;@Component
public class CurrentUserArgumentResolver implements HandlerMethodArgumentResolver {@Autowiredprivate UserService userService; // 用于获取当前用户的服务@Overridepublic boolean supportsParameter(MethodParameter parameter) {// 判断是否为 @CurrentUser 注解return parameter.hasParameterAnnotation(CurrentUser.class) && parameter.getParameterType().equals(User.class);}@Overridepublic Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {// 从请求头中获取用户信息(这里假设从 JWT 或 session 获取)String userId = webRequest.getHeader("Authorization"); // 假设请求头有 Authorizationreturn userService.getUserById(userId);}
}

2.3 注册 HandlerMethodArgumentResolver

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate CurrentUserArgumentResolver currentUserArgumentResolver;@Overridepublic void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {resolvers.add(currentUserArgumentResolver);  // 注册自定义解析器}
}

2.4 使用 @CurrentUser

@RestController
public class UserController {@GetMapping("/user")public String getUserInfo(@CurrentUser User user) {return "User Info: " + user.getName();}
}

3. ResponseBodyAdvice(统一响应包装)

需求: 所有的响应数据都加上统一的包装(比如 codemessagedata 字段)。

代码实现

3.1 创建 ResponseWrapper

public class ResponseWrapper<T> {private int code;private String message;private T data;public ResponseWrapper(int code, String message, T data) {this.code = code;this.message = message;this.data = data;}// Getters and Setters
}

3.2 创建 ResponseBodyAdvice 实现

import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import org.springframework.core.MethodParameter;@Component
public class ResponseWrapperAdvice implements ResponseBodyAdvice<Object> {@Overridepublic boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {// 统一对所有返回类型生效return true;}@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,Class<? extends HttpMessageConverter<?>> selectedConverterType,org.springframework.http.server.ServerHttpRequest request,org.springframework.http.server.ServerHttpResponse response) {if (body instanceof ResponseWrapper) {return body; // 如果已经是包装过的响应,直接返回}return new ResponseWrapper<>(200, "Success", body); // 统一包装}
}

3.3 使用统一响应包装

@RestController
public class UserController {@GetMapping("/user")public ResponseWrapper<User> getUser() {User user = new User("John", 30);return new ResponseWrapper<>(200, "Success", user);}
}

4. AbstractHttpMessageConverter(自定义 media-type)

需求: 支持自定义的 media-type(例如 application/custom+json)。

代码实现

4.1 创建自定义 HttpMessageConverter

import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.stereotype.Component;
import java.io.IOException;@Component
public class CustomHttpMessageConverter extends AbstractHttpMessageConverter<Object> {public CustomHttpMessageConverter() {super(new MediaType("application", "custom+json"));}@Overrideprotected boolean supports(Class<?> clazz) {return true; // 支持所有类型}@Overrideprotected Object readInternal(Class<? extends Object> clazz, org.springframework.http.HttpInputMessage inputMessage)throws IOException, org.springframework.http.converter.HttpMessageNotReadableException {// 自定义读取逻辑return new Object(); // 返回自定义对象}@Overrideprotected void writeInternal(Object object, org.springframework.http.HttpOutputMessage outputMessage)throws IOException, org.springframework.http.converter.HttpMessageNotWritableException {// 自定义写入逻辑outputMessage.getBody().write(object.toString().getBytes());}
}

4.2 注册 CustomHttpMessageConverter

@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate CustomHttpMessageConverter customHttpMessageConverter;@Overridepublic void extendMessageConverters(List<HttpMessageConverter<?>> converters) {converters.add(customHttpMessageConverter);  // 注册自定义消息转换器}
}

5. BeanPostProcessor(基于注解生成代理)

需求: 在服务方法上使用 @Log 注解,自动生成代理,记录方法调用日志。

代码实现

5.1 创建 @Log 注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
}

5.2 创建 LogBeanPostProcessor

import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;@Component
public class LogBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) {return bean; // 初始化前不做任何操作}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) {if (bean instanceof MyService) {return Proxy.newProxyInstance(bean.getClass().getClassLoader(),bean.getClass().getInterfaces(),(proxy, method, args) -> {if (method.isAnnotationPresent(Log.class)) {System.out.println("Method " + method.getName() + " is called");}return method.invoke(bean, args);});}return bean;}
}

5.3 使用 @Log 注解

public interface MyService {@Logvoid doSomething();
}@Service
public class MyServiceImpl implements MyService {@Overridepublic void doSomething() {System.out.println("Doing something...");}
}

http://www.dtcms.com/a/366859.html

相关文章:

  • 力扣654:最大二叉树
  • AI+Java 守护你的钱袋子!金融领域的智能风控与极速交易
  • .NET 开发者的“Fiddler”:Titanium.Web.Proxy 库的强大魅力
  • 以数据与自动化驱动实验室变革:智能化管理整体规划
  • “乾坤大挪移”:耐达讯自动化RS485转Profinet解锁HMI新乾坤
  • 数据安全章节考试考点及关系梳理
  • Hadoop(七)
  • 服务器数据恢复—服务器断电,RAID数据恢复大揭秘
  • Python - 通用滑块验证码识别库 Captcha-Recognizer
  • MySQL复制技术的发展历程
  • 【论文阅读—深度学习处理表格数据】ResNet-like FT Transformer
  • 当电力设计遇上AI:良策金宝AI如何重构行业效率边界?
  • 学习嵌入式的第三十三天——网络编程
  • HTB Sau
  • 服务器异常磁盘写排查手册 · 已删除文件句柄篇
  • 稠密矩阵和稀疏矩阵的对比
  • C++面试突击(1)
  • 【面试】MySQL 面试常见优化问题
  • 面试官:如何确保动态线程池任务都执行完?
  • 计算机网络模型入门指南:分层原理与各层作用
  • EasyExcel:阿里开源的高效 Excel 处理工具,轻松解决 POI 内存溢出问题
  • SolidWorks对电脑的硬件配置要求具体有哪些
  • [Sublime Text]-显示菜单栏
  • 《云原生深坑实录:让团队卡壳的不是配置,是底层逻辑盲区》
  • 【Dify】使用工具节点实现 API 接口调用与 JSON 处理
  • 25高教社杯数模国赛【B题超高质量思路+问题分析】
  • 具身智能多模态感知与场景理解:视觉探索
  • 第二阶段WinForm-13:图表控件,N层架构,Dapper
  • 数据结构与排序算法:从理论到场景,解锁高效数据处理的核心逻辑
  • 【项目思路】基于STM32+ZigBee的智能家居--浴室场景设计