SpringBoot RESTful API控制器与请求映射注解源码级解析
一、SpringBoot RESTful API基础架构
1.1 RESTful API设计理念
REST(Representational State Transfer)是一种软件架构风格,它定义了一组用于创建Web服务的约束条件和原则。RESTful API基于HTTP协议,使用URL标识资源,使用HTTP方法(GET、POST、PUT、DELETE)操作资源,使用JSON或XML等格式表示资源状态。
SpringBoot为构建RESTful API提供了强大支持,通过@RestController
、@RequestMapping
等注解,可以快速开发出符合RESTful风格的Web服务。这些注解在底层与Spring MVC框架深度集成,利用了Servlet容器的特性,实现了高效的请求处理和响应返回。
1.2 SpringBoot REST架构组件
SpringBoot RESTful API的核心组件包括:
- 控制器(Controller):处理HTTP请求的组件,使用
@RestController
或@Controller
注解标识。 - 请求映射(Request Mapping):将HTTP请求映射到具体的处理方法,使用
@RequestMapping
、@GetMapping
等注解。 - 请求参数处理:解析HTTP请求中的参数,包括路径变量、查询参数、请求体等。
- 响应处理:将处理结果转换为HTTP响应,包括状态码、响应体、响应头等。
- 异常处理:统一处理请求过程中发生的异常,使用
@ExceptionHandler
、@ControllerAdvice
等注解。
这些组件通过Spring的依赖注入和AOP机制协同工作,形成了一个完整的RESTful API处理流程。
1.3 REST与Spring MVC的关系
Spring MVC是Spring框架的Web模块,它基于Servlet API实现了一个强大的Web框架。RESTful API是Spring MVC的一种应用场景,Spring MVC提供了丰富的注解和工具,使得开发RESTful API变得非常简单。
SpringBoot对Spring MVC进行了自动配置,通过@EnableWebMvc
注解和各种自动配置类,简化了Spring MVC的使用。在SpringBoot中,开发RESTful API只需要关注业务逻辑,而不需要过多关注框架的配置。
二、控制器类注解体系
2.1 @RestController注解解析
@RestController
是Spring 4.0引入的一个复合注解,它结合了@Controller
和@ResponseBody
的功能。使用@RestController
注解的类会被Spring MVC识别为控制器,并且其方法返回的对象会自动序列化为HTTP响应体。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
@AliasFor(annotation = Controller.class)
String value() default "";
}
从源码可以看出,@RestController
实际上是@Controller
和@ResponseBody
的组合。@Controller
注解用于标识一个类是Spring MVC控制器,而@ResponseBody
注解用于指示方法返回值直接作为HTTP响应体,而不是视图名称。
2.2 @Controller注解详解
@Controller
是Spring MVC中最基本的控制器注解,它用于标识一个类是控制器组件。被@Controller
注解的类会被Spring的组件扫描机制发现,并注册为Spring Bean。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
@AliasFor(annotation = Component.class)
String value() default "";
}
从源码可以看出,@Controller
继承自@Component
,因此它具有@Component
的所有特性。Spring MVC会特别处理被@Controller
注解的类,将其方法与HTTP请求映射关联起来。
2.3 @ResponseBody注解作用
@ResponseBody
注解用于指示控制器方法的返回值应该直接序列化为HTTP响应体,而不是解析为视图名称。在@RestController
出现之前,需要在每个方法上单独添加@ResponseBody
注解,现在可以通过@RestController
统一应用。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseBody {
}
@ResponseBody
注解的处理是通过RequestMappingHandlerAdapter
中的MessageConverter
实现的。当一个方法被@ResponseBody
注解时,Spring MVC会使用适当的HttpMessageConverter
将返回值转换为HTTP响应体。
三、请求映射注解体系
3.1 @RequestMapping注解核心
@RequestMapping
是Spring MVC中最基本的请求映射注解,它可以应用于类和方法上,用于将HTTP请求映射到控制器的处理方法。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
String[] value() default {};
RequestMethod[] method() default {};
String[] params() default {};
String[] headers() default {};
String[] consumes() default {};
String[] produces() default {};
}
@RequestMapping
注解的主要属性包括:
value
:指定请求的URL路径。method
:指定HTTP请求方法(GET、POST等)。params
:指定请求参数的条件。headers
:指定请求头的条件。consumes
:指定请求体的MIME类型。produces
:指定响应体的MIME类型。
3.2 快捷请求映射注解
为了简化@RequestMapping
的使用,Spring MVC提供了一组快捷注解,它们是@RequestMapping
的特定HTTP方法版本:
@GetMapping
:等价于@RequestMapping(method = RequestMethod.GET)
@PostMapping
:等价于@RequestMapping(method = RequestMethod.POST)
@PutMapping
:等价于@RequestMapping(method = RequestMethod.PUT)
@DeleteMapping
:等价于@RequestMapping(method = RequestMethod.DELETE)
@PatchMapping
:等价于@RequestMapping(method = RequestMethod.PATCH)
这些快捷注解的源码实现类似,以@GetMapping
为例:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.GET)
public @interface GetMapping {
@AliasFor(annotation = RequestMapping.class)
String[] value() default {};
// 其他属性省略...
}
3.3 请求映射注解的继承与组合
Spring MVC支持请求映射注解的继承和组合。当一个控制器类继承自另一个控制器类时,父类的请求映射注解会被子类继承。
此外,Spring MVC还支持自定义组合注解。例如,可以定义一个自定义注解,组合@RequestMapping
和其他元数据:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.GET, produces = "application/json")
public @interface JsonGetMapping {
@AliasFor(annotation = RequestMapping.class)
String[] value() default {};
}
这样,@JsonGetMapping
就可以作为一个自定义的请求映射注解使用,它会自动设置HTTP方法为GET,并指定响应内容类型为JSON。
四、控制器类的注册与扫描
4.1 组件扫描机制
SpringBoot通过组件扫描机制自动发现和注册控制器类。组件扫描是Spring框架的核心特性之一,它允许Spring自动扫描指定包下的类,并将带有特定注解的类注册为Spring Bean。
在SpringBoot应用中,通常会使用@SpringBootApplication
注解,它包含了@ComponentScan
注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
// 其他属性省略...
}
@ComponentScan
注解会扫描@SpringBootApplication
注解所在类的包及其子包,将带有@Component
、@Controller
、@Service
等注解的类注册为Spring Bean。
4.2 控制器类的注册过程
当Spring扫描到带有@Controller
或@RestController
注解的类时,会将其注册为BeanDefinition
,并在后续的初始化过程中创建实例。
控制器类的注册过程主要涉及以下几个步骤:
- 扫描类路径:
ClassPathBeanDefinitionScanner
扫描指定包下的类文件。 - 识别候选组件:通过
AnnotationTypeFilter
识别带有@Controller
或@RestController
注解的类。 - 注册BeanDefinition:将识别出的控制器类注册为
RootBeanDefinition
。 - 实例化和初始化:在Spring容器启动过程中,实例化并初始化控制器类。
4.3 HandlerMapping的注册
控制器类注册完成后,Spring MVC会创建相应的HandlerMapping
来处理请求映射。HandlerMapping
是Spring MVC中的核心组件,负责将HTTP请求映射到具体的处理方法。
在Spring MVC中,默认的HandlerMapping
实现是RequestMappingHandlerMapping
,它会处理带有@RequestMapping
注解的控制器方法。
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
implements MatchableHandlerMapping, EmbeddedValueResolverAware {
// 初始化方法
@Override
protected void initHandlerMethods() {
// 查找所有带有@RequestMapping注解的Bean
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
obtainApplicationContext(), Object.class) :
obtainApplicationContext().getBeanNamesForType(Object.class));
for (String beanName : beanNames) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
Class<?> beanType = null;
try {
beanType = obtainApplicationContext().getType(beanName);
} catch (Throwable ex) {
// 忽略异常
}
if (beanType != null && isHandler(beanType)) {
// 检测并注册控制器方法
detectHandlerMethods(beanName);
}
}
}
// 其他初始化代码...
}
@Override
protected boolean isHandler(Class<?> beanType) {
// 判断是否是控制器类
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RestController.class));
}
// 其他方法省略...
}
五、请求映射处理流程
5.1 DispatcherServlet请求处理流程
Spring MVC的核心是DispatcherServlet
,它是一个前端控制器,负责接收所有HTTP请求并协调处理流程。DispatcherServlet
的请求处理流程如下:
- 接收请求:
DispatcherServlet
接收HTTP请求并创建HttpServletRequest
和HttpServletResponse
对象。 - 查找Handler:通过
HandlerMapping
查找处理该请求的Handler(即控制器方法)。 - 获取HandlerAdapter:根据Handler类型获取对应的
HandlerAdapter
。 - 执行Handler:调用
HandlerAdapter
执行Handler,获取返回值。 - 处理返回值:通过
HandlerMethodReturnValueHandler
处理返回值,生成HTTP响应。 - 处理异常:如果执行过程中发生异常,通过
HandlerExceptionResolver
处理异常。
5.2 RequestMappingHandlerMapping工作原理
RequestMappingHandlerMapping
是处理@RequestMapping
注解的核心组件,它负责将HTTP请求映射到控制器方法。其工作原理如下:
- 注册请求映射:在Spring容器启动时,扫描所有带有
@Controller
或@RestController
注解的类,将其中的@RequestMapping
注解方法注册到RequestMappingInfo
中。 - 匹配请求:当接收到HTTP请求时,根据请求的URL、HTTP方法、请求头等信息,从已注册的
RequestMappingInfo
中找到匹配的处理方法。 - 返回HandlerExecutionChain:将匹配到的处理方法封装为
HandlerExecutionChain
对象,包含Handler和相关的HandlerInterceptor
。
5.3 RequestMappingHandlerAdapter执行过程
RequestMappingHandlerAdapter
是执行控制器方法的核心组件,它负责调用控制器方法并处理返回值。其执行过程如下:
- 参数解析:通过
HandlerMethodArgumentResolver
解析方法参数,包括路径变量、查询参数、请求体等。 - 方法调用:使用反射调用控制器方法,传递解析后的参数。
- 返回值处理:通过
HandlerMethodReturnValueHandler
处理方法返回值,生成HTTP响应。
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
implements BeanFactoryAware, InitializingBean {
// 执行控制器方法
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
// 如果是异步请求,处理异步执行
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// 没有会话,直接执行
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// 非同步会话,直接执行
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
// 调用控制器方法
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
// 创建DataBinderFactory
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
// 创建ModelFactory
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// 创建ServletInvocableHandlerMethod
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
// 创建ModelAndViewContainer
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
// 异步请求处理
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
// 调用控制器方法
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
// 返回ModelAndView
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
// 其他方法省略...
}
六、请求参数处理机制
6.1 路径变量处理
路径变量是RESTful API中常用的参数传递方式,通过@PathVariable
注解可以将URL中的变量部分绑定到方法参数上。
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
return userService.getUserById(id);
}
路径变量的处理是通过PathVariableMethodArgumentResolver
实现的,它会从HandlerMapping
中获取路径变量信息,并将其转换为方法参数类型。
public class PathVariableMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
if (!parameter.hasParameterAnnotation(PathVariable.class)) {
return false;
}
if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
PathVariable pathVariable = parameter.getParameterAnnotation(PathVariable.class);
return (pathVariable != null && StringUtils.hasText(pathVariable.value()));
}
return true;
}
@Override
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
PathVariable ann = parameter.getParameterAnnotation(PathVariable.class);
return new PathVariableNamedValueInfo(ann);
}
@Override
@Nullable
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
// 从URI模板变量中获取值
Map<String, String> uriTemplateVars = (Map<String, String>)
request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
return (uriTemplateVars != null ? uriTemplateVars.get(name) : null);
}
// 其他方法省略...
}
6.2 查询参数处理
查询参数是通过URL中的查询字符串传递的参数,通过@RequestParam
注解可以将查询参数绑定到方法参数上。
@GetMapping("/users")
public List<User> getUsers(@RequestParam(required = false) String name) {
return userService.getUsersByName(name);
}
查询参数的处理是通过RequestParamMethodArgumentResolver
实现的,它会从HTTP请求的查询字符串中获取参数值,并将其转换为方法参数类型。
public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
private final boolean useDefaultResolution;
public RequestParamMethodArgumentResolver(boolean useDefaultResolution) {
this.useDefaultResolution = useDefaultResolution;
}
@Override
public boolean supportsParameter(MethodParameter parameter) {
if (parameter.hasParameterAnnotation(RequestParam.class)) {
if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
return (requestParam != null && StringUtils.hasText(requestParam.value()));
}
else {
return true;
}
}
else {
if (parameter.hasParameterAnnotation(RequestPart.class)) {
return false;
}
parameter = parameter.nestedIfOptional();
if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {
return true;
}
else if (this.useDefaultResolution) {
return BeanUtils.isSimpleProperty(parameter.getNestedParameterType());
}
else {
return false;
}
}
}
@Override
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
RequestParam ann = parameter.getParameterAnnotation(RequestParam.class);
return (ann != null ? new RequestParamNamedValueInfo(ann) : new RequestParamNamedValueInfo());
}
@Override
@Nullable
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
// 从请求参数中获取值
HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
if (servletRequest != null) {
Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
if (mpArg != MultipartResolutionDelegate.UNRESOLVED) {
return mpArg;
}
}
Object arg = null;
MultipartRequest multipartRequest = request.getNativeRequest(MultipartRequest.class);
if (multipartRequest != null) {
List<MultipartFile> files = multipartRequest.getFiles(name);
if (!files.isEmpty()) {
arg = (files.size() == 1 ? files.get(0) : files);
}
}
if (arg == null) {
String[] paramValues = request.getParameterValues(name);
if (paramValues != null) {
arg = (paramValues.length == 1 ? paramValues[0] : paramValues);
}
}
return arg;
}
// 其他方法省略...
}
6.3 请求体处理
请求体是HTTP请求中的消息体部分,通常用于传递复杂的数据结构,如JSON或XML。通过@RequestBody
注解可以将请求体内容绑定到方法参数上。
@PostMapping("/users")
public User createUser(@RequestBody User user) {
return userService.createUser(user);
}
请求体的处理是通过RequestResponseBodyMethodProcessor
实现的,它会使用HttpMessageConverter
将请求体内容转换为方法参数类型。
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
public RequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters) {
this(converters, null);
}
public RequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters,
@Nullable List<Object> requestResponseBodyAdvice) {
super(converters, null, requestResponseBodyAdvice);
}
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestBody.class);
}
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
returnType.hasMethodAnnotation(ResponseBody.class));
}
@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
parameter = parameter.nestedIfOptional();
// 读取请求体并转换为对象
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
String name = Conventions.getVariableNameForParameter(parameter);
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
if (arg != null) {
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
}
}
if (mavContainer != null) {
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
}
}
return adaptArgumentIfNecessary(arg, parameter);
}
// 其他方法省略...
}
七、响应处理机制
7.1 返回值处理流程
控制器方法的返回值会通过HandlerMethodReturnValueHandler
进行处理,将其转换为HTTP响应。Spring MVC提供了多种返回值处理器,根据返回值类型选择合适的处理器进行处理。
主要的返回值处理器包括:
ModelAndViewMethodReturnValueHandler
:处理返回ModelAndView
的方法。ViewMethodReturnValueHandler
:处理返回View
的方法。HttpEntityMethodProcessor
:处理返回HttpEntity
或ResponseEntity
的方法。RequestResponseBodyMethodProcessor
:处理返回值带有@ResponseBody
注解的方法。SimpleTypeHandlerMethodReturnValueHandler
:处理返回简单类型(如String、Integer等)的方法。
返回值处理的核心流程在ServletInvocableHandlerMethod
的invokeAndHandle
方法中:
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
@Nullable
private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 调用控制器方法
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
// 处理返回值
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
// 其他方法省略...
}
7.2 HttpMessageConverter工作原理
HttpMessageConverter
是Spring MVC中用于实现HTTP请求和响应与Java对象之间转换的核心接口。它负责将请求体内容转换为Java对象,以及将Java对象转换为响应体内容。
主要的HttpMessageConverter
实现包括:
MappingJackson2HttpMessageConverter
:处理JSON格式的请求和响应。Jaxb2RootElementHttpMessageConverter
:处理XML格式的请求和响应。StringHttpMessageConverter
:处理字符串格式的请求和响应。FormHttpMessageConverter
:处理表单数据的请求和响应。
HttpMessageConverter
的工作流程如下:
- 根据请求或响应的Content-Type确定使用的转换器。
- 检查转换器是否支持目标类型。
- 调用转换器的
read()
方法将请求体转换为Java对象,或调用write()
方法将Java对象转换为响应体。
7.3 ResponseEntity与状态码处理
ResponseEntity
是Spring MVC中用于表示HTTP响应的类,它可以包含响应体、状态码和响应头等信息。通过返回ResponseEntity
对象,可以精确控制HTTP响应的各个方面。
@GetMapping("/users/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
User user = userService.getUserById(id);
if (user != null) {
return ResponseEntity.ok(user);
} else {
return ResponseEntity.notFound().build();
}
}
ResponseEntity
的处理是通过HttpEntityMethodProcessor
实现的,它会将ResponseEntity
中的状态码、响应头和响应体设置到HTTP响应中。
public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodProcessor {
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return (HttpEntity.class.isAssignableFrom(returnType.getParameterType()) &&
!ResponseEntity.class.isAssignableFrom(returnType.getParameterType()));
}
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
mavContainer.setRequestHandled(true);
if (returnValue == null) {
return;
}
HttpEntity<?> httpEntity = (HttpEntity<?>) returnValue;
if (httpEntity instanceof ResponseEntity) {
// 设置响应状态码
webRequest.getNativeResponse(HttpServletResponse.class).setStatus(
((ResponseEntity<?>) httpEntity).getStatusCodeValue());
}
HttpHeaders entityHeaders = httpEntity.getHeaders();
if (!entityHeaders.isEmpty()) {
// 设置响应头
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
ServletServerHttpResponse outputMessage = new ServletServerHttpResponse(response);
for (Map.Entry<String, List<String>> entry : entityHeaders.entrySet()) {
String headerName = entry.getKey();
if ("Set-Cookie".equalsIgnoreCase(headerName)) {
List<String> headerValues = entry.getValue();
for (String headerValue : headerValues) {
response.addHeader(headerName, headerValue);
}
}
else {
outputMessage.getHeaders().put(headerName, entry.getValue());
}
}
}
Object body = httpEntity.getBody();
if (body != null) {
// 写入响应体
writeWithMessageConverters(body, returnType, webRequest);
}
else {
// 处理空响应体
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
if (response.getStatus() == HttpServletResponse.SC_OK) {
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
}
}
}
// 其他方法省略...
}
八、异常处理机制
8.1 异常处理流程
Spring MVC提供了强大的异常处理机制,允许开发者统一处理控制器方法中抛出的异常。异常处理的核心组件是HandlerExceptionResolver
,它负责捕获异常并返回适当的响应。
异常处理的流程如下:
- 当控制器方法抛出异常时,
DispatcherServlet
会捕获该异常。 DispatcherServlet
会遍历所有注册的HandlerExceptionResolver
,找到能够处理该异常的解析器。- 调用解析器的
resolveException
方法,生成ModelAndView
对象。 - 使用生成的
ModelAndView
对象渲染错误视图或返回错误响应。
8.2 @ExceptionHandler注解
@ExceptionHandler
注解用于标记处理特定异常的方法,这些方法通常定义在控制器类中。当控制器方法抛出异常时,Spring MVC会查找并调用最匹配的@ExceptionHandler
方法。
@RestController
public class UserController {
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
User user = userService.getUserById(id);
if (user == null) {
throw new UserNotFoundException("User not found with id: " + id);
}
return user;
}
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<String> handleUserNotFoundException(UserNotFoundException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage());
}
// 其他方法省略...
}
@ExceptionHandler
方法的处理是通过ExceptionHandlerExceptionResolver
实现的,它会查找并调用适当的异常处理方法。
public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver
implements InitializingBean {
@Override
@Nullable
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {
// 查找匹配的异常处理方法
ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
if (exceptionHandlerMethod == null) {
return null;
}
// 准备参数
try {
if (logger.isDebugEnabled()) {
logger.debug("Using @ExceptionHandler " + exceptionHandlerMethod);
}
// 确保响应未提交
if (this.argumentResolvers != null) {
exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
ServletWebRequest webRequest = new ServletWebRequest(request, response);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
try {
// 调用异常处理方法
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception);
}
catch (Exception invocationEx) {
// 处理异常处理方法内部抛出的异常
if (logger.isErrorEnabled()) {
logger.error("Failed to invoke @ExceptionHandler method: " + exceptionHandlerMethod, invocationEx);
}
return null;
}
if (mavContainer.isRequestHandled()) {
return new ModelAndView();
}
else {
ModelMap model = mavContainer.getModel();
HttpStatus status = mavContainer.getStatus();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, status);
mav.setViewName(mavContainer.getViewName());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
return mav;
}
}
catch (Exception ex) {
// 处理异常处理过程中发生的异常
if (logger.isErrorEnabled()) {
logger.error("Failure in @ExceptionHandler " + exceptionHandlerMethod, ex);
}
return null;
}
}
// 其他方法省略...
}
8.3 @ControllerAdvice注解
@ControllerAdvice
注解用于定义全局异常处理器,它可以将@ExceptionHandler
方法应用到所有控制器类。通过@ControllerAdvice
,可以实现统一的异常处理机制。
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleGlobalException(Exception ex) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Internal server error");
}
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<String> handleIllegalArgumentException(IllegalArgumentException ex) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ex.getMessage());
}
// 其他异常处理方法...
}
@ControllerAdvice
的处理是通过ExceptionHandlerExceptionResolver
实现的,它会扫描所有带有@ControllerAdvice
注解的类,并注册其中的@ExceptionHandler
方法。
public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver
implements InitializingBean {
@Override
public void afterPropertiesSet() {
// 初始化异常处理方法解析器
initExceptionHandlerAdviceCache();
if (this.argumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
private void initExceptionHandlerAdviceCache() {
if (getApplicationContext() == null) {
return;
}
// 查找所有带有@ControllerAdvice注解的Bean
List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
AnnotationAwareOrderComparator.sort(adviceBeans);
for (ControllerAdviceBean adviceBean : adviceBeans) {
Class<?> beanType = adviceBean.getBeanType();
if (beanType == null) {
throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
}
// 查找并注册@ExceptionHandler方法
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);
if (resolver.hasExceptionMappings()) {
this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
}
// 处理@ModelAttribute和@InitBinder方法
if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
this.responseBodyAdvice.add(adviceBean);
}
}
if (logger.isDebugEnabled()) {
int handlerSize = this.exceptionHandlerAdviceCache.size();
int adviceSize = this.responseBodyAdvice.size();
logger.debug("Detected " + handlerSize + " @ExceptionHandler methods in " + adviceSize + " @ControllerAdvice beans");
}
}
// 其他方法省略...
}
九、控制器类的高级特性
9.1 @ModelAttribute注解
@ModelAttribute
注解用于将方法参数或方法返回值绑定到模型属性上。它可以应用于方法参数和方法上。
当应用于方法参数时,@ModelAttribute
用于从模型中获取属性,或创建一个新的模型属性:
@GetMapping("/users/{id}/edit")
public String editUser(@PathVariable Long id, @ModelAttribute("user") User user) {
// 从数据库获取用户信息并设置到模型属性中
User dbUser = userService.getUserById(id);
BeanUtils.copyProperties(dbUser, user);
return "user-edit";
}
当应用于方法时,@ModelAttribute
用于在每次请求处理前将数据添加到模型中:
@ModelAttribute
public void addCommonAttributes(Model model) {
model.addAttribute("appName", "My Application");
}
9.2 @InitBinder注解
@InitBinder
注解用于初始化WebDataBinder
,它可以用于注册自定义编辑器、设置允许或禁止的字段等。
@InitBinder
public void initBinder(WebDataBinder binder) {
// 注册自定义日期编辑器
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
// 设置允许的字段
binder.setAllowedFields("username", "email", "age");
}
9.3 跨域资源共享(CORS)支持
Spring MVC提供了对跨域资源共享(CORS)的支持,允许服务器跨域响应请求。可以通过@CrossOrigin
注解或全局配置来启用CORS支持。
@RestController
@RequestMapping("/api")
@CrossOrigin(origins = "http://example.com")
public class ApiController {
@GetMapping("/users")
public List<User> getUsers() {
return userService.getAllUsers();
}
}
全局CORS配置可以通过实现WebMvcConfigurer
接口来完成:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://example.com")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
十、SpringBoot自动配置与控制器类
10.1 SpringBoot Web自动配置
SpringBoot通过自动配置机制简化了Spring MVC的使用。WebMvcAutoConfiguration
是SpringBoot中Web MVC的核心自动配置类,它会根据类路径中的依赖和应用配置自动配置Spring MVC。
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
// 自动配置RequestMappingHandlerMapping
@Bean
@Primary
@ConditionalOnMissingBean
public RequestMappingHandlerMapping requestMappingHandlerMapping(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
// 创建RequestMappingHandlerMapping实例
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
mapping.setOrder(0);
mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
mapping.setContentNegotiationManager(contentNegotiationManager);
mapping.setCorsConfigurations(getCorsConfigurations());
PathMatchConfigurer configurer = getPathMatchConfigurer();
if (configurer.getPatternParser() != null) {
mapping.setPatternParser(configurer.getPatternParser());
}
else {
mapping.setUrlPathHelper(configurer.getUrlPathHelperOrDefault());
mapping.setPathMatcher(configurer.getPathMatcherOrDefault());
mapping.setUseSuffixPatternMatch(configurer.isUseSuffixPatternMatch());
mapping.setUseRegisteredSuffixPatternMatch(configurer.isUseRegisteredSuffixPatternMatch());
mapping.setUseTrailingSlashMatch(configurer.isUseTrailingSlashMatch());
}
return mapping;
}
// 自动配置RequestMappingHandlerAdapter
@Bean
@Primary
@ConditionalOnMissingBean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcValidator") Validator validator) {
// 创建RequestMappingHandlerAdapter实例
RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
adapter.setContentNegotiationManager(contentNegotiationManager);
adapter.setMessageConverters(getMessageConverters(conversionService, validator));
adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer(conversionService, validator));
adapter.setCustomArgumentResolvers(getArgumentResolvers());
adapter.setCustomReturnValueHandlers(getReturnValueHandlers());
if (jackson2Present) {
adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice()));
adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));
}
AsyncSupportConfigurer configurer = getAsyncSupportConfigurer();
if (configurer.getTaskExecutor() != null) {
adapter.setTaskExecutor(configurer.getTaskExecutor());
}
if (configurer.getAsyncRequestTimeout() != null) {
adapter.setAsyncRequestTimeout(configurer.getAsyncRequestTimeout());
}
return adapter;
}
// 其他自动配置方法...
}
10.2 自定义控制器配置
SpringBoot允许通过多种方式自定义控制器配置。可以通过实现WebMvcConfigurer
接口来定制Spring MVC的行为:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
// 配置路径匹配规则
configurer.setUseTrailingSlashMatch(true);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 添加拦截器
registry.addInterceptor(new LoggingInterceptor()).addPathPatterns("/**");
}
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
// 配置内容协商
configurer.defaultContentType(MediaType.APPLICATION_JSON);
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
// 配置消息转换器
converters.add(new MappingJackson2HttpMessageConverter());
converters.add(new StringHttpMessageConverter());
}
// 其他配置方法...
}
10.3 控制器类的测试
SpringBoot提供了强大的测试支持,使得测试控制器类变得非常简单。可以使用@WebMvcTest
注解来测试控制器类,它会自动配置Spring MVC基础设施,但不会启动完整的应用上下文。
@WebMvcTest(UserController.class)
public class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
public void testGetUser() throws Exception {
// 设置mock对象行为
User user = new User(1L, "John Doe", "john@example.com");
when(userService.getUserById(1L)).thenReturn(user);
// 执行请求
mockMvc.perform(get("/users/1"))
.andExpect(status
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.name").value("John Doe"))
.andExpect(jsonPath("$.email").value("john@example.com"));
}
@Test
public void testGetUserNotFound() throws Exception {
// 设置mock对象行为
when(userService.getUserById(2L)).thenReturn(null);
// 执行请求
mockMvc.perform(get("/users/2"))
.andExpect(status().isNotFound());
}
@Test
public void testCreateUser() throws Exception {
// 创建测试数据
User user = new User(null, "Jane Doe", "jane@example.com");
User savedUser = new User(2L, "Jane Doe", "jane@example.com");
// 设置mock对象行为
when(userService.createUser(any(User.class))).thenReturn(savedUser);
// 执行请求
mockMvc.perform(post("/users")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"name\":\"Jane Doe\",\"email\":\"jane@example.com\"}"))
.andExpect(status().isCreated())
.andExpect(header().string("Location", containsString("/users/2")));
}
// 其他测试方法...
}
10.4 控制器类的性能优化
在高并发场景下,控制器类的性能优化至关重要。以下是一些常见的优化策略:
- 异步请求处理:使用
CompletableFuture
、Callable
或DeferredResult
等方式处理异步请求,释放Servlet线程以处理更多请求。
@GetMapping("/async")
public CompletableFuture<ResponseEntity<String>> asyncRequest() {
return CompletableFuture.supplyAsync(() -> {
// 执行耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return ResponseEntity.ok("Async response");
});
}
- 缓存机制:使用Spring Cache或Redis等缓存技术缓存频繁访问的数据,减少数据库访问。
@GetMapping("/users/{id}")
@Cacheable("users")
public User getUser(@PathVariable Long id) {
return userService.getUserById(id);
}
- 连接池配置:合理配置数据库连接池参数,避免连接耗尽。
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.idle-timeout=30000
- 限流与熔断:使用Resilience4j或Sentinel等框架实现限流和熔断,保护服务不被过载请求压垮。
@GetMapping("/limited")
@RateLimiter(name = "default", fallbackMethod = "fallback")
public ResponseEntity<String> limitedRequest() {
return ResponseEntity.ok("Normal response");
}
public ResponseEntity<String> fallback(Exception e) {
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body("Rate limit exceeded");
}
十一、请求映射的高级特性
11.1 路径模式匹配
Spring MVC支持多种路径模式匹配方式,包括:
- Ant风格路径:使用
?
、*
、**
等通配符。?
:匹配单个字符*
:匹配任意数量的任意字符(不包括路径分隔符/
)**
:匹配任意数量的任意字符(包括路径分隔符/
)
@GetMapping("/users/*/profile")
public String getUserProfile() {
// 匹配/users/{id}/profile
return "profile";
}
@GetMapping("/resources/**")
public ResponseEntity<Resource> getResource() {
// 匹配/resources/下的所有路径
return ResponseEntity.ok(new ClassPathResource("static/favicon.ico"));
}
- 正则表达式路径:使用
{variable:regex}
语法。
@GetMapping("/articles/{year:\\d{4}}/{month:\\d{2}}")
public String getArticle(@PathVariable int year, @PathVariable int month) {
// 匹配/articles/2023/05等路径
return "article-list";
}
11.2 矩阵变量
矩阵变量是URL中包含的键值对参数,格式为;key=value
。Spring MVC通过@MatrixVariable
注解支持矩阵变量。
@GetMapping("/cars/{carId}")
public String getCar(@PathVariable String carId,
@MatrixVariable(pathVar = "carId") List<String> colors) {
// 处理URL:/cars/abc;color=red;color=blue
return "Car ID: " + carId + ", Colors: " + colors;
}
需要在配置中启用矩阵变量支持:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
urlPathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(urlPathHelper);
}
}
11.3 内容协商
内容协商是指根据客户端的请求头(如Accept)决定返回的响应格式。Spring MVC提供了灵活的内容协商机制。
- 基于请求头的内容协商:根据客户端的Accept头选择合适的响应格式。
@GetMapping("/users")
public List<User> getUsers() {
return userService.getAllUsers();
}
- 基于URL扩展名的内容协商:通过URL中的扩展名(如.json、.xml)决定响应格式。
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(true)
.mediaType("json", MediaType.APPLICATION_JSON)
.mediaType("xml", MediaType.APPLICATION_XML);
}
}
- 基于请求参数的内容协商:通过请求参数(如
?format=json
)决定响应格式。
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorParameter(true)
.parameterName("format")
.mediaType("json", MediaType.APPLICATION_JSON)
.mediaType("xml", MediaType.APPLICATION_XML);
}
}
十二、控制器类的安全机制
12.1 Spring Security基础
Spring Security是Spring生态系统中用于提供安全服务的框架,它提供了认证、授权、CSRF防护等功能。
在SpringBoot应用中,可以通过添加spring-boot-starter-security
依赖来启用Spring Security:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
12.2 基于注解的安全控制
Spring Security提供了基于注解的安全控制机制,可以通过@PreAuthorize
、@PostAuthorize
、@PreFilter
和@PostFilter
等注解实现细粒度的安全控制。
@RestController
@RequestMapping("/api/admin")
public class AdminController {
@GetMapping("/users")
@PreAuthorize("hasRole('ADMIN')")
public List<User> getAllUsers() {
return userService.getAllUsers();
}
@DeleteMapping("/users/{id}")
@PreAuthorize("hasRole('ADMIN') && @securityService.canDeleteUser(authentication, #id)")
public void deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
}
}
需要在配置类中启用方法级安全:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// 其他配置...
}
12.3 跨站请求伪造(CSRF)防护
Spring Security默认启用CSRF防护,通过生成CSRF令牌并验证请求中的令牌来防止CSRF攻击。
对于RESTful API,可以通过配置禁用CSRF防护:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
// 其他配置...
;
}
}
或者为特定的请求路径禁用CSRF防护:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.ignoringAntMatchers("/api/**")
// 其他配置...
;
}
}
12.4 OAuth2与JWT支持
Spring Security提供了对OAuth2和JWT的支持,使得构建安全的RESTful API变得更加简单。
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/public/**").permitAll()
.anyRequest().authenticated()
.and()
.oauth2ResourceServer()
.jwt();
}
@Bean
public JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withJwkSetUri(jwkSetUri).build();
}
}
十三、响应式RESTful API支持
13.1 Spring WebFlux基础
Spring WebFlux是Spring框架提供的响应式Web框架,它基于Reactor项目实现,支持非阻塞、事件驱动的编程模型。
与Spring MVC不同,Spring WebFlux不需要Servlet API,可以运行在Netty、Undertow等非Servlet容器上。
13.2 响应式控制器
Spring WebFlux中的控制器与Spring MVC类似,但返回值类型通常是Mono
或Flux
,表示异步流。
@RestController
@RequestMapping("/api/reactive/users")
public class ReactiveUserController {
private final ReactiveUserService userService;
public ReactiveUserController(ReactiveUserService userService) {
this.userService = userService;
}
@GetMapping
public Flux<User> getAllUsers() {
return userService.findAll();
}
@GetMapping("/{id}")
public Mono<ResponseEntity<User>> getUserById(@PathVariable String id) {
return userService.findById(id)
.map(user -> ResponseEntity.ok(user))
.defaultIfEmpty(ResponseEntity.notFound().build());
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Mono<User> createUser(@RequestBody User user) {
return userService.save(user);
}
}
13.3 响应式请求映射
Spring WebFlux中的请求映射注解与Spring MVC基本相同,但处理方法可以返回响应式类型。
@RestController
@RequestMapping("/api/reactive")
public class ReactiveController {
@GetMapping("/stream")
public Flux<ServerSentEvent<String>> streamEvents() {
return Flux.interval(Duration.ofSeconds(1))
.map(seq -> ServerSentEvent.<String>builder()
.id(String.valueOf(seq))
.event("periodic-event")
.data("Hello, World! " + LocalTime.now())
.build());
}
@PostMapping("/upload")
public Mono<Void> handleFileUpload(@RequestBody Flux<DataBuffer> file) {
return file
.map(buffer -> {
// 处理文件内容
return buffer;
})
.then();
}
}
十四、控制器类的监控与调试
14.1 Actuator集成
Spring Boot Actuator提供了对应用的监控和管理功能,通过添加spring-boot-starter-actuator
依赖可以启用这些功能。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
常用的Actuator端点包括:
/actuator/health
:应用健康状态/actuator/info
:应用信息/actuator/metrics
:应用指标/actuator/mappings
:请求映射信息/actuator/httptrace
:HTTP请求追踪
14.2 自定义健康指示器
可以通过实现HealthIndicator
接口来自定义健康检查逻辑:
@Component
public class DatabaseHealthIndicator implements HealthIndicator {
private final DataSource dataSource;
public DatabaseHealthIndicator(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public Health health() {
try (Connection connection = dataSource.getConnection()) {
return Health.up().withDetail("database", "reachable").build();
} catch (SQLException e) {
return Health.down().withException(e).build();
}
}
}
14.3 性能监控与分析
可以使用工具如VisualVM、YourKit或Spring Boot Admin来监控控制器类的性能。
Spring Boot Admin是一个开源项目,提供了对Spring Boot应用的集中管理和监控:
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>2.6.6</version>
</dependency>
配置文件中添加:
spring.boot.admin.client.url=http://localhost:8080
management.endpoints.web.exposure.include=*
14.4 调试技巧
在开发和调试控制器类时,可以使用以下技巧:
- 日志调试:在控制器方法中添加日志输出,查看请求参数和处理过程。
@RestController
@RequestMapping("/api")
public class DebugController {
private static final Logger logger = LoggerFactory.getLogger(DebugController.class);
@PostMapping("/users")
public User createUser(@RequestBody User user) {
logger.debug("Creating user: {}", user);
return userService.createUser(user);
}
}
-
断点调试:在IDE中设置断点,逐步调试控制器方法。
-
使用Postman或curl测试:使用工具发送HTTP请求,测试控制器的行为。
curl -X POST "http://localhost:8080/api/users" -H "Content-Type: application/json" -d '{"name":"John Doe","email":"john@example.com"}'
十五、控制器类的最佳实践
15.1 单一职责原则
控制器类应该遵循单一职责原则,只负责处理HTTP请求和返回响应,不应该包含业务逻辑。
@RestController
@RequestMapping("/api/orders")
public class OrderController {
private final OrderService orderService;
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
@PostMapping
public ResponseEntity<Order> createOrder(@RequestBody OrderRequest request) {
// 验证请求
Order order = orderService.createOrder(request);
return ResponseEntity.status(HttpStatus.CREATED).body(order);
}
// 其他方法...
}
15.2 统一异常处理
使用@ControllerAdvice
和@ExceptionHandler
实现统一的异常处理,避免在每个控制器中重复处理异常。
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleResourceNotFoundException(ResourceNotFoundException ex) {
ErrorResponse response = new ErrorResponse(HttpStatus.NOT_FOUND.value(), ex.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response);
}
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<ErrorResponse> handleIllegalArgumentException(IllegalArgumentException ex) {
ErrorResponse response = new ErrorResponse(HttpStatus.BAD_REQUEST.value(), ex.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
}
// 其他异常处理方法...
}
15.3 输入验证
使用Spring的验证框架对输入参数进行验证,确保数据的有效性。
@RestController
@RequestMapping("/api/users")
public class UserController {
@PostMapping
public ResponseEntity<User> createUser(@Valid @RequestBody User user, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
List<String> errors = bindingResult.getAllErrors().stream()
.map(ObjectError::getDefaultMessage)
.collect(Collectors.toList());
return ResponseEntity.badRequest().body(errors);
}
User savedUser = userService.saveUser(user);
return ResponseEntity.status(HttpStatus.CREATED).body(savedUser);
}
}
15.4 版本控制
为API提供版本控制,确保API的向后兼容性。
@RestController
@RequestMapping("/api/v1/users")
public class UserV1Controller {
// V1版本的API实现
}
@RestController
@RequestMapping("/api/v2/users")
public class UserV2Controller {
// V2版本的API实现
}
15.5 文档化API
使用SpringDoc或Swagger等工具为API生成文档,方便客户端开发人员使用。
@RestController
@RequestMapping("/api/users")
@Api(tags = "用户管理")
public class UserController {
@GetMapping("/{id}")
@ApiOperation("获取用户详情")
@ApiResponses({
@ApiResponse(code = 200, message = "成功", response = User.class),
@ApiResponse(code = 404, message = "用户不存在")
})
public ResponseEntity<User> getUser(@PathVariable Long id) {
// 实现逻辑
}
}
十六、控制器类的性能优化
16.1 异步请求处理
对于耗时操作,使用异步请求处理可以释放Servlet容器线程,提高系统吞吐量。
@RestController
@RequestMapping("/api/async")
public class AsyncController {
@GetMapping("/long-task")
public CompletableFuture<ResponseEntity<String>> longTask() {
return CompletableFuture.supplyAsync(() -> {
// 执行耗时操作
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return ResponseEntity.ok("Task completed");
});
}
}
16.2 缓存机制
使用Spring Cache或Redis等缓存技术缓存频繁访问的数据,减少数据库访问。
@Service
public class UserServiceImpl implements UserService {
@Override
@Cacheable("users")
public User getUserById(Long id) {
// 从数据库获取用户
return userRepository.findById(id).orElse(null);
}
@Override
@CacheEvict(value = "users", key = "#user.id")
public User saveUser(User user) {
// 保存用户到数据库
return userRepository.save(user);
}
}
16.3 连接池优化
合理配置数据库连接池参数,避免连接耗尽。
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.idle-timeout=30000
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.max-lifetime=1800000
16.4 懒加载与分页
对于大数据集,使用懒加载和分页技术避免一次性加载过多数据。
@GetMapping("/users")
public ResponseEntity<Page<User>> getUsers(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
Pageable pageable = PageRequest.of(page, size);
Page<User> users = userService.findAll(pageable);
return ResponseEntity.ok(users);
}
16.5 异步日志
使用异步日志记录可以减少日志记录对请求处理的影响。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
配置logback.xml:
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="FILE" />
</appender>
十七、控制器类的测试策略
17.1 单元测试
使用JUnit和Mockito对控制器类进行单元测试,测试控制器方法的逻辑。
@RunWith(SpringRunner.class)
public class UserControllerUnitTest {
private MockMvc mockMvc;
@Mock
private UserService userService;
@InjectMocks
private UserController userController;
@Before
public void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(userController).build();
}
@Test
public void testGetUser() throws Exception {
User user = new User(1L, "John Doe", "john@example.com");
when(userService.getUserById(1L)).thenReturn(user);
mockMvc.perform(get("/api/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.name").value("John Doe"));
}
}
17.2 集成测试
使用Spring MVC Test框架进行集成测试,测试控制器与其他组件的协作。
@RunWith(SpringRunner.class)
@WebMvcTest(UserController.class)
public class UserControllerIntegrationTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
public void testCreateUser() throws Exception {
User user = new User(null, "John Doe", "john@example.com");
User savedUser = new User(1L, "John Doe", "john@example.com");
when(userService.createUser(any(User.class))).thenReturn(savedUser);
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"name\":\"John Doe\",\"email\":\"john@example.com\"}"))
.andExpect(status().isCreated())
.andExpect(header().string("Location", containsString("/api/users/1")));
}
}
17.3 端到端测试
使用Selenium或RestAssured进行端到端测试,测试整个API流程。
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class UserControllerE2ETest {
@LocalServerPort
private int port;
private RestAssured given;
@Before
public void setUp() {
RestAssured.baseURI = "http://localhost:" + port;
}
@Test
public void testCreateAndGetUser() {
User user = new User(null, "John Doe", "john@example.com");
// 创建用户
Response response = given()
.contentType(ContentType.JSON)
.body(user)
.when()
.post("/api/users");
response.then()
.statusCode(HttpStatus.CREATED.value());
String location = response.getHeader("Location");
// 获取用户
given()
.when()
.get(location)
.then()
.statusCode(HttpStatus.OK.value())
.body("name", equalTo("John Doe"));
}
}
17.4 性能测试
使用JMeter或Gatling进行性能测试,测试控制器在高并发下的性能表现。
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class UserControllerPerformanceTest {
@Test
public void testUserEndpointPerformance() {
// 创建100个并发用户
ScenarioBuilder scenarioBuilder = scenario("User Endpoint Performance")
.exec(http("Get User")
.get("/api/users/1")
.check(status().is(200)));
// 设置性能测试参数
setUp(scenarioBuilder.injectOpen(atOnceUsers(100))).protocols(http.baseUrl("http://localhost:8080"));
}
}
十八、控制器类的部署与运维
18.1 打包与部署
将SpringBoot应用打包为可执行JAR或WAR文件,然后部署到服务器上。
# 打包为可执行JAR
mvn clean package
# 运行应用
java -jar target/my-application-0.0.1-SNAPSHOT.jar
18.2 配置管理
使用Spring Boot的配置文件管理应用配置,支持不同环境的配置。
# application.properties
server.port=8080
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=secret
# application-dev.properties (开发环境配置)
spring.profiles.active=dev
logging.level.root=DEBUG
# application-prod.properties (生产环境配置)
spring.profiles.active=prod
logging.level.root=INFO
18.3 监控与告警
集成Prometheus和Grafana进行监控,设置告警规则。
# prometheus.yml
scrape_configs:
- job_name: 'spring-boot'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['localhost:8080']
18.4 日志管理
配置集中式日志管理系统,如ELK Stack或Graylog。
# logback-spring.xml
<appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpAppender">
<destination>logstash:5000</destination>
<encoder class="net.logstash.logback.encoder.LogstashEncoder" />
</appender>
18.5 容器化部署
使用Docker和Kubernetes进行容器化部署,提高应用的可扩展性和可维护性。
# Dockerfile
FROM openjdk:17-jdk-slim
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-boot-app
spec:
replicas: 3
selector:
matchLabels:
app: spring-boot-app
template:
metadata:
labels:
app: spring-boot-app
spec:
containers:
- name: spring-boot-app
image: my-spring-boot-app:1.0.0
ports:
- containerPort: 8080
env:
- name: SPRING_PROFILES_ACTIVE
value: prod