如何在Spring MVC控制器中简化自定义请求处理的配置?
可行方案:自定义注解简化REST控制器样板代码
针对你的需求,有两种主流实现方式,都能大幅减少控制器中的重复代码,完全匹配你期望的注解式写法:
方案一:方法级@Provider注解 + AOP拦截
这种方式适合保留控制器类,仅简化方法实现,写法和你给出的第一个示例一致。
步骤1:定义自定义@Provider注解
import java.lang.annotation.*; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Provider { // 指定要调用的ListProvider实现类 Class<? extends ListProvider> value(); }
步骤2:调整ListProvider适配Spring请求(可选,也可保留原RequestObject参数)
如果允许替换为HttpServletRequest,可让Provider自己处理请求解析:
public abstract class ListProvider { protected final ObjectMapper objectMapper = new ObjectMapper(); // 改为接收HttpServletRequest,由子类自行解析请求体 public abstract ResponseObject getResponse(HttpServletRequest request) throws IOException; // 通用解析工具方法,子类可直接调用 protected RequestObject parseRequest(HttpServletRequest request) throws IOException { return objectMapper.readValue(request.getInputStream(), RequestObject.class); } }
步骤3:编写AOP切面拦截注解方法
通过切面自动调用指定Provider的业务逻辑,完全替代控制器方法的样板代码:
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; @Aspect @Component public class ProviderMethodInterceptor { private final ApplicationContext context; public ProviderMethodInterceptor(ApplicationContext context) { this.context = context; } @Around("@annotation(providerAnnotation)") public Object handleProviderRequest(ProceedingJoinPoint joinPoint, Provider providerAnnotation) throws Throwable { // 从Spring容器获取指定的Provider实例 ListProvider provider = context.getBean(providerAnnotation.value()); // 获取当前HTTP请求对象 HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); // 调用Provider的业务方法并返回结果 return provider.getResponse(request); } }
最终控制器写法
@RestController @RequestMapping("/client") public class ClientController { @PostMapping("/list") @Provider(ClientListProvider.class) public ResponseObject list() { // 空方法即可,业务逻辑由AOP切面自动处理 return null; } }
方案二:类级@ProviderController注解 + 动态注册请求映射
这种方式更极致,连控制器方法都可以省略,直接通过类注解生成完整的REST接口,匹配你给出的第二个示例。
步骤1:定义类级@ProviderController注解
import java.lang.annotation.*; import org.springframework.web.bind.annotation.RequestMethod; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @RestController public @interface ProviderController { // 基础路径(对应原@RequestMapping) String basePath(); // 指定关联的ListProvider实现类 Class<? extends ListProvider> providerClass(); // 列表接口路径(默认/list) String listPath() default "/list"; // 请求方法(默认POST) RequestMethod method() default RequestMethod.POST; }
步骤2:编写请求映射注册器
通过Spring的RequestMappingHandlerMapping动态为带注解的控制器注册请求映射:
import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.RequestMappingInfo; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import javax.servlet.http.HttpServletRequest; @Component public class ProviderControllerRegistrar implements BeanPostProcessor, ApplicationContextAware { private ApplicationContext context; private final RequestMappingHandlerMapping handlerMapping; @Autowired public ProviderControllerRegistrar(RequestMappingHandlerMapping handlerMapping) { this.handlerMapping = handlerMapping; } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.context = applicationContext; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { Class<?> beanClass = bean.getClass(); ProviderController annotation = beanClass.getAnnotation(ProviderController.class); if (annotation != null) { // 构建完整的请求路径 String fullPath = annotation.basePath() + annotation.listPath(); // 创建请求映射信息 RequestMappingInfo mappingInfo = RequestMappingInfo .paths(fullPath) .methods(annotation.method()) .build(); // 获取指定的Provider实例 ListProvider provider = context.getBean(annotation.providerClass()); // 创建对应的HandlerMethod(绑定Provider的getResponse方法) HandlerMethod handlerMethod = new HandlerMethod(provider, "getResponse", HttpServletRequest.class); // 将映射注册到Spring MVC handlerMapping.registerMapping(mappingInfo, handlerMethod, null); } return bean; } }
最终控制器写法
@ProviderController(basePath = "/client", providerClass = ClientListProvider.class) public class ClientController { // 完全不需要写任何方法,自动生成/client/list的POST接口 }
注意事项
- 如果你想保留原
RequestObject参数(不替换为HttpServletRequest),可以调整切面或注册器的逻辑,让Spring自动通过@RequestBody解析参数,只需修改HandlerMethod的参数列表为RequestObject即可。 - 确保所有
ListProvider实现类都标注了@Service,能被Spring容器扫描到。 - 两种方案都支持自定义异常处理,只需在Provider中抛出异常,配合Spring的
@ControllerAdvice统一处理即可。
内容的提问来源于stack exchange,提问作者Javaddict
相关产品推荐
相关产品推荐

