Spring MVC作为Java EE领域最主流的Web框架,其设计精妙之处在于通过清晰的组件分工实现了请求处理的高度可扩展性。在Spring Boot自动配置的便利背后,这套经典架构依然保持着完整的处理链条。让我们先看一个典型的HTTP请求在Spring MVC中的旅程:
/api/users的GET请求这个过程中最关键的抽象在于"适配器模式"的运用——无论Handler是哪种形式(传统Controller、@RequestMapping方法、函数式接口),最终都能被统一处理。这种设计使得Spring MVC既能兼容历史实现,又能优雅支持新特性。
提示:在Spring Boot 2.7+版本中,默认注册的HandlerMapping包括RequestMappingHandlerMapping(优先级0)、BeanNameUrlHandlerMapping(优先级1)和RouterFunctionMapping(优先级2)。了解这个顺序对解决路径冲突问题很重要。
在传统Servlet容器中,DispatcherServlet的初始化遵循标准的Servlet生命周期:
java复制// 伪代码展示初始化时序
public class DispatcherServlet extends FrameworkServlet {
// 1. Servlet容器调用此方法
public void init(ServletConfig config) {
// 2. 初始化父级上下文
initWebApplicationContext();
// 3. 初始化策略组件
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
// 4. 初始化九大核心组件
initHandlerMappings(context);
initHandlerAdapters(context);
// ...其他组件初始化
}
}
而在Spring Boot环境中,这个流程通过自动配置变得更加简洁:
每个策略组件的初始化都遵循"配置优先,默认兜底"的原则:
java复制protected void initHandlerMappings(ApplicationContext context) {
// 1. 尝试从容器获取所有HandlerMapping类型的Bean
Map<String, HandlerMapping> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
context, HandlerMapping.class, true, false);
// 2. 如果容器中没有,使用默认策略
if (beans.isEmpty()) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
} else {
// 3. 对获取到的HandlerMapping进行排序
this.handlerMappings = new ArrayList<>(beans.values());
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
默认策略定义在DispatcherServlet.properties文件中:
code复制org.springframework.web.servlet.HandlerMapping=\
org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
org.springframework.web.servlet.function.support.RouterFunctionMapping
| 组件类型 | 默认实现类 | 主要职责 |
|---|---|---|
| HandlerMapping | RequestMappingHandlerMapping | 处理@RequestMapping注解的控制器方法 |
| HandlerAdapter | RequestMappingHandlerAdapter | 执行控制器方法 |
| ViewResolver | InternalResourceViewResolver | 解析视图名称到JSP等资源 |
| HandlerExceptionResolver | ExceptionHandlerExceptionResolver | 处理控制器抛出的异常 |
当请求到达时,DispatcherServlet会遍历所有HandlerMapping,其查找过程包含三个关键层次:
以RequestMappingHandlerMapping为例,其匹配逻辑的核心代码:
java复制public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping {
protected HandlerMethod getHandlerInternal(HttpServletRequest request) {
// 1. 获取请求路径(考虑Servlet映射路径)
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
// 2. 加读锁保证线程安全
this.mappingRegistry.acquireReadLock();
try {
// 3. 查找匹配的HandlerMethod
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
} finally {
this.mappingRegistry.releaseReadLock();
}
}
}
java复制@Component("/oldController")
public class LegacyController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) {
// 实现逻辑
}
}
注册特点:
java复制@RestController
@RequestMapping("/api")
public class ModernController {
@GetMapping("/users")
public List<User> listUsers() {
// 实现逻辑
}
}
注册特点:
java复制@Configuration
public class WebConfig {
@Bean
public RouterFunction<ServerResponse> routerFunction() {
return route()
.GET("/user/{id}", this::getUser)
.build();
}
private Mono<ServerResponse> getUser(ServerRequest request) {
// 实现逻辑
}
}
注册特点:
Spring MVC 5.3+引入了AntPathMatcher的替代方案PathPatternParser,两者主要区别:
| 特性 | AntPathMatcher | PathPatternParser |
|---|---|---|
| 匹配算法 | 字符串逐段匹配 | 预解析为路径元素链表 |
| 性能 | 较慢 | 快约6-8倍 |
| **匹配规则 | 支持 | 支持 |
| 通配符位置 | 可在任意位置 | 必须在路径段内 |
| 后缀模式 | 支持 | 不支持 |
启用PathPatternParser的方式:
java复制@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setPatternParser(new PathPatternParser());
}
}
HandlerAdapter的设计完美体现了适配器模式的威力,它解决了不同处理器接口的统一调用问题。核心接口定义:
java复制public interface HandlerAdapter {
// 是否支持该处理器
boolean supports(Object handler);
// 执行处理器并返回ModelAndView
ModelAndView handle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception;
// 支持Last-Modified头处理
long getLastModified(HttpServletRequest request, Object handler);
}
这是最复杂的适配器,其处理过程可分为三个阶段:
准备阶段:
执行阶段:
收尾阶段:
核心方法invokeHandlerMethod的简化逻辑:
java复制protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
// 1. 包装请求和响应
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
// 2. 创建数据绑定工厂
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
// 3. 创建模型工厂
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// 4. 创建方法执行器
ServletInvocableHandlerMethod invocableMethod =
createInvocableHandlerMethod(handlerMethod);
// 5. 配置参数解析器
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
// 6. 配置返回值处理器
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
// 7. 执行方法
invocableMethod.invokeAndHandle(webRequest, mavContainer);
// 8. 返回处理结果
return getModelAndView(mavContainer, modelFactory, webRequest);
} finally {
webRequest.requestCompleted();
}
}
假设我们需要支持一种新的处理器类型:
java复制public interface JsonRpcHandler {
Object handleJsonRpc(Map<String, Object> params);
}
实现对应的适配器:
java复制public class JsonRpcHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return handler instanceof JsonRpcHandler;
}
@Override
public ModelAndView handle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
// 1. 解析JSON-RPC请求
Map<String, Object> params = parseJsonRequest(request);
// 2. 调用处理器
JsonRpcHandler jsonRpcHandler = (JsonRpcHandler) handler;
Object result = jsonRpcHandler.handleJsonRpc(params);
// 3. 返回JSON响应
writeJsonResponse(response, result);
return null; // 表示已直接处理响应
}
// 其他辅助方法...
}
注册自定义适配器:
java复制@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureHandlerAdapters(List<HandlerAdapter> adapters) {
adapters.add(new JsonRpcHandlerAdapter());
}
}
Spring MVC提供了超过30种内置的参数解析器,它们被组织在HandlerMethodArgumentResolverComposite中。解析器的判断逻辑:
java复制public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
private final List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>();
@Override
public boolean supportsParameter(MethodParameter parameter) {
return getArgumentResolver(parameter) != null;
}
@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
// 先查缓存
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result == null) {
// 遍历所有解析器
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
if (resolver.supportsParameter(parameter)) {
result = resolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}
}
核心逻辑:
java复制public class RequestParamMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
// 1. 获取参数名称
String name = getParameterName(parameter);
// 2. 获取请求参数值
Object arg = resolveName(name, parameter, webRequest);
// 3. 类型转换
if (arg == null) {
if (hasDefaultValue(parameter)) {
arg = resolveDefaultValue(parameter.getDefaultValue());
} else if (parameter.isOptional()) {
arg = (parameter.getParameterType() == Optional.class ? Optional.empty() : null);
}
}
// 4. 数据绑定和验证
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, null, name);
arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
}
return arg;
}
}
处理流程:
关键代码:
java复制public class RequestResponseBodyMethodProcessor implements HandlerMethodArgumentResolver {
@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
// 1. 获取请求体
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
// 2. 选择消息转换器
for (HttpMessageConverter<?> converter : this.messageConverters) {
if (converter.canRead(parameter.getParameterType(), parameter.getContainingClass(),
inputMessage.getHeaders().getContentType())) {
// 3. 读取并转换
Object body = ((HttpMessageConverter<Object>) converter).read(
parameter.getParameterType(), inputMessage);
// 4. 验证
if (binderFactory != null) {
String name = Conventions.getVariableNameForParameter(parameter);
WebDataBinder binder = binderFactory.createBinder(webRequest, body, name);
if (body != null) {
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors()) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
}
}
}
return body;
}
}
throw new HttpMediaTypeNotSupportedException(...);
}
}
实现一个从JWT令牌中解析用户信息的解析器:
java复制public class JwtUserArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(JwtUser.class)
&& User.class.isAssignableFrom(parameter.getParameterType());
}
@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
String token = request.getHeader("Authorization");
if (token != null && token.startsWith("Bearer ")) {
String jwt = token.substring(7);
return JwtUtils.parseUser(jwt); // 解析JWT获取用户信息
}
throw new MissingRequestHeaderException("Authorization", parameter);
}
}
注册自定义解析器:
java复制@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new JwtUserArgumentResolver());
}
}
Spring MVC的返回值处理采用责任链模式,主要处理器类型包括:
当方法标注@ResponseBody时,RequestResponseBodyMethodProcessor会:
关键代码:
java复制public class RequestResponseBodyMethodProcessor implements HandlerMethodReturnValueHandler {
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest) throws Exception {
// 1. 标记请求已处理
mavContainer.setRequestHandled(true);
// 2. 准备响应对象
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
ServletServerHttpResponse outputMessage = new ServletServerHttpResponse(response);
// 3. 选择消息转换器
for (HttpMessageConverter<?> converter : this.messageConverters) {
if (converter.canWrite(returnType.getParameterType(),
returnType.getContainingClass(),
outputMessage.getHeaders().getContentType())) {
// 4. 写入响应
((HttpMessageConverter<Object>) converter).write(
returnValue, returnType.getParameterType(),
outputMessage.getHeaders().getContentType(),
outputMessage);
return;
}
}
throw new HttpMediaTypeNotAcceptableException(...);
}
}
实现一个处理Markdown文本的返回值处理器:
java复制public class MarkdownReturnValueHandler implements HandlerMethodReturnValueHandler {
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return returnType.getMethodAnnotation(MarkdownResponse.class) != null;
}
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest) throws Exception {
// 1. 标记请求已处理
mavContainer.setRequestHandled(true);
// 2. 转换Markdown为HTML
String html = MarkdownUtils.toHtml(returnValue.toString());
// 3. 写入响应
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write(html);
}
}
注册处理器:
java复制@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
handlers.add(new MarkdownReturnValueHandler());
}
}
这是处理@ExceptionHandler注解的核心解析器:
关键代码:
java复制public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver {
@Override
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod, Exception exception) {
// 1. 查找异常处理方法
Method exceptionHandlerMethod = findExceptionHandlerMethod(handlerMethod, exception);
if (exceptionHandlerMethod != null) {
try {
// 2. 创建方法执行器
ServletInvocableHandlerMethod invocableMethod =
new ServletInvocableHandlerMethod(
handlerMethod.getBean(), exceptionHandlerMethod);
// 3. 设置参数解析器
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
// 4. 设置返回值处理器
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
// 5. 执行异常处理方法
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
invocableMethod.invokeAndHandle(request, response, mavContainer, exception);
// 6. 返回处理结果
return getModelAndView(mavContainer, request);
} catch (Throwable invocationEx) {
logger.error("Failed to invoke @ExceptionHandler method", invocationEx);
}
}
return null;
}
}
实现一个将业务异常转换为特定JSON响应的解析器:
java复制public class BusinessExceptionResolver implements HandlerExceptionResolver {
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) {
if (ex instanceof BusinessException) {
try {
BusinessException bex = (BusinessException) ex;
ErrorResponse error = new ErrorResponse(bex.getCode(), bex.getMessage());
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpStatus.BAD_REQUEST.value());
response.getWriter().write(objectMapper.writeValueAsString(error));
return new ModelAndView(); // 空表示已处理
} catch (IOException e) {
return null;
}
}
return null; // 返回null让其他解析器处理
}
}
注册解析器:
java复制@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
resolvers.add(new BusinessExceptionResolver());
}
}
Spring MVC支持多种视图技术,常见的视图解析器包括:
DispatcherServlet的视图解析过程:
java复制public class DispatcherServlet extends FrameworkServlet {
private void render(ModelAndView mv, HttpServletRequest request,
HttpServletResponse response) throws Exception {
// 1. 解析视图名称
View view;
if (mv.isReference()) {
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
} else {
view = mv.getView();
}
// 2. 渲染视图
view.render(mv.getModelInternal(), request, response);
}
protected View resolveViewName(String viewName, Map<String, Object> model,
Locale locale, HttpServletRequest request) throws Exception {
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
return null;
}
}
ContentNegotiatingViewResolver的工作流程:
配置示例:
java复制@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public ViewResolver contentNegotiatingViewResolver(ContentNegotiationManager manager) {
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
resolver.setContentNegotiationManager(manager);
List<ViewResolver> resolvers = new ArrayList<>();
resolvers.add(thymeleafViewResolver());
resolvers.add(jsonViewResolver());
resolver.setViewResolvers(resolvers);
return resolver;
}
@Bean
public ViewResolver jsonViewResolver() {
return (viewName, locale) -> {
if (viewName.startsWith("json:")) {
return new MappingJackson2JsonView();
}
return null;
};
}
}
Spring MVC内部有多处缓存设计:
优化建议:
关键配置参数:
| 参数 | 默认值 | 建议值 | 说明 |
|---|---|---|---|
| spring.mvc.async.request-timeout | 无 | 30000 | 异步请求超时(毫秒) |
| spring.mvc.servlet.load-on-startup | -1 | 1 | DispatcherServlet启动顺序 |
| server.tomcat.max-threads | 200 | 根据CPU核心数调整 | 最大工作线程数 |
最佳实践:
配置示例:
java复制@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/")
.setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS))
.resourceChain(true)
.addResolver(new GzipResourceResolver())
.addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"));
}
}
使用MockMvc进行隔离测试:
java复制@WebMvcTest(UserController.class)
public class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
public void testGetUser() throws Exception {
given(userService.getUser(1L)).willReturn(new User(1L, "test"));
mockMvc.perform(get("/users/1")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("test"));
}
}
使用SpringBootTest进行完整上下文测试:
java复制@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class UserControllerIntegrationTest {
@LocalServerPort
private int port;
@Autowired
private TestRestTemplate restTemplate;
@Test
public void testGetUser() {
ResponseEntity<User> response = restTemplate.getForEntity(
"http://localhost:" + port + "/users/1", User.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody().getName()).isEqualTo("test");
}
}
测试自定义参数解析器:
java复制public class JwtUserArgumentResolverTest {
private JwtUserArgumentResolver resolver = new JwtUserArgumentResolver();
private MockHttpServletRequest request = new MockHttpServletRequest();
private NativeWebRequest webRequest = new ServletWebRequest(request);
@Test
public void testResolveArgument() throws Exception {
request.addHeader("Authorization", "Bearer valid.token");
MethodParameter parameter = new MethodParameter(
TestController.class.getMethod("testMethod", User.class), 0);
User user = (User) resolver.resolveArgument(
parameter, null, webRequest, null);
assertThat(user.getName()).isEqualTo("testUser");
}
@Controller
static class TestController {
public void testMethod(@JwtUser User user) {}
}
}
| 特性 | Spring MVC | WebFlux |
|---|---|---|
| 编程模型 | 命令式 | 响应式 |
| 线程模型 | 每个请求一个线程 | 事件循环+少量线程 |
| 并发支持 | 阻塞IO | 非阻塞IO |
| 适用场景 | 传统关系型数据库 | 高并发IO密集型 |
| 测试场景 | Spring MVC TPS | WebFlux TPS |
|---|---|---|
| CPU密集型(计算圆周率) | 1200 | 800 |
| IO密集型(数据库查询) | 350 | 2800 |
| 混合型(业务逻辑+IO) | 600 | 1800 |
从MVC迁移到WebFlux的建议步骤:
问题现象:
解决方案:
java复制@Bean
public Filter characterEncodingFilter() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
filter.setForceEncoding(true);
return filter;
}
java复制@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
StringHttpMessageConverter converter = new StringHttpMessageConverter(StandardCharsets.UTF_8);
converters.add(0, converter);
}
}
方案一:全局配置
java复制@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("https://example.com")
.allowedMethods("GET", "POST")
.allowCredentials(true);
}
}
方案二:注解配置
java复制@RestController
@RequestMapping("/api")
@CrossOrigin(origins = "https://example.com", maxAge = 3600)
public class ApiController {
// 控制器方法
}
常见问题:
完整配置:
properties复制# application.properties
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=20MB
spring.servlet.multipart.location=/tmp/uploads
处理示例:
java复制@PostMapping("/upload")
public String handleUpload(@RequestParam MultipartFile file) {
if (!file.isEmpty()) {
String fileName = file.getOriginalFilename();
Path path = Paths.get("/uploads", fileName);
Files.copy(file.getInputStream(), path, StandardCopyOption.REPLACE_EXISTING);
return "Upload success";
}
return "Upload failed";
}
实现一个基于数据库配置的路由映射:
java复制public class DatabaseHandlerMapping extends AbstractHandlerMapping {
private final RouteRepository routeRepository;
public DatabaseHandlerMapping(RouteRepository routeRepository) {
this.routeRepository = routeRepository;
}
@Override
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
String path = getUrlPathHelper().getLookupPathForRequest(request);
Route route = routeRepository.findByPath(path);
if (route != null) {
return new DatabaseHandler(route);
}
return null;
}
private static class DatabaseHandler {
private final Route route;
public DatabaseHandler(Route route) {
this.route = route;
}
public String handle() {
return route.getResponse();
}
}
}
适配上述DatabaseHandler:
java复制public class DatabaseHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return handler instanceof DatabaseHandler;
}
@Override
public ModelAndView handle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
DatabaseHandler dbHandler = (DatabaseHandler) handler;
String result = dbHandler.handle();
response.getWriter().write(result);
return null;
}
@Override
public long getLastModified(HttpServletRequest request, Object handler) {
return -1;
}
}
| 扩展点 | 接口 | 用途 |
|---|---|---|
| 请求映射 | HandlerMapping | 自定义请求到处理器的映射 |
| 处理器适配 | HandlerAdapter | 支持新的处理器类型 |
| 参数解析 | HandlerMethodArgumentResolver | 自定义参数绑定 |
| 返回值处理 | HandlerMethodReturnValueHandler | 自定义返回值处理 |
| 视图解析 | ViewResolver | 支持新的视图技术 |
| 异常处理 | HandlerExceptionResolver | 自定义异常处理 |
| 类型转换 | Converter | 自定义参数类型转换 |
| 格式化 | Formatter | 自定义数据格式化 |
资源命名:
HTTP方法使用:
| 方法 | 用途 |
|---|---|
| GET | 获取资源 |
| POST | 创建资源 |
| PUT | 全量更新资源 |
| PATCH | 部分更新资源 |
| DELETE | 删除资源 |
响应状态码:
| 状态码 | 含义 |
|---|---|
| 200 | 成功请求 |
| 201 | 资源创建成功 |
| 204 | 无内容(删除成功) |
| 400 | 客户端错误 |
| 401 | 未授权 |
| 403 | 禁止访问 |
| 404 | 资源不存在 |
| 500 | 服务器错误 |
输入验证:
输出编码:
CSRF防护:
安全头配置:
java复制@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.headers()
.contentSecurityPolicy("default-src 'self'")
.and()
.xssProtection()
.and()
.httpStrictTransportSecurity();
}
}