主要组件
HttpServletBean
是spring对HttpServlet
简单扩展包含多环境的操作FrameworkServlet
实现了ApplicationContextAware
,所以包含了上下文WebApplicationContext
DispatcherServlet
HTTP请求处理程序/控制器的中央调度程序,有自身的组件处理请求
DispatcherServlet 组件
组件 | 描述 |
---|---|
Handler | 处理器,也就是controller 层,可以是类,也可以是方法,只要是有@RequestMapping 注解的 |
HandlerMapping | 根据请求,找到对应的Handler |
HandlerAdapter | 适配器,将Servlet中的Request和Response转换为Handler 处理的形式 |
HandlerExceptionResolver | 异常处理的组件 |
LocaleResolver | 从Request中解析Locale |
ViewResolver | 解析视图,将视图名(Handler返回)和Locale(LocaleResolver 获得)转换成View |
RequestToViewNameTranslator | 如果用户未提供View或视图名称,则配置的RequestToViewNameTranslator将当前请求转换为视图名称 |
ThemeResolver | 解析主题的组件 |
MultipartResolver | 文件上传组件,将文件转换为MultipartFile 并封装到MultipartHttpServletRequest 中 |
FlashMapManager | 用来管理FlashMap的,FlashMap主要用在redirect中传递参数 |
/**
* Initialize the strategy objects that this servlet uses.
* DispatcherServlet 从应用程序上下文(ApplicationContext )中加载自身使用的组件
* <p>May be overridden in subclasses in order to initialize further strategyobjects.
*/
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
请求处理流程
- 发送请求到DispatchServlet(中央控制器)
- DispatcherServlet(中央控制器)调用HandlerMapping(处理器映射器)根据请求url找到需要执行的处理器(此处做了简化实际返回的是执行链)
- DispatcherServlet(中央控制器)通过HandlerAdapter(处理器适配器)调用处理器
- 执行处理器,返回ModelAndView给中央控制器
- 中央控制器调用ViewResolver(视图解析器)根据处理器返回的ModelAndView中的逻辑视图名为中央控制器返回一个可用的view实例
- 中央控制器根据View渲染视图(将模型填充到视图),并响应给用户
自定义设置
实现WebMvcConfigurer
可以覆盖默认的设置。会让配置文件中关于MVC的配置失效。
package com.example.demo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@EnableWebMvc
public class SpringMVCConfig implements WebMvcConfigurer {
}
自定义入参转换
/**
* 入参转换
*
* @param registry 对象
*/
@Override
public void addFormatters(FormatterRegistry registry) {
// 不能替换为lambda表达式 无法类型推断
// 将“2021-01-20 12:00:00”格式装换为LocalDateTime
registry.addConverter(new Converter<String, LocalDateTime>() {
@Override
public LocalDateTime convert(String source) {
return LocalDateTime.parse(source, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
});
}
返回值包装
实现ResponseBodyAdvice<Object>
和注解@RestControllerAdvice
对所有返回值增加一层包装
package com.example.demo.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import com.example.demo.domain.Result;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectWriter;
/**
* 处理解析 {@link ResponseBodyAdvice} 统一返回包装器
*
* @author 刘斌
*/
@RestControllerAdvice
public class GlobalResponseBodyHandler implements ResponseBodyAdvice<Object> {
private static final Logger LOGGER = LoggerFactory.getLogger(GlobalResponseBodyHandler.class);
@Autowired
private ObjectWriter objectWriter;
@Override
public boolean supports(@Nullable MethodParameter returnType,
@Nullable Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body,
@Nullable MethodParameter returnType,
@Nullable MediaType selectedContentType,
@Nullable Class<? extends HttpMessageConverter<?>> selectedConverterType,
@Nullable ServerHttpRequest request,
@Nullable ServerHttpResponse response) {
try {
LOGGER.info(objectWriter.writeValueAsString(body));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
//返回值为空
if (body == null) {
return new Result();
}
//返回值为result
if (body instanceof Result) {
return body;
}
// 将方法原本的返回值设置到obj中
final Result result = new Result();
result.setObj(body);
return result;
}
}
成功在Controller
返回值上包装了一层通用返回值
返回值转换
将上图中LocalDateTime
转换成字符串格式。还是在SpringMVCConfig
中重写configureMessageConverters
方法
/**
* jackSon 的 ObjectMapper
*
* @return ObjectMapper
*/
@Bean("myObjectMapper")
public ObjectMapper objectMapper() {
// 序列化设置
final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectMapper.setTimeZone(TimeZone.getDefault());
// 序列换成json时,将所有的long变成string
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
// 日期序列化设置
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(
"yyyy-MM-dd HH:mm:ss")));
objectMapper.registerModule(simpleModule)
.registerModule(javaTimeModule);
return objectMapper;
}
/**
* 自定义的ObjectMapper 的转换器
*/
@Autowired
@Qualifier("myObjectMapper")
private ObjectMapper objectMapper;
/**
* jackson json 转换器
*
* @return 对象
*/
public MappingJackson2HttpMessageConverter customJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
jsonConverter.setObjectMapper(objectMapper);
return jsonConverter;
}
/**
* 全局的消息转换
*
* @param converters 集合
*/
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(customJackson2HttpMessageConverter());
}
统一异常处理
通过@RestControllerAdvice
注解,捕获Controller
中的异常
-@ExceptionHandler
捕获的异常类型。抛出的RuntimeException
会被handleRuntimeException
方法处理。最小范围捕获
-@ResponseStatus
设置响应的状态
package com.example.demo.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import com.example.demo.domain.Result;
/**
* 全局异常捕获。
*
* @author liubin
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Logger LOGGER =
LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 全局异常捕获。
*
* @param exception 异常。
* @return re
*/
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Result handleException(Exception exception) {
final Result result = new Result();
LOGGER.error("ApiException 异常抛出", exception);
result.setSucceeded(false);
result.setMsg("服务器崩溃了....");
return result;
}
/**
* 全局RuntimeException 异常捕获。
*
* @param exception 异常。
* @return re
*/
@ExceptionHandler(RuntimeException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Result handleRuntimeException(RuntimeException exception) {
final Result result = new Result();
LOGGER.error("ApiException RuntimeException 异常抛出", exception);
result.setSucceeded(false);
result.setMsg("RuntimeException 服务器崩溃了....");
return result;
}
}