03-HandlerAdapter
applyPre***()
处理的是集合- 进入后
pre***()
处理单个
1. adapter
public interface Controller {
}
// ------------------------------------------------------
class SimpleController implements Controller {
public void doSimplerHandler() {
System.out.println("doSimplerHandler()...");
}
}
class HttpController implements Controller {
public void doHttpHandler() {
System.out.println("doHttpHandler()...");
}
}
public interface HandlerAdapter {
boolean supports(Object handler);
void handle(Object handler);
}
// ------------------------------------------------------
class SimpleHandlerAdapter implements HandlerAdapter {
public boolean supports(Object handler) {
return (handler instanceof SimpleController);
}
public void handle(Object handler) {
((SimpleController) handler).doSimplerHandler();
}
}
class HttpHandlerAdapter implements HandlerAdapter {
public boolean supports(Object handler) {
return (handler instanceof HttpController);
}
public void handle(Object handler) {
((HttpController) handler).doHttpHandler();
}
}
/**
* SpringMVC中使用适配器模式,controller类型不同,有多重实现方式,调用方法不确定
* 如果不使用适配器模式,就需要在执行时添加一系列的分支判断
* 如果新增一个controller类型,需要在代码中添加一个分支判断,太难以维护
* 适配器模式,让每一个适配器对应一种controller类型,扩展时只需要增加一个适配器即可
*/
public class AdapterTest {
public static List<HandlerAdapter> handlerAdapters = new ArrayList<>();
public AdapterTest() {
handlerAdapters.add(new SimpleHandlerAdapter());
handlerAdapters.add(new HttpHandlerAdapter());
// 1.
handlerAdapters.add(new AnnotationHandlerAdapter());
}
public void doDispatch() {
HttpController controller = new HttpController();
// AnnotationController controller = new AnnotationController();
// SimpleController controller = new SimpleController();
// 得到对应适配器
HandlerAdapter adapter = getHandler(controller);
// 适配器执行对应的controller方法
adapter.handle(controller);
}
public HandlerAdapter getHandler(Controller controller) {
for (HandlerAdapter adapter : handlerAdapters) {
if (adapter.supports(controller)) {
return adapter;
}
}
return null;
}
public static void main(String[] args) {
new AdapterTest().doDispatch();
}
}
1. 拓展Controller
public class AnnotationController implements Controller {
public void doAnnotationHandler() {
System.out.println("doAnnotationHandler()...");
}
}
public class AnnotationHandlerAdapter implements HandlerAdapter {
public boolean supports(Object handler) {
return (handler instanceof AnnotationController);
}
public void handle(Object handler) {
((AnnotationController) handler).doAnnotationHandler();
}
}
2. getHandlerAdapter()
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
org.springframework.web.servlet.function.support.HandlerFunctionAdapter
@SuppressWarnings("serial")
public class DispatcherServlet extends FrameworkServlet {
/**
* 处理器适配器,用于执行处理器
* <p>
* List of HandlerAdapters used by this servlet.
*/
@Nullable
private List<HandlerAdapter> handlerAdapters;
/**
* 处理实际的分发到处理器中
* 内层是捕获在对请求进行处理的过程中抛出的异常,在处理异常的时候会设置到dispatcherException变量,然后在processorDispatcherResult方法中进行处理
* 外层是处理渲染页面时抛出的异常,主要是处理processDispatchResult方法抛出的异常
* <p>
* Process the actual dispatching to the handler.
* <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
* to find the first that supports the handler class.
* <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
* themselves to decide which methods are acceptable.
*
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception in case of any kind of processing failure
*/
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 实际处理时所用的request,如果不是上传请求,则直接使用接收到的request,否则封装成上传类型的request
HttpServletRequest processedRequest = request;
// 处理请求的处理器链(包含处理器和对应的interceptor)
HandlerExecutionChain mappedHandler = null;
// 是不是上传请求的标志
boolean multipartRequestParsed = false;
// 获取异步管理器
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
// 封装model和view的容器
ModelAndView mv = null;
// 处理请求过程中抛出的异常,但是不包含渲染过程中抛出的异常
Exception dispatchException = null;
try {
// 1. 上传请求,则通过multipartResolver将其封装成MultipartHttpServletRequest
processedRequest = checkMultipart(request);
// 设置上传请求标志
multipartRequestParsed = (processedRequest != request);
// 2. Determine handler for the current request.
// 获取请求对应的HandlerExecutionChain对象(HandlerMethod、HandlerInterceptor)
mappedHandler = getHandler(processedRequest);
// 获取不到,则根据配置抛出异常、返回404错误
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 3. Determine handler adapter for the current request.
// 获得当前handler对应的HandlerAdapter对象
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 4. Process last-modified header, if supported by the handler.
// 处理GET、HEAD请求的Last-Modified
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
// 获取请求中Service端最后被修改时间
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 5. 执行响应的Interceptor的preHandler
// 注意:该方法如果有一个拦截器的前置处理返回false,则开始倒序触发所有的拦截器的 已完成处理
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 6. Actually invoke the handler.
// 真正的调用handler方法,也就是执行对应的方法,并返回视图
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 7. 需要异步处理,直接返回
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 8. 当view为空时,根据request设置默认的view
applyDefaultViewName(processedRequest, mv);
// 9. 执行响应的interceptor的postHandler
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception ex) {
// 记录异常
dispatchException = ex;
} catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 10. 处理返回结果,包括处理异常、渲染页面、触发Interceptor的afterCompletion
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
} catch (Exception ex) {
// 已完成处理 拦截器
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
} catch (Throwable err) {
// 完成处理激活触发器
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
// 11. 释放资源
finally {
// 判断是否执行异步请求
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else {
// Clean up any resources used by a multipart request.
// 删除上传请求的资源
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
/**
* Return the HandlerAdapter for this handler object.
*
* @param handler the handler object to find an adapter for
* @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
*/
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
// 1. AbstractHandlerMethodAdapter
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
}
1. SimpleControllerHandlerAdapter
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
// 判断是 Controller 类型
return (handler instanceof Controller);
}
@Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// Controller 类型的调用
return ((Controller) handler).handleRequest(request, response);
}
@Override
public long getLastModified(HttpServletRequest request, Object handler) {
// 处理器实现了 LastModified 接口的情况下
if (handler instanceof LastModified) {
return ((LastModified) handler).getLastModified(request);
}
return -1L;
}
}
2. HttpRequestHandlerAdapter
public class HttpRequestHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
// 判断是 HttpRequestHandler 类型
return (handler instanceof HttpRequestHandler);
}
@Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// HttpRequestHandler 类型的调用
((HttpRequestHandler) handler).handleRequest(request, response);
return null;
}
@Override
public long getLastModified(HttpServletRequest request, Object handler) {
// 处理器实现了 LastModified 接口的情况下
if (handler instanceof LastModified) {
return ((LastModified) handler).getLastModified(request);
}
return -1L;
}
}
3. RequestMappingHandlerAdapter
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
implements BeanFactoryAware, InitializingBean {
/**
* MethodFilter that matches {@link InitBinder @InitBinder} methods.
*/
public static final MethodFilter INIT_BINDER_METHODS = method ->
AnnotatedElementUtils.hasAnnotation(method, InitBinder.class);
/**
* MethodFilter that matches {@link ModelAttribute @ModelAttribute} methods.
*/
public static final MethodFilter MODEL_ATTRIBUTE_METHODS = method ->
(!AnnotatedElementUtils.hasAnnotation(method, RequestMapping.class) &&
AnnotatedElementUtils.hasAnnotation(method, ModelAttribute.class));
private List<HttpMessageConverter<?>> messageConverters;
private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager();
// 用于保存实现了ResponseBodyAdvice接口,可以修改返回的ResponseBody的类
// JsonViewRequestBodyAdvice, JsonViewResponseBodyAdvice
private final List<Object> requestResponseBodyAdvice = new ArrayList<>();
// 用于给处理器方法和注释了@ModelAttribute的方法设置参数
@Nullable
private HandlerMethodArgumentResolverComposite argumentResolvers;
// 用于将处理器的返回值处理成ModelAndView的类型
@Nullable
private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
// 用于给注释了@initBinder的方法设置参数
@Nullable
private HandlerMethodArgumentResolverComposite initBinderArgumentResolvers;
public RequestMappingHandlerAdapter() {
// 初始化 messageConverters
this.messageConverters = new ArrayList<>(4);
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(new StringHttpMessageConverter());
try {
this.messageConverters.add(new SourceHttpMessageConverter<>());
} catch (Error err) {
// Ignore when no TransformerFactory implementation is available
}
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
}
public void setRequestBodyAdvice(@Nullable List<RequestBodyAdvice> requestBodyAdvice) {
if (requestBodyAdvice != null) {
this.requestResponseBodyAdvice.addAll(requestBodyAdvice);
}
}
public void setResponseBodyAdvice(@Nullable List<ResponseBodyAdvice<?>> responseBodyAdvice) {
if (responseBodyAdvice != null) {
this.requestResponseBodyAdvice.addAll(responseBodyAdvice);
}
}
@Override
public void afterPropertiesSet() {
// 1.. Do this first, it may add ResponseBody advice beans
initControllerAdviceCache();
// 2.. 初始化argumentResolvers
if (this.argumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
// 3. 初始化initBinderArgumentResolvers
if (this.initBinderArgumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
// 4.. 初始化returnValueHandlers
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
private void initControllerAdviceCache() {
// 判断当前应用程序上下文是否为空,如果为空,直接返回
if (getApplicationContext() == null) {
return;
}
// 1... ControllerAdviceBean 扫描@ControllerAdvice的Bean,生成对应的ControllerAdviceBean对象,并将进行排序
List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();
// 遍历ControllerAdviceBean数组
for (ControllerAdviceBean adviceBean : adviceBeans) {
Class<?> beanType = adviceBean.getBeanType();
if (beanType == null) {
throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
}
// 2. 扫描`@ModelAttribute`方法,无`@RequestMapping`注解的方法,添加到`modelAttributeAdviceCache`属性中
// 该类方法用于在执行方法前修改 Model 对象
Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS); // ModelAttribute
if (!attrMethods.isEmpty()) {
this.modelAttributeAdviceCache.put(adviceBean, attrMethods);
}
// 3. 扫描`@InitBinder`方法,添加到`initBinderAdviceCache`属性中
// 该类方法用于在执行方法前初始化数据绑定器
Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS); // InitBinder
if (!binderMethods.isEmpty()) {
this.initBinderAdviceCache.put(adviceBean, binderMethods);
}
// 如果是RequestBodyAdvice或ResponseBodyAdvice的子类,添加到requestResponseBodyAdviceBeans中
if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
requestResponseBodyAdviceBeans.add(adviceBean);
}
}
// 将requestResponseBodyAdviceBeans添加到this.requestResponseBodyAdvice属性中
if (!requestResponseBodyAdviceBeans.isEmpty()) {
this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
}
// 打印日志
if (logger.isDebugEnabled()) {
int modelSize = this.modelAttributeAdviceCache.size();
int binderSize = this.initBinderAdviceCache.size();
int reqCount = getBodyAdviceCount(RequestBodyAdvice.class);
int resCount = getBodyAdviceCount(ResponseBodyAdvice.class);
if (modelSize == 0 && binderSize == 0 && reqCount == 0 && resCount == 0) {
logger.debug("ControllerAdvice beans: none");
} else {
logger.debug("ControllerAdvice beans: " + modelSize + " @ModelAttribute, " + binderSize +
" @InitBinder, " + reqCount + " RequestBodyAdvice, " + resCount + " ResponseBodyAdvice");
}
}
}
/**
* Return the list of argument resolvers to use including built-in resolvers
* and custom resolvers provided via {@link #setCustomArgumentResolvers}.
*/
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30);
// Annotation-based argument resolution
// 1. 添加按注解解析参数的解析器
// @RequestParam解析
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
resolvers.add(new RequestParamMapMethodArgumentResolver());
resolvers.add(new PathVariableMethodArgumentResolver());
resolvers.add(new PathVariableMapMethodArgumentResolver());
resolvers.add(new MatrixVariableMethodArgumentResolver());
resolvers.add(new MatrixVariableMapMethodArgumentResolver());
resolvers.add(new ServletModelAttributeMethodProcessor(false));
// @RequestBody解析
resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
resolvers.add(new RequestHeaderMapMethodArgumentResolver());
resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new SessionAttributeMethodArgumentResolver());
resolvers.add(new RequestAttributeMethodArgumentResolver());
// Type-based argument resolution
// 添加按类型解析参数的解析器
resolvers.add(new ServletRequestMethodArgumentResolver());
resolvers.add(new ServletResponseMethodArgumentResolver());
resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RedirectAttributesMethodArgumentResolver());
resolvers.add(new ModelMethodProcessor());
resolvers.add(new MapMethodProcessor());
resolvers.add(new ErrorsMethodArgumentResolver());
resolvers.add(new SessionStatusMethodArgumentResolver());
resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
// Custom arguments
// 添加自定义参数解析器,主要用于解析自定义类型
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(getCustomArgumentResolvers());
}
// 2. Catch-all
// 最后两个解析器可以解析所有类型的参数
// @RequestParam解析
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
// 无注解引用类型
resolvers.add(new ServletModelAttributeMethodProcessor(true));
return resolvers;
}
/**
* Return the list of return value handlers to use including built-in and
* custom handlers provided via {@link #setReturnValueHandlers}.
*/
private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(20);
// Single-purpose return value types
handlers.add(new ModelAndViewMethodReturnValueHandler());
handlers.add(new ModelMethodProcessor());
handlers.add(new ViewMethodReturnValueHandler());
handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(),
this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager));
handlers.add(new StreamingResponseBodyReturnValueHandler());
handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
this.contentNegotiationManager, this.requestResponseBodyAdvice));
handlers.add(new HttpHeadersReturnValueHandler());
handlers.add(new CallableMethodReturnValueHandler());
handlers.add(new DeferredResultMethodReturnValueHandler());
handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));
// Annotation-based return value types
handlers.add(new ModelAttributeMethodProcessor(false));
// 2. RequestResponseBodyMethodProcessor
// JsonViewRequestBodyAdvice, JsonViewResponseBodyAdvice
handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
this.contentNegotiationManager, this.requestResponseBodyAdvice));
// 1. Multi-purpose return value types
handlers.add(new ViewNameMethodReturnValueHandler());
handlers.add(new MapMethodProcessor());
// Custom return value types
if (getCustomReturnValueHandlers() != null) {
handlers.addAll(getCustomReturnValueHandlers());
}
// Catch-all
if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
} else {
handlers.add(new ModelAttributeMethodProcessor(true));
}
return handlers;
}
public List<HttpMessageConverter<?>> getMessageConverters() {
return this.messageConverters;
}
@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
return true;
}
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
// 3... #3. handleInternal()
}
/**
* This implementation always returns -1. An {@code @RequestMapping} method can
* calculate the lastModified value, call {@link WebRequest#checkNotModified(long)},
* and return {@code null} if the result of that call is {@code true}.
*/
@Override
protected long getLastModifiedInternal(HttpServletRequest request, HandlerMethod handlerMethod) {
return -1;
}
}
1. AbstractHandlerMethodAdapter
public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {
/**
* This implementation expects the handler to be an {@link HandlerMethod}.
*
* @param handler the handler instance to check
* @return whether or not this adapter can adapt the given handler
*/
@Override
public final boolean supports(Object handler) {
// 1... RequestMappingHandlerAdapter
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
/**
* This implementation expects the handler to be an {@link HandlerMethod}.
*/
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 1... RequestMappingHandlerAdapter
return handleInternal(request, response, (HandlerMethod) handler);
}
/**
* This implementation expects the handler to be an {@link HandlerMethod}.
*/
@Override
public final long getLastModified(HttpServletRequest request, Object handler) {
// 1... RequestMappingHandlerAdapter
return getLastModifiedInternal(request, (HandlerMethod) handler);
}
protected abstract long getLastModifiedInternal(HttpServletRequest request, HandlerMethod handlerMethod);
}
2. <mvc:annotation-driven/>
RequestMappingHandlerAdapter
先从IOC中获取。取不到,再从DispatcherServlet.properties
获取
http\://www.springframework.org/schema/mvc=org.springframework.web.servlet.config.MvcNamespaceHandler
http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler
http\://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler
http\://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler
http\://www.springframework.org/schema/task=org.springframework.scheduling.config.TaskNamespaceHandler
http\://www.springframework.org/schema/cache=org.springframework.cache.config.CacheNamespaceHandler
public class MvcNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
// 1.
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser());
registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser());
registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser());
registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser());
registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser());
registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser());
registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser());
registerBeanDefinitionParser("tiles-configurer", new TilesConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("freemarker-configurer", new FreeMarkerConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("script-template-configurer", new ScriptTemplateConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("cors", new CorsBeanDefinitionParser());
}
}
class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
public static final String HANDLER_MAPPING_BEAN_NAME = RequestMappingHandlerMapping.class.getName();
public static final String HANDLER_ADAPTER_BEAN_NAME = RequestMappingHandlerAdapter.class.getName();
public static final String CONTENT_NEGOTIATION_MANAGER_BEAN_NAME = "mvcContentNegotiationManager";
static {
ClassLoader classLoader = AnnotationDrivenBeanDefinitionParser.class.getClassLoader();
javaxValidationPresent = ClassUtils.isPresent("javax.validation.Validator", classLoader);
romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", classLoader);
jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", classLoader);
// 1..
jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader);
jackson2CborPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", classLoader);
gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
}
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext context) {
Object source = context.extractSource(element);
XmlReaderContext readerContext = context.getReaderContext();
CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
context.pushContainingComponent(compDefinition);
// 1.. mediaTypes
RuntimeBeanReference contentNegotiationManager = getContentNegotiationManager(element, source, context);
// 2. RequestMappingHandlerMapping
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
handlerMappingDef.setSource(source);
handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
handlerMappingDef.getPropertyValues().add("order", 0);
handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
if (element.hasAttribute("enable-matrix-variables")) {
Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enable-matrix-variables"));
handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
}
configurePathMatchingProperties(handlerMappingDef, element, context);
// 3. requestMappingHandlerMapping => IOC
readerContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME, handlerMappingDef);
RuntimeBeanReference corsRef = MvcNamespaceUtils.registerCorsConfigurations(null, context, source);
handlerMappingDef.getPropertyValues().add("corsConfigurations", corsRef);
RuntimeBeanReference conversionService = getConversionService(element, source, context);
RuntimeBeanReference validator = getValidator(element, source, context);
RuntimeBeanReference messageCodesResolver = getMessageCodesResolver(element);
RootBeanDefinition bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);
bindingDef.setSource(source);
bindingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
bindingDef.getPropertyValues().add("conversionService", conversionService);
bindingDef.getPropertyValues().add("validator", validator);
bindingDef.getPropertyValues().add("messageCodesResolver", messageCodesResolver);
// 4.. MappingJackson2HttpMessageConverter(Jackson2ObjectMapperFactoryBean => ObjectMapper) => IOC
ManagedList<?> messageConverters = getMessageConverters(element, source, context);
ManagedList<?> argumentResolvers = getArgumentResolvers(element, context);
ManagedList<?> returnValueHandlers = getReturnValueHandlers(element, context);
String asyncTimeout = getAsyncTimeout(element);
RuntimeBeanReference asyncExecutor = getAsyncExecutor(element);
ManagedList<?> callableInterceptors = getInterceptors(element, source, context, "callable-interceptors");
ManagedList<?> deferredResultInterceptors = getInterceptors(element, source, context, "deferred-result-interceptors");
// 5. RequestMappingHandlerAdapter
RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
handlerAdapterDef.setSource(source);
handlerAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// 5.1.
handlerAdapterDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
handlerAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);
// 5.2.
handlerAdapterDef.getPropertyValues().add("messageConverters", messageConverters);
// 5.3... JsonViewRequestBodyAdvice, JsonViewResponseBodyAdvice
addRequestBodyAdvice(handlerAdapterDef);
addResponseBodyAdvice(handlerAdapterDef);
if (element.hasAttribute("ignore-default-model-on-redirect")) {
Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute("ignore-default-model-on-redirect"));
handlerAdapterDef.getPropertyValues().add("ignoreDefaultModelOnRedirect", ignoreDefaultModel);
}
if (argumentResolvers != null) {
handlerAdapterDef.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
}
if (returnValueHandlers != null) {
handlerAdapterDef.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);
}
if (asyncTimeout != null) {
handlerAdapterDef.getPropertyValues().add("asyncRequestTimeout", asyncTimeout);
}
if (asyncExecutor != null) {
handlerAdapterDef.getPropertyValues().add("taskExecutor", asyncExecutor);
}
handlerAdapterDef.getPropertyValues().add("callableInterceptors", callableInterceptors);
handlerAdapterDef.getPropertyValues().add("deferredResultInterceptors", deferredResultInterceptors);
// 6. requestMappingHandlerAdapter => IOC
readerContext.getRegistry().registerBeanDefinition(HANDLER_ADAPTER_BEAN_NAME, handlerAdapterDef);
RootBeanDefinition uriContributorDef =
new RootBeanDefinition(CompositeUriComponentsContributorFactoryBean.class);
uriContributorDef.setSource(source);
uriContributorDef.getPropertyValues().addPropertyValue("handlerAdapter", handlerAdapterDef);
uriContributorDef.getPropertyValues().addPropertyValue("conversionService", conversionService);
String uriContributorName = MvcUriComponentsBuilder.MVC_URI_COMPONENTS_CONTRIBUTOR_BEAN_NAME;
readerContext.getRegistry().registerBeanDefinition(uriContributorName, uriContributorDef);
RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class);
csInterceptorDef.setSource(source);
csInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, conversionService);
RootBeanDefinition mappedInterceptorDef = new RootBeanDefinition(MappedInterceptor.class);
mappedInterceptorDef.setSource(source);
mappedInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, (Object) null);
mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, csInterceptorDef);
String mappedInterceptorName = readerContext.registerWithGeneratedName(mappedInterceptorDef);
RootBeanDefinition methodExceptionResolver = new RootBeanDefinition(ExceptionHandlerExceptionResolver.class);
methodExceptionResolver.setSource(source);
methodExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
methodExceptionResolver.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
methodExceptionResolver.getPropertyValues().add("messageConverters", messageConverters);
methodExceptionResolver.getPropertyValues().add("order", 0);
addResponseBodyAdvice(methodExceptionResolver);
if (argumentResolvers != null) {
methodExceptionResolver.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
}
if (returnValueHandlers != null) {
methodExceptionResolver.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);
}
String methodExResolverName = readerContext.registerWithGeneratedName(methodExceptionResolver);
RootBeanDefinition statusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class);
statusExceptionResolver.setSource(source);
statusExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
statusExceptionResolver.getPropertyValues().add("order", 1);
String statusExResolverName = readerContext.registerWithGeneratedName(statusExceptionResolver);
RootBeanDefinition defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class);
defaultExceptionResolver.setSource(source);
defaultExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
defaultExceptionResolver.getPropertyValues().add("order", 2);
String defaultExResolverName = readerContext.registerWithGeneratedName(defaultExceptionResolver);
context.registerComponent(new BeanComponentDefinition(handlerMappingDef, HANDLER_MAPPING_BEAN_NAME));
context.registerComponent(new BeanComponentDefinition(handlerAdapterDef, HANDLER_ADAPTER_BEAN_NAME));
context.registerComponent(new BeanComponentDefinition(uriContributorDef, uriContributorName));
context.registerComponent(new BeanComponentDefinition(mappedInterceptorDef, mappedInterceptorName));
context.registerComponent(new BeanComponentDefinition(methodExceptionResolver, methodExResolverName));
context.registerComponent(new BeanComponentDefinition(statusExceptionResolver, statusExResolverName));
context.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExResolverName));
// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
MvcNamespaceUtils.registerDefaultComponents(context, source);
context.popAndRegisterContainingComponent();
return null;
}
private RuntimeBeanReference getContentNegotiationManager(
Element element, @Nullable Object source, ParserContext context) {
RuntimeBeanReference beanRef;
if (element.hasAttribute("content-negotiation-manager")) {
String name = element.getAttribute("content-negotiation-manager");
beanRef = new RuntimeBeanReference(name);
}
else {
// 1.
RootBeanDefinition factoryBeanDef = new RootBeanDefinition(ContentNegotiationManagerFactoryBean.class);
factoryBeanDef.setSource(source);
factoryBeanDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// 2..
factoryBeanDef.getPropertyValues().add("mediaTypes", getDefaultMediaTypes());
String name = CONTENT_NEGOTIATION_MANAGER_BEAN_NAME;
context.getReaderContext().getRegistry().registerBeanDefinition(name, factoryBeanDef);
context.registerComponent(new BeanComponentDefinition(factoryBeanDef, name));
beanRef = new RuntimeBeanReference(name);
}
return beanRef;
}
private Properties getDefaultMediaTypes() {
Properties defaultMediaTypes = new Properties();
if (romePresent) {
defaultMediaTypes.put("atom", MediaType.APPLICATION_ATOM_XML_VALUE);
defaultMediaTypes.put("rss", MediaType.APPLICATION_RSS_XML_VALUE);
}
if (jaxb2Present || jackson2XmlPresent) {
defaultMediaTypes.put("xml", MediaType.APPLICATION_XML_VALUE);
}
if (jackson2Present || gsonPresent) {
// 1. "application/json"
defaultMediaTypes.put("json", MediaType.APPLICATION_JSON_VALUE);
}
if (jackson2SmilePresent) {
defaultMediaTypes.put("smile", "application/x-jackson-smile");
}
if (jackson2CborPresent) {
defaultMediaTypes.put("cbor", MediaType.APPLICATION_CBOR_VALUE);
}
return defaultMediaTypes;
}
private ManagedList<?> getMessageConverters(Element element, @Nullable Object source, ParserContext context) {
Element convertersElement = DomUtils.getChildElementByTagName(element, "message-converters");
ManagedList<Object> messageConverters = new ManagedList<>();
if (convertersElement != null) {
messageConverters.setSource(source);
for (Element beanElement : DomUtils.getChildElementsByTagName(convertersElement, "bean", "ref")) {
Object object = context.getDelegate().parsePropertySubElement(beanElement, null);
messageConverters.add(object);
}
}
if (convertersElement == null || Boolean.parseBoolean(convertersElement.getAttribute("register-defaults"))) {
messageConverters.setSource(source);
messageConverters.add(createConverterDefinition(ByteArrayHttpMessageConverter.class, source));
RootBeanDefinition stringConverterDef = createConverterDefinition(StringHttpMessageConverter.class, source);
stringConverterDef.getPropertyValues().add("writeAcceptCharset", false);
messageConverters.add(stringConverterDef);
messageConverters.add(createConverterDefinition(ResourceHttpMessageConverter.class, source));
messageConverters.add(createConverterDefinition(ResourceRegionHttpMessageConverter.class, source));
messageConverters.add(createConverterDefinition(SourceHttpMessageConverter.class, source));
messageConverters.add(createConverterDefinition(AllEncompassingFormHttpMessageConverter.class, source));
if (romePresent) {
messageConverters.add(createConverterDefinition(AtomFeedHttpMessageConverter.class, source));
messageConverters.add(createConverterDefinition(RssChannelHttpMessageConverter.class, source));
}
if (jackson2XmlPresent) {
Class<?> type = MappingJackson2XmlHttpMessageConverter.class;
RootBeanDefinition jacksonConverterDef = createConverterDefinition(type, source);
GenericBeanDefinition jacksonFactoryDef = createObjectMapperFactoryDefinition(source);
jacksonFactoryDef.getPropertyValues().add("createXmlMapper", true);
jacksonConverterDef.getConstructorArgumentValues().addIndexedArgumentValue(0, jacksonFactoryDef);
messageConverters.add(jacksonConverterDef);
}
else if (jaxb2Present) {
messageConverters.add(createConverterDefinition(Jaxb2RootElementHttpMessageConverter.class, source));
}
if (jackson2Present) {
// 1.. MappingJackson2HttpMessageConverter
Class<?> type = MappingJackson2HttpMessageConverter.class;
RootBeanDefinition jacksonConverterDef = createConverterDefinition(type, source);
// 2.. Jackson2ObjectMapperFactoryBean => ObjectMapper
GenericBeanDefinition jacksonFactoryDef = createObjectMapperFactoryDefinition(source);
jacksonConverterDef.getConstructorArgumentValues().addIndexedArgumentValue(0, jacksonFactoryDef);
// 3.
messageConverters.add(jacksonConverterDef);
}
else if (gsonPresent) {
messageConverters.add(createConverterDefinition(GsonHttpMessageConverter.class, source));
}
if (jackson2SmilePresent) {
Class<?> type = MappingJackson2SmileHttpMessageConverter.class;
RootBeanDefinition jacksonConverterDef = createConverterDefinition(type, source);
GenericBeanDefinition jacksonFactoryDef = createObjectMapperFactoryDefinition(source);
jacksonFactoryDef.getPropertyValues().add("factory", new SmileFactory());
jacksonConverterDef.getConstructorArgumentValues().addIndexedArgumentValue(0, jacksonFactoryDef);
messageConverters.add(jacksonConverterDef);
}
if (jackson2CborPresent) {
Class<?> type = MappingJackson2CborHttpMessageConverter.class;
RootBeanDefinition jacksonConverterDef = createConverterDefinition(type, source);
GenericBeanDefinition jacksonFactoryDef = createObjectMapperFactoryDefinition(source);
jacksonFactoryDef.getPropertyValues().add("factory", new CBORFactory());
jacksonConverterDef.getConstructorArgumentValues().addIndexedArgumentValue(0, jacksonFactoryDef);
messageConverters.add(jacksonConverterDef);
}
}
return messageConverters;
}
private RootBeanDefinition createConverterDefinition(Class<?> converterClass, @Nullable Object source) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(converterClass);
beanDefinition.setSource(source);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
return beanDefinition;
}
private GenericBeanDefinition createObjectMapperFactoryDefinition(@Nullable Object source) {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
// 1.
beanDefinition.setBeanClass(Jackson2ObjectMapperFactoryBean.class);
beanDefinition.setSource(source);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
return beanDefinition;
}
protected void addRequestBodyAdvice(RootBeanDefinition beanDef) {
if (jackson2Present) {
// 1.
beanDef.getPropertyValues().add("requestBodyAdvice", new RootBeanDefinition(JsonViewRequestBodyAdvice.class));
}
}
protected void addResponseBodyAdvice(RootBeanDefinition beanDef) {
if (jackson2Present) {
// 1.
beanDef.getPropertyValues().add("responseBodyAdvice", new RootBeanDefinition(JsonViewResponseBodyAdvice.class));
}
}
}
1. Jackson2ObjectMapperFB
package org.springframework.http.converter.json;
public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper>, BeanClassLoaderAware,
ApplicationContextAware, InitializingBean {
private final Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
@Nullable
private ObjectMapper objectMapper;
@Override
public void afterPropertiesSet() {
if (this.objectMapper != null) {
this.builder.configure(this.objectMapper);
}
else {
this.objectMapper = this.builder.build();
}
}
/**
* Return the singleton ObjectMapper.
*/
@Override
@Nullable
public ObjectMapper getObject() {
return this.objectMapper;
}
}
1. Jackson2ObjMapperBuilder
package org.springframework.http.converter.json;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* A builder used to create {@link ObjectMapper} instances with a fluent API.
*/
public class Jackson2ObjectMapperBuilder {
/**
* Build a new {@link ObjectMapper} instance.
* <p>Each build operation produces an independent {@link ObjectMapper} instance.
* The builder's settings can get modified, with a subsequent build operation
* then producing a new {@link ObjectMapper} based on the most recent settings.
* @return the newly built ObjectMapper
*/
@SuppressWarnings("unchecked")
public <T extends ObjectMapper> T build() {
ObjectMapper mapper;
if (this.createXmlMapper) {
mapper = (this.defaultUseWrapper != null ?
new XmlObjectMapperInitializer().create(this.defaultUseWrapper, this.factory) :
new XmlObjectMapperInitializer().create(this.factory));
}
else {
mapper = (this.factory != null ? new ObjectMapper(this.factory) : new ObjectMapper());
}
configure(mapper);
return (T) mapper;
}
}
2. ObjectMapper
package com.fasterxml.jackson.databind;
public class ObjectMapper
extends ObjectCodec
implements Versioned,
java.io.Serializable // as of 2.1
{
private static final long serialVersionUID = 2L; // as of 2.9
/**
* Method similar to {@link #canSerialize(Class)} but that can return
* actual {@link Throwable} that was thrown when trying to construct
* serializer: this may be useful in figuring out what the actual problem is.
*
* @since 2.3
*/
public boolean canSerialize(Class<?> type, AtomicReference<Throwable> cause) {
return _serializerProvider(getSerializationConfig()).hasSerializerFor(type, cause);
}
public boolean canDeserialize(JavaType type, AtomicReference<Throwable> cause)
{
return createDeserializationContext(null,
getDeserializationConfig()).hasValueDeserializerFor(type, cause);
}
/**
* Accessor for getting currently configured {@link TypeFactory} instance.
*/
public TypeFactory getTypeFactory() {
return _typeFactory;
}
@SuppressWarnings("unchecked")
public <T> T readValue(byte[] src, JavaType valueType)
throws IOException, JsonParseException, JsonMappingException
{
_assertNotNull("src", src);
return (T) _readMapAndClose(_jsonFactory.createParser(src), valueType);
}
/**
* Convenience method for constructing {@link ObjectWriter}
* with default settings.
*/
public ObjectWriter writer() {
return _newWriter(getSerializationConfig());
}
/**
* Factory method sub-classes must override, to produce {@link ObjectWriter}
* instances of proper sub-type
*
* @since 2.5
*/
protected ObjectWriter _newWriter(SerializationConfig config) {
return new ObjectWriter(this, config);
}
}
3. ControllerAdviceBean
- 处理
@ControllerAdvice
类
public class ControllerAdviceBean implements Ordered {
/**
* Find beans annotated with {@link ControllerAdvice @ControllerAdvice} in the
* given {@link ApplicationContext} and wrap them as {@code ControllerAdviceBean}
* instances.
* <p>As of Spring Framework 5.2, the {@code ControllerAdviceBean} instances
* in the returned list are sorted using {@link OrderComparator#sort(List)}.
* @see #getOrder()
* @see OrderComparator
* @see Ordered
*/
public static List<ControllerAdviceBean> findAnnotatedBeans(ApplicationContext context) {
List<ControllerAdviceBean> adviceBeans = new ArrayList<>();
for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, Object.class)) {
if (!ScopedProxyUtils.isScopedTarget(name)) {
// 1. @ControllerAdvice
ControllerAdvice controllerAdvice = context.findAnnotationOnBean(name, ControllerAdvice.class);
if (controllerAdvice != null) {
// Use the @ControllerAdvice annotation found by findAnnotationOnBean()
// in order to avoid a subsequent lookup of the same annotation.
adviceBeans.add(new ControllerAdviceBean(name, context, controllerAdvice));
}
}
}
OrderComparator.sort(adviceBeans);
return adviceBeans;
}
}
4. last-modified
// Process last-modified header, if supported by the handler.
// 处理GET、HEAD请求的Last-Modified
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
// 获取请求中Service端最后被修改时间
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
5. applyPreHandle()
HandlerMappingInterceptor#preHandle()
返回false
,执行处理过的HandlerMappingInterceptor
的triggerAfterCompletion()
public class HandlerExecutionChain {
private final Object handler;
@Nullable
private HandlerInterceptor[] interceptors;
@Nullable
private List<HandlerInterceptor> interceptorList;
/**
* Apply preHandle methods of registered interceptors.
* @return {@code true} if the execution chain should proceed with the
* next interceptor or the handler itself. Else, DispatcherServlet assumes
* that this interceptor has already dealt with the response itself.
*/
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
// 1. 前置处理
if (!interceptor.preHandle(request, response, this.handler)) {
// 2.. 已完成前置处理拦截器进行afterCompletion()
triggerAfterCompletion(request, response, null);
// 当前前置处理失败
return false;
}
// 标记interceptorIndex
this.interceptorIndex = i;
}
}
return true;
}
/**
* Apply postHandle methods of registered interceptors.
*/
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
// 遍历拦截器数组,倒序执行
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}
/**
* Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
* Will just invoke afterCompletion for all interceptors whose preHandle invocation
* has successfully completed and returned true.
*/
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
// 1. 倒序,遍历拦截器
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
try {
// 2. 拦截器afterCompletion()
interceptor.afterCompletion(request, response, this.handler, ex);
} catch (Throwable ex2) {
// 执行失败,打印错误日志,不会结束循环
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
}
}
<mvc:interceptors>
<!-- 拦截具体请求 -->
<mvc:interceptor>
<mvc:mapping path="/ctl1"/>
<bean class="com.listao.interceptor.HandlerMappingInterceptor"/>
</mvc:interceptor>
<!-- 拦截所有请求 -->
<!--<bean class="com.listao.interceptor.HandlerMappingInterceptor"/>-->
<!--<bean class="com.listao.interceptor.HandlerMappingInterceptor1"/>-->
</mvc:interceptors>
public class HandlerMappingInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle...");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("postHandle...");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) throws Exception {
System.out.println("afterCompletion()...");
}
}
public class HandlerMappingInterceptor1 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle1...");
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("postHandle1...");
}
}
preHandle...
preHandle1...
afterCompletion()...
3. handleInternal()
afterPropertiesSet()
- 初始化
argumentResolvers
- 初始化
initBinderArgumentResolvers
- 初始化
@Controller
注解难解析原因- 方法名称自定义
- 形参解析
- 方法上各类注解
@RequestBody
@RequestMapping
@RequestParam
@PathVariables
@Validated
@Valid
@CookieValue
@SessionAttribute
@ModelAttribute
@RequestHeader
AbstractHandlerMethodAdapter#handle()
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
implements BeanFactoryAware, InitializingBean {
@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
return true;
}
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
// HttpMethod、Session校验
checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required.
// synchronizeOnSession为true,则对session进行同步
if (this.synchronizeOnSession) {
// 同步相同Session的逻辑,默认情况false
HttpSession session = request.getSession(false);
if (session != null) {
// 获取Session锁对象
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
} else {
// No HttpSession available -> no mutex necessary
mav = invokeHandlerMethod(request, response, handlerMethod);
}
} else {
// 1.. No synchronization on session demanded at all...
mav = invokeHandlerMethod(request, response, handlerMethod);
}
// 响应不包含'Cache-Control'头
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
} else {
prepareResponse(response);
}
}
return mav;
}
/**
* Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
* if view resolution is required.
*
* @see #createInvocableHandlerMethod(HandlerMethod)
* @since 4.2
*/
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
// request、response创建ServletWebRequest,相当于工具类
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
// 1.. 用来创建WebDataBinder对象,进行参数绑定
// 实现参数和String的类型转换,ArgumentResolver在进行参数解析的过程中会用
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
// 2.. 处理model。1-在处理器处理之前对model进行初始化,2-处理完请求后对model参数进行更新
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// 3.. 参数绑定、处理请求、返回值处理
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
// 参数处理器
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
// 返回值处理器
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
// 4. 参数绑定工厂对象
invocableMethod.setDataBinderFactory(binderFactory);
// 参数名称发现器
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
// 保存model、View
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
// flashMap
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
// 5... ModelFactory 将sessionAttributes和注释了@ModelAttribute的方法的参数设置到model中
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
// 根据配置对ignoreDefaultModelOnRedirect进行设置
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
// 创建AsyncWebRequest异步请求对象
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
// WebAsyncManager异步请求管理器
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
// 如果当前异步请求已经处理并得到结果,则将返回的结果放到mavContainer对象中,然后将invocable对象进行包装转换,转成需要的执行对象然后开始执行
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 + "]";
});
// 转换具体的invocable执行对象
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
// 6... ServletInvocableHandlerMethod 执行调用
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
// 7.. 处理完请求后的后置处理,此处一共做了三件事,
// 1. 调用ModelFactory的updateModel方法更新model,包括设置SessionAttribute和给Model设置BinderResult
// 2. 根据mavContainer创建了ModelAndView
// 3. 如果mavContainer里的model是RedirectAttributes类型,则将其设置到FlashMap
return getModelAndView(mavContainer, modelFactory, webRequest);
} finally {
// 标记请求完成
webRequest.requestCompleted();
}
}
}
1. getDataBinderFactory()
@InitBinder
ServletRequestDataBinderFactory
/**
* MethodFilter that matches {@link InitBinder @InitBinder} methods.
*/
public static final MethodFilter INIT_BINDER_METHODS = method ->
AnnotatedElementUtils.hasAnnotation(method, InitBinder.class);
private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
Class<?> handlerType = handlerMethod.getBeanType();
// 检查当前Handler中的initBinder方法是否已经存在于缓存中
Set<Method> methods = this.initBinderCache.get(handlerType);
// 如果没有则查找并设置到缓冲中
if (methods == null) {
// 1... 当前Controller中被@InitBinder修饰方法
methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
this.initBinderCache.put(handlerType, methods);
}
// 定义保存InitBinder方法的临时变量
List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();
// Global methods first
// 2. 全局@InitBinder
this.initBinderAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
Object bean = controllerAdviceBean.resolveBean();
for (Method method : methodSet) {
initBinderMethods.add(createInitBinderMethod(bean, method));
}
}
});
// 3. 合并@InitBinder
for (Method method : methods) {
Object bean = handlerMethod.getBean();
initBinderMethods.add(createInitBinderMethod(bean, method));
}
// 创建DataBinderFactory并返回
return createDataBinderFactory(initBinderMethods);
}
protected InitBinderDataBinderFactory createDataBinderFactory(List<InvocableHandlerMethod> binderMethods)
throws Exception {
// 1.
return new ServletRequestDataBinderFactory(binderMethods, getWebBindingInitializer());
}
1. MethodIntrospector
public final class MethodIntrospector {
private MethodIntrospector() {
}
public static Set<Method> selectMethods(Class<?> targetType, final ReflectionUtils.MethodFilter methodFilter) {
// 1..
return selectMethods(targetType,
(MetadataLookup<Boolean>) method -> (methodFilter.matches(method) ? Boolean.TRUE : null)).keySet();
}
public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) {
final Map<Method, T> methodMap = new LinkedHashMap<>();
Set<Class<?>> handlerTypes = new LinkedHashSet<>();
Class<?> specificHandlerType = null;
if (!Proxy.isProxyClass(targetType)) {
specificHandlerType = ClassUtils.getUserClass(targetType);
handlerTypes.add(specificHandlerType);
}
handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));
for (Class<?> currentHandlerType : handlerTypes) {
final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);
// 2...
ReflectionUtils.doWithMethods(currentHandlerType, method -> {
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
T result = metadataLookup.inspect(specificMethod);
if (result != null) {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
methodMap.put(specificMethod, result);
}
}
}, ReflectionUtils.USER_DECLARED_METHODS);
}
return methodMap;
}
}
2. ReflectionUtils
public abstract class ReflectionUtils {
public static final MethodFilter USER_DECLARED_METHODS =
// 2.2. 当method不是桥接方法 又 不是合成方法时,返回true;否则返回false
(method -> !method.isBridge() && !method.isSynthetic());
public static void doWithMethods(Class<?> clazz, MethodCallback mc, @Nullable MethodFilter mf) {
// 1.. Keep backing up the inheritance hierarchy.
// 从缓存中获取clazz的所有声明的方法,包括它的所有接口中所有默认方法;没有时就从{@code clazz}中获取,再添加到缓存中,
Method[] methods = getDeclaredMethods(clazz, false);
// 遍历所有方法
for (Method method : methods) {
// 2.1.. 如果mf不为null 且 method不满足mf的匹配要求
if (mf != null && !mf.matches(method)) {
// 跳过该method
continue;
}
try {
// 3.. 对method执行回调操作
mc.doWith(method);
} catch (IllegalAccessException ex) {
throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex);
}
}
// 如果clazz的父类不为nulll且(mf不是与未在{@code java.lang.Object}上声明的所有非桥接非合成方法匹配的预购建方法过滤器或者clazz的父类不为Object
if (clazz.getSuperclass() != null && (mf != USER_DECLARED_METHODS || clazz.getSuperclass() != Object.class)) {
// 递归方法
// 执行给定回调操作在clazz的父类的所有匹配方法, 子类和父类发生的相同命名方法将出现两次,
// 子类和父类发生的相同命名方法将出现两次,除非被mf排查
doWithMethods(clazz.getSuperclass(), mc, mf);
}
// 如果clazz是接口
else if (clazz.isInterface()) {
// 遍历clazz的所有接口
for (Class<?> superIfc : clazz.getInterfaces()) {
// 递归方法
// 执行给定回调操作在superIfc的所有匹配方法, 子类和父类发生的相同命名方法将出现两次,
// 子类和父类发生的相同命名方法将出现两次,除非被mf排查
doWithMethods(superIfc, mc, mf);
}
}
}
/**
* 从缓存中获取clazz的所有声明的方法,包括它的所有接口中所有默认方法;没有时就从{@code clazz}中获取,再添加到缓存中,再返回出去
*
* @param clazz
* @param defensive
* @return
*/
private static Method[] getDeclaredMethods(Class<?> clazz, boolean defensive) {
Assert.notNull(clazz, "Class must not be null");
// 1. declaredMethodsCache
Method[] result = declaredMethodsCache.get(clazz);
if (result == null) {
try {
// 2. 获取clazz的所有声明方法
Method[] declaredMethods = clazz.getDeclaredMethods();
// 3. 接口上的默认方法
List<Method> defaultMethods = findConcreteMethodsOnInterfaces(clazz);
if (defaultMethods != null) {
result = new Method[declaredMethods.length + defaultMethods.size()];
System.arraycopy(declaredMethods, 0, result, 0, declaredMethods.length);
int index = declaredMethods.length;
for (Method defaultMethod : defaultMethods) {
result[index] = defaultMethod;
index++;
}
} else {
result = declaredMethods;
}
// 4. 无方法,EMPTY_METHOD_ARRAY添加缓存。减少内存消耗
declaredMethodsCache.put(clazz, (result.length == 0 ? EMPTY_METHOD_ARRAY : result));
} catch (Throwable ex) {
throw new IllegalStateException("Failed to introspect Class [" + clazz.getName() +
"] from ClassLoader [" + clazz.getClassLoader() + "]", ex);
}
}
// 5. clone()
return (result.length == 0 || !defensive) ? result : result.clone();
}
}
1. Synthetic
- 编译后的类可查看。编译器会自动生成一些未知基本方法
- eg:默认构造方法
2. Bridge
- 所有用户自定义方法都不是桥接方法
/**
* 桥接方法:jdk1.5之后引入泛型之后,为了使java的泛型方法生成的字节码和1.5版本前的字节码相兼容,由编译器自动生成的方法
* <p>
* 在使用的时候可以通过Method.isBridge方法来判断一个方法是否是桥接方法,在字节码中桥接方法会被标记为ACC_BRIDGE和ACC_SYNTHETIC
* ACC_BRIDGE说明这个方法是由编译生成的桥接方法
* ACC_SYNTHETIC说明这个方法是由编译器生成的,并不会在源代码中出现
* <p>
* 什么时候编译器会生成桥接方法呢?
* 一个子类在继承或实现一个父类的泛型方法时,在子类中明确指定了泛型类型,那么在编译时编译器会自动生成桥接方法
* <p>
* 大家可以通过 javap -verbose SubClass.class来查看字节码文件
*/
public interface SuperClass<T> {
T method(T param);
}
/*
* 在此方法中只声明了一个方法,但是从字节码中可以看到三个方法,
* 第一个是无参的构造方法,编译器自动生成
* 第二个是实现接口中的方法
* 第三个是编译器自动生成的桥接方法,可以看到那两个标志(ACC_BRIDGE, ACC_SYNTHETIC),同时方法的参数和返回值都是Object类型
*
* 在声明SuperClass类型的变量时,不指定泛型类型,那么在方法调用时就可以传任何类型的参数,因为SuperClass中的方法参数实际上是Object类型,而且
* 编译器也不能够发现错误,但是当运行的时候就会发现参数类型不是Subclass声明的类型,会抛出类型转换异常,因为此时调用的是桥接方法,而在桥接方法中会
* 进行类型强制转换,所以会抛出此异常
*
* 如果在声明的时候直接就指定好了泛型的类型,那么编译的时候就会出现异常情况,这样的话就会把错误提前
*
* 为什么要生成桥接方法呢?
* 在1.5版本之前创建一个集合对象之后,可以向其中放置任何类型的元素,无法确定和限制具体的类型,而引入泛型之后可以指定存放什么类型的数据,
* 泛型在此处起到的作用就是检查向集合中添加的对象类型是否匹配泛型类型,如果不正常,那么在编译的时候就会出错,而不必等到运行时才发现错误
* 但是泛型是在之后的版本中出现的,为了向前兼容,所以会在编译时去掉泛型,但是可以通过反射API来获取泛型的信息,在编译时可以通过泛型来保证
* 类型的正确性,而不必等到运行时才发现类型不正确,正是由于java泛型的擦除特性,如果不生成桥接方法,那么与1.5之前的字节码就不兼容了。
* 因为有了继承关系,如果不生成桥接方法,那么SubClass就没有实现接口中声明的方法,语义就不正确了,所以编译器才会生成桥接方法来保证兼容性
*/
public class SubClass implements SuperClass<String> {
@Override
public String method(String param) {
return param;
}
public static void main(String[] args) {
SuperClass superClass = new SubClass();
System.out.println(superClass.method("123"));
System.out.println(superClass.method(new Object()));
}
}
3. @ControllerAdvice
共享类
@ExceptionHandler
@InitBinder
@ModelAttribute
/**
* 使用@ControllerAdvice注解的Controller是一个增强的Controller,主要有三个功能
* 1. 全局异常处理
* 2. 全局数据绑定
* 3. 全局数据预处理
*/
@ControllerAdvice
public class ControllerAdviceCtl {
// 全局异常处理
@ExceptionHandler(Exception.class)
public ModelAndView customerException(Exception e) {
ModelAndView mv = new ModelAndView();
mv.addObject("message", e.getMessage());
mv.setViewName("error");
return mv;
}
// 全局数据绑定
@ModelAttribute(name = "md")
public Map<String, Object> modelAttribute() {
HashMap<String, Object> map = new HashMap<>();
map.put("age", 99);
map.put("gender", "男");
return map;
}
@InitBinder("a")
public void a(WebDataBinder binder) {
binder.setFieldDefaultPrefix("a.");
}
@InitBinder("b")
public void b(WebDataBinder binder) {
binder.setFieldDefaultPrefix("b.");
}
}
3. ServletReqDataBinderFactory
public class ServletRequestDataBinderFactory extends InitBinderDataBinderFactory {
public ServletRequestDataBinderFactory(@Nullable List<InvocableHandlerMethod> binderMethods,
@Nullable WebBindingInitializer initializer) {
// 1... InitBinderDataBinderFactory
super(binderMethods, initializer);
}
/**
* Returns an instance of {@link ExtendedServletRequestDataBinder}.
*/
@Override
protected ServletRequestDataBinder createBinderInstance(
@Nullable Object target, String objectName, NativeWebRequest request) throws Exception {
// 1.
return new ExtendedServletRequestDataBinder(target, objectName);
}
}
1. DefaultDataBinderFactory
public class DefaultDataBinderFactory implements WebDataBinderFactory {
@Override
@SuppressWarnings("deprecation")
public final WebDataBinder createBinder(
NativeWebRequest webRequest, @Nullable Object target, String objectName) throws Exception {
// 1... ServletRequestDataBinderFactory
WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest);
// WebBindingInitializer 在此处完成解析,全局生效
if (this.initializer != null) {
this.initializer.initBinder(dataBinder, webRequest);
}
// 2... InitBinderDataBinderFactory。执行所有数据绑定器初始化方法,为当前参数的数据绑定器进行初始化
initBinder(dataBinder, webRequest);
return dataBinder;
}
}
2. InitBinderDataBinderFactory
public class InitBinderDataBinderFactory extends DefaultDataBinderFactory {
// 保存所有的初始化数据绑定器的方法
// 是在创建数据绑定工厂的时候解析出来的,包含了Controller内部的和@ControllerAdvice注解标识出的类
private final List<InvocableHandlerMethod> binderMethods;
/**
* Create a new InitBinderDataBinderFactory instance.
*
* @param binderMethods {@code @InitBinder} methods
* @param initializer for global data binder initialization
*/
public InitBinderDataBinderFactory(@Nullable List<InvocableHandlerMethod> binderMethods,
@Nullable WebBindingInitializer initializer) {
super(initializer);
this.binderMethods = (binderMethods != null ? binderMethods : Collections.emptyList());
}
// 保存所有的初始化数据绑定器的方法
// 是在创建数据绑定工厂的时候解析出来的,包含了Controller内部的和@ControllerAdvice注解标识出的类
private final List<InvocableHandlerMethod> binderMethods;
@Override
public void initBinder(WebDataBinder dataBinder, NativeWebRequest request) throws Exception {
for (InvocableHandlerMethod binderMethod : this.binderMethods) {
// 1.. 确定是否应该使用给定的@InitBinder方法初始化给定的WebDataBinder实例
// 默认情况下,检查注解值中指定的属性名是否和参数名匹配
if (isBinderMethodApplicable(binderMethod, dataBinder)) {
// 2... InvocableHandlerMethod 调用@InitBinder方法
Object returnValue = binderMethod.invokeForRequest(request, null, dataBinder);
// 标注有@InitBinder注解方法必须返回void
if (returnValue != null) {
throw new IllegalStateException(
"@InitBinder methods must not return a value (should be void): " + binderMethod);
}
}
}
}
/**
* 判断指定的方式是否需要进行初始化操作
* <p>
* Determine whether the given {@code @InitBinder} method should be used
* to initialize the given {@link WebDataBinder} instance. By default we
* check the specified attribute names in the annotation value, if any.
*/
protected boolean isBinderMethodApplicable(HandlerMethod initBinderMethod, WebDataBinder dataBinder) {
InitBinder ann = initBinderMethod.getMethodAnnotation(InitBinder.class);
Assert.state(ann != null, "No InitBinder annotation");
String[] names = ann.value();
// 1. @InitBinder("***")是否包含属性名
return (ObjectUtils.isEmpty(names) || ObjectUtils.containsElement(names, dataBinder.getObjectName()));
}
}
- User、Student 类中字段一致,通过
@InitBinder
对应的参数绑定前缀不同,可以解决参数绑定问题 http://localhost:8080/getBean?u.name=zhangsan&u.age=18&s.name=lisi&s.age=20
// 参数绑定
@InitBinder("user")
public void init1(WebDataBinder binder) {
binder.setFieldDefaultPrefix("u.");
}
@InitBinder("stu")
public void init2(WebDataBinder binder) {
binder.setFieldDefaultPrefix("s.");
}
@RequestMapping("/getBean")
public ModelAndView getBean(User user, @ModelAttribute("stu") Student stu) {
System.out.println("user = " + user);
System.out.println("stu = " + stu);
String viewName = "success";
ModelAndView modelAndView = new ModelAndView(viewName);
modelAndView.addObject("user", user);
modelAndView.addObject("student", stu);
return modelAndView;
}
4. ExtendedServletReqDataBinder
public class ExtendedServletRequestDataBinder extends ServletRequestDataBinder {
public ExtendedServletRequestDataBinder(@Nullable Object target, String objectName) {
super(target, objectName);
}
}
1. DataBinder
public class DataBinder implements PropertyEditorRegistry, TypeConverter {
@Nullable
private final Object target;
private final String objectName;
@Nullable
private SimpleTypeConverter typeConverter;
public DataBinder(@Nullable Object target, String objectName) {
this.target = ObjectUtils.unwrapOptional(target);
this.objectName = objectName;
}
protected void doBind(MutablePropertyValues mpvs) {
checkAllowedFields(mpvs);
checkRequiredFields(mpvs);
// 1..
applyPropertyValues(mpvs);
}
protected void applyPropertyValues(MutablePropertyValues mpvs) {
try {
// Bind request parameters onto target object.
// 1.. getPropertyAccessor() => BeanWrapperImpl
getPropertyAccessor().setPropertyValues(mpvs, isIgnoreUnknownFields(), isIgnoreInvalidFields());
}
catch (PropertyBatchUpdateException ex) {
// Use bind error processor to create FieldErrors.
for (PropertyAccessException pae : ex.getPropertyAccessExceptions()) {
getBindingErrorProcessor().processPropertyAccessException(pae, getInternalBindingResult());
}
}
}
/**
* Return the underlying PropertyAccessor of this binder's BindingResult.
*/
protected ConfigurablePropertyAccessor getPropertyAccessor() {
// 1.. BeanPropertyBindingResult#getPropertyAccessor() => BeanWrapperImpl
return getInternalBindingResult().getPropertyAccessor();
}
/**
* Return the internal BindingResult held by this DataBinder,
* as an AbstractPropertyBindingResult.
*/
protected AbstractPropertyBindingResult getInternalBindingResult() {
if (this.bindingResult == null) {
// 1..
initBeanPropertyAccess();
}
return this.bindingResult;
}
/**
* Initialize standard JavaBean property access for this DataBinder.
* <p>This is the default; an explicit call just leads to eager initialization.
* @see #initDirectFieldAccess()
* @see #createBeanPropertyBindingResult()
*/
public void initBeanPropertyAccess() {
Assert.state(this.bindingResult == null,
"DataBinder is already initialized - call initBeanPropertyAccess before other configuration methods");
// 1..
this.bindingResult = createBeanPropertyBindingResult();
}
/**
* Create the {@link AbstractPropertyBindingResult} instance using standard
* JavaBean property access.
* @since 4.2.1
*/
protected AbstractPropertyBindingResult createBeanPropertyBindingResult() {
// 1... BeanPropertyBindingResult
BeanPropertyBindingResult result = new BeanPropertyBindingResult(getTarget(),
getObjectName(), isAutoGrowNestedPaths(), getAutoGrowCollectionLimit());
if (this.conversionService != null) {
result.initConversion(this.conversionService);
}
if (this.messageCodesResolver != null) {
result.setMessageCodesResolver(this.messageCodesResolver);
}
return result;
}
@Override
@Nullable
public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
@Nullable MethodParameter methodParam) throws TypeMismatchException {
// 1.
return getTypeConverter().convertIfNecessary(value, requiredType, methodParam);
}
}
1. BeanPropertyBindingResult
public class BeanPropertyBindingResult extends AbstractPropertyBindingResult implements Serializable {
@Nullable
private final Object target;
@Nullable
private transient BeanWrapper beanWrapper;
/**
* Creates a new instance of the {@link BeanPropertyBindingResult} class.
* @param target the target bean to bind onto
* @param objectName the name of the target object
* @param autoGrowNestedPaths whether to "auto-grow" a nested path that contains a null value
* @param autoGrowCollectionLimit the limit for array and collection auto-growing
*/
public BeanPropertyBindingResult(@Nullable Object target, String objectName,
boolean autoGrowNestedPaths, int autoGrowCollectionLimit) {
super(objectName);
this.target = target;
this.autoGrowNestedPaths = autoGrowNestedPaths;
this.autoGrowCollectionLimit = autoGrowCollectionLimit;
}
/**
* Returns the {@link BeanWrapper} that this instance uses.
* Creates a new one if none existed before.
* @see #createBeanWrapper()
*/
@Override
public final ConfigurablePropertyAccessor getPropertyAccessor() {
if (this.beanWrapper == null) {
// 1..
this.beanWrapper = createBeanWrapper();
this.beanWrapper.setExtractOldValueForEditor(true);
this.beanWrapper.setAutoGrowNestedPaths(this.autoGrowNestedPaths);
this.beanWrapper.setAutoGrowCollectionLimit(this.autoGrowCollectionLimit);
}
return this.beanWrapper;
}
/**
* Create a new {@link BeanWrapper} for the underlying target object.
* @see #getTarget()
*/
protected BeanWrapper createBeanWrapper() {
if (this.target == null) {
throw new IllegalStateException("Cannot access properties on null bean instance '" + getObjectName() + "'");
}
// 1... PropertyAccessorFactory
return PropertyAccessorFactory.forBeanPropertyAccess(this.target);
}
}
2. PropertyAccessorFactory
public final class PropertyAccessorFactory {
/**
* Obtain a BeanWrapper for the given target object,
* accessing properties in JavaBeans style.
* @param target the target object to wrap
* @return the property accessor
* @see BeanWrapperImpl
*/
public static BeanWrapper forBeanPropertyAccess(Object target) {
return new BeanWrapperImpl(target);
}
}
2. WebDataBinder
public class WebDataBinder extends DataBinder {
public WebDataBinder(@Nullable Object target, String objectName) {
super(target, objectName);
}
@Override
protected void doBind(MutablePropertyValues mpvs) {
checkFieldDefaults(mpvs);
checkFieldMarkers(mpvs);
// 1... DataBinder
super.doBind(mpvs);
}
protected void checkFieldDefaults(MutablePropertyValues mpvs) {
String fieldDefaultPrefix = getFieldDefaultPrefix();
if (fieldDefaultPrefix != null) {
PropertyValue[] pvArray = mpvs.getPropertyValues();
for (PropertyValue pv : pvArray) {
if (pv.getName().startsWith(fieldDefaultPrefix)) {
String field = pv.getName().substring(fieldDefaultPrefix.length());
if (getPropertyAccessor().isWritableProperty(field) && !mpvs.contains(field)) {
mpvs.add(field, pv.getValue());
}
mpvs.removePropertyValue(pv);
}
}
}
}
protected void checkFieldMarkers(MutablePropertyValues mpvs) {
String fieldMarkerPrefix = getFieldMarkerPrefix();
if (fieldMarkerPrefix != null) {
PropertyValue[] pvArray = mpvs.getPropertyValues();
for (PropertyValue pv : pvArray) {
if (pv.getName().startsWith(fieldMarkerPrefix)) {
String field = pv.getName().substring(fieldMarkerPrefix.length());
if (getPropertyAccessor().isWritableProperty(field) && !mpvs.contains(field)) {
Class<?> fieldType = getPropertyAccessor().getPropertyType(field);
mpvs.add(field, getEmptyValue(field, fieldType));
}
mpvs.removePropertyValue(pv);
}
}
}
}
}
3. ServletRequestDataBinder
public class ServletRequestDataBinder extends WebDataBinder {
public ServletRequestDataBinder(@Nullable Object target, String objectName) {
super(target, objectName);
}
public void bind(ServletRequest request) {
MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class);
if (multipartRequest != null) {
bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
}
addBindValues(mpvs, request);
// 1... WebDataBinder
doBind(mpvs);
}
}
1. ServletReqParamProptyValues
@SuppressWarnings("serial")
public class ServletRequestParameterPropertyValues extends MutablePropertyValues {
/** Default prefix separator. */
public static final String DEFAULT_PREFIX_SEPARATOR = "_";
/**
* Create new ServletRequestPropertyValues using no prefix
* (and hence, no prefix separator).
* @param request the HTTP request
*/
public ServletRequestParameterPropertyValues(ServletRequest request) {
this(request, null, null);
}
/**
* Create new ServletRequestPropertyValues supplying both prefix and
* prefix separator.
* @param request the HTTP request
* @param prefix the prefix for parameters (the full prefix will
* consist of this plus the separator)
* @param prefixSeparator separator delimiting prefix (e.g. "spring")
* and the rest of the parameter name ("param1", "param2")
*/
public ServletRequestParameterPropertyValues(
ServletRequest request, @Nullable String prefix, @Nullable String prefixSeparator) {
super(WebUtils.getParametersStartingWith(request, (prefix != null ? prefix + prefixSeparator : null)));
}
}
2. WebUtils
public abstract class WebUtils {
/**
* Return a map containing all parameters with the given prefix.
* Maps single values to String and multiple values to String array.
* <p>For example, with a prefix of "spring_", "spring_param1" and
* "spring_param2" result in a Map with "param1" and "param2" as keys.
* @param request the HTTP request in which to look for parameters
* @param prefix the beginning of parameter names
* (if this is null or the empty string, all parameters will match)
* @return map containing request parameters <b>without the prefix</b>,
* containing either a String or a String array as values
* @see javax.servlet.ServletRequest#getParameterNames
* @see javax.servlet.ServletRequest#getParameterValues
* @see javax.servlet.ServletRequest#getParameterMap
*/
public static Map<String, Object> getParametersStartingWith(ServletRequest request, @Nullable String prefix) {
Assert.notNull(request, "Request must not be null");
Enumeration<String> paramNames = request.getParameterNames();
Map<String, Object> params = new TreeMap<>();
if (prefix == null) {
prefix = "";
}
while (paramNames != null && paramNames.hasMoreElements()) {
String paramName = paramNames.nextElement();
if (prefix.isEmpty() || paramName.startsWith(prefix)) {
String unprefixed = paramName.substring(prefix.length());
String[] values = request.getParameterValues(paramName);
if (values == null || values.length == 0) {
// Do nothing, no values found at all.
}
else if (values.length > 1) {
params.put(unprefixed, values);
}
else {
params.put(unprefixed, values[0]);
}
}
}
return params;
}
}
2. getModelFactory()
@ModelAttribute
和@InitBinder
同理
/**
* MethodFilter that matches {@link ModelAttribute @ModelAttribute} methods.
*/
public static final MethodFilter MODEL_ATTRIBUTE_METHODS = method ->
(!AnnotatedElementUtils.hasAnnotation(method, RequestMapping.class) &&
AnnotatedElementUtils.hasAnnotation(method, ModelAttribute.class));
private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
// 获取sessionAttributesHandler
SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
// 获取处理器类的类型
Class<?> handlerType = handlerMethod.getBeanType();
// 获取处理器类中注释了@modelAttribute而且没有注释@RequestMapping的类型,第一个获取后添加到缓存,以后直接从缓存中获取
Set<Method> methods = this.modelAttributeCache.get(handlerType);
if (methods == null) {
// 1... 当前Controller中被(!@RequestMapping && @ModelAttribute)修饰方法
methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
this.modelAttributeCache.put(handlerType, methods);
}
// 注释了@ModelAttribute的方法
List<InvocableHandlerMethod> attrMethods = new ArrayList<>();
// 2. Global methods first
// 全局的@ModelAttribute方法
this.modelAttributeAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
Object bean = controllerAdviceBean.resolveBean();
for (Method method : methodSet) {
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
}
});
// 3. 合并@ModelAttribute
for (Method method : methods) {
Object bean = handlerMethod.getBean();
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
// 4... 第一个@ModelAttribute的方法,第二个WebDataBinderFactory,第三个是SessionAttributeHandler
return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
}
3. createInvocableHandlerMethod()
// 3.. 参数绑定、处理请求、返回值处理
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);
/**
* Create a {@link ServletInvocableHandlerMethod} from the given {@link HandlerMethod} definition.
*
* @param handlerMethod the {@link HandlerMethod} definition
* @return the corresponding {@link ServletInvocableHandlerMethod} (or custom subclass thereof)
* @since 4.2
*/
protected ServletInvocableHandlerMethod createInvocableHandlerMethod(HandlerMethod handlerMethod) {
return new ServletInvocableHandlerMethod(handlerMethod);
}
4. initModel()
@SessionAttributes
类@SessionAttribute
类、方法
// 全局数据绑定
@ModelAttribute(name = "md")
public Map<String, Object> modelAttribute() {
HashMap<String, Object> map = new HashMap<>();
map.put("age", 99);
map.put("gender", "男");
return map;
}
1. ModelFactory
@ModelAttribute
调用处理@ModelAttribute
会覆盖请求参数 kv,且 controller 形参必须被@ModelAttribute
修饰
/**
* ModelFactory是用来维护model的,具体包含两个功能,1、初始化Model,2、处理器执行后将Model中相应的参数更新到sessionAttribute中
* <p>
* Assist with initialization of the {@link Model} before controller method
* invocation and with updates to it after the invocation.
*
* <p>On initialization the model is populated with attributes temporarily stored
* in the session and through the invocation of {@code @ModelAttribute} methods.
*
* <p>On update model attributes are synchronized with the session and also
* {@link BindingResult} attributes are added if missing.
*/
public final class ModelFactory {
private final List<ModelMethod> modelMethods = new ArrayList<>();
private final SessionAttributesHandler sessionAttributesHandler;
/**
* Create a new instance with the given {@code @ModelAttribute} methods.
*
* @param handlerMethods the {@code @ModelAttribute} methods to invoke
* @param binderFactory for preparation of {@link BindingResult} attributes
* @param attributeHandler for access to session attributes
*/
public ModelFactory(@Nullable List<InvocableHandlerMethod> handlerMethods,
WebDataBinderFactory binderFactory, SessionAttributesHandler attributeHandler) {
if (handlerMethods != null) {
for (InvocableHandlerMethod handlerMethod : handlerMethods) {
this.modelMethods.add(new ModelMethod(handlerMethod));
}
}
this.dataBinderFactory = binderFactory;
this.sessionAttributesHandler = attributeHandler;
}
public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod)
throws Exception {
// 1. @SessionAttributes参数,合并到ModelAndViewContainer
Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
container.mergeAttributes(sessionAttributes);
// 2.. 执行@ModelAttribute方法并将结果设置到Model中
invokeModelAttributeMethods(request, container);
// 3.. 注释@ModelAttribute、@SessionAttributes的参数
for (String name : findSessionAttributeArguments(handlerMethod)) {
if (!container.containsAttribute(name)) {
Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
if (value == null) {
throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);
}
container.addAttribute(name, value);
}
}
}
/**
* Invoke model attribute methods to populate the model.
* Attributes are added only if not already present in the model.
*/
private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer container)
throws Exception {
while (!this.modelMethods.isEmpty()) {
// 1.. @ModelAttribute方法
InvocableHandlerMethod modelMethod = getNextModelMethod(container).getHandlerMethod();
ModelAttribute ann = modelMethod.getMethodAnnotation(ModelAttribute.class);
Assert.state(ann != null, "No ModelAttribute annotation");
// 参数名已经在ModelAndViewContainer中则跳过
if (container.containsAttribute(ann.name())) {
if (!ann.binding()) {
container.setBindingDisabled(ann.name());
}
continue;
}
// 2... InvocableHandlerMethod 调用@ModelAttribute方法
Object returnValue = modelMethod.invokeForRequest(request, container);
// 不是void
if (!modelMethod.isVoid()) {
// 3.. 使用getNameForReturnValue获取参数名
String returnValueName = getNameForReturnValue(returnValue, modelMethod.getReturnType());
if (!ann.binding()) {
container.setBindingDisabled(returnValueName);
}
// 4. 如果不存在container,添加进去
if (!container.containsAttribute(returnValueName)) {
container.addAttribute(returnValueName, returnValue);
}
}
}
}
private ModelMethod getNextModelMethod(ModelAndViewContainer container) {
for (ModelMethod modelMethod : this.modelMethods) {
if (modelMethod.checkDependencies(container)) {
this.modelMethods.remove(modelMethod);
// 1.
return modelMethod;
}
}
ModelMethod modelMethod = this.modelMethods.get(0);
this.modelMethods.remove(modelMethod);
return modelMethod;
}
public static String getNameForReturnValue(@Nullable Object returnValue, MethodParameter returnType) {
ModelAttribute ann = returnType.getMethodAnnotation(ModelAttribute.class);
// 1. @ModelAttribute设置了value,则直接将其作为参数名返回
if (ann != null && StringUtils.hasText(ann.value())) {
return ann.value();
} else {
MethoMethod methodd method = returnType.getMethod();
Assert.state(method != null, "No handler method");
Class<?> containingClass = returnType.getContainingClass();
Class<?> resolvedType = GenericTypeResolver.resolveReturnType(method, containingClass);
// 2... 返回值类型、返回值获取参数名
return Conventions.getVariableNameForReturnType(method, resolvedType, returnValue);
}
}
/**
* 获取同时有@ModelAttribute又有@SessionAttributes的参数
* <p>
* Find {@code @ModelAttribute} arguments also listed as {@code @SessionAttributes}.
*/
private List<String> findSessionAttributeArguments(HandlerMethod handlerMethod) {
List<String> result = new ArrayList<>();
for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
// 1. 参数名
String name = getNameForParameter(parameter);
// 参数类型
Class<?> paramType = parameter.getParameterType();
// 根据参数名、参数类型检查参数是否在@SessionAttributes注解中
if (this.sessionAttributesHandler.isHandlerSessionAttribute(name, paramType)) {
result.add(name);
}
}
}
return result;
}
public static String getNameForParameter(MethodParameter parameter) {
ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
String name = (ann != null ? ann.value() : null);
return (StringUtils.hasText(name) ? name : Conventions.getVariableNameForParameter(parameter));
}
// ----------------------------------------------------------------------------------------
public void updateModel(NativeWebRequest request, ModelAndViewContainer container) throws Exception {
// 1... ModelAndViewContainer 获取defaultModel
ModelMap defaultModel = container.getDefaultModel();
// 如果处理器里调用了setComplete则将SessionAttribute清空,否则将defaultModel中的参数设置到SessionAttributes中
// 2... ModelAndViewContainer
if (container.getSessionStatus().isComplete()) { // skip
this.sessionAttributesHandler.cleanupAttributes(request);
}
// 将mavContainer的defaultModel中的参数设置到SessionAttributes
else {
// 3... SessionAttributesHandler
this.sessionAttributesHandler.storeAttributes(request, defaultModel);
}
// 判断请求是否已经处理完或者是redirect类型的返回值,其实就是判断是否需要进行页面的渲染操作
if (!container.isRequestHandled() && container.getModel() == defaultModel) {
// 4..
updateBindingResult(request, defaultModel);
}
}
/**
* 如果处理器绑定参数时注释了@Valid和@Validated注解,那么会将校验的结果设置到BindingResult类型的参数中
* 如果没有添加校验的注释,为了渲染方便,ModelFactory会给Model设置一个跟参数相对应的BindingResult
* <p>
* Add {@link BindingResult} attributes to the model for attributes that require it.
*/
private void updateBindingResult(NativeWebRequest request, ModelMap model) throws Exception {
List<String> keyNames = new ArrayList<>(model.keySet());
for (String name : keyNames) {
Object value = model.get(name);
// 遍历每一个Model中保存的参数,判断是否需要添加BindingResult,如果需要则使用WebDataBinder获取BindingResult并添加到Model
// 5.. 在添加前检查Model中是否已经存在,如果已经存在就不添加了
if (value != null && isBindingCandidate(name, value)) { // 一般进不来
String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + name;
// 如果model中不存在bindingResult
if (!model.containsAttribute(bindingResultKey)) {
// 通过dataBinderFactory创建webDataBinder
WebDataBinder dataBinder = this.dataBinderFactory.createBinder(request, value, name);
// 添加到model
model.put(bindingResultKey, dataBinder.getBindingResult());
}
}
}
}
/**
* 判断是都需要添加BindingResult对象
* <p>
* Whether the given attribute requires a {@link BindingResult} in the model.
*/
private boolean isBindingCandidate(String attributeName, Object value) {
// 判断是不是其他参数绑定结果的BindingResult,如果是,则不需要添加
if (attributeName.startsWith(BindingResult.MODEL_KEY_PREFIX)) {
return false;
}
// 判断是不是SessionAttribute管理的属性,如果是返回true
if (this.sessionAttributesHandler.isHandlerSessionAttribute(attributeName, value.getClass())) {
return true;
}
// 6... BeanUtils 判断如果不是空值、数组、Collection、Map和简单类型,则返回true添加到BindingResult
return (!value.getClass().isArray() && !(value instanceof Collection) &&
!(value instanceof Map) && !BeanUtils.isSimpleValueType(value.getClass()));
}
}
2. Conventions
public final class Conventions {
/**
* Suffix added to names when using arrays.
*/
private static final String PLURAL_SUFFIX = "List";
public static String getVariableNameForReturnType(Method method, Class<?> resolvedType, @Nullable Object value) {
Assert.notNull(method, "Method must not be null");
// Object类型,返回实际类型
if (Object.class == resolvedType) {
if (value == null) {
throw new IllegalArgumentException(
"Cannot generate variable name for an Object return type with null value");
}
return getVariableName(value);
}
Class<?> valueClass;
boolean pluralize = false;
String reactiveSuffix = "";
// 数组、Collection会使用内部实际包装的类型,并在最后添加:List
if (resolvedType.isArray()) {
valueClass = resolvedType.getComponentType();
pluralize = true;
} else if (Collection.class.isAssignableFrom(resolvedType)) {
valueClass = ResolvableType.forMethodReturnType(method).asCollection().resolveGeneric();
if (valueClass == null) {
if (!(value instanceof Collection)) {
throw new IllegalArgumentException("Cannot generate variable name " +
"for non-typed Collection return type and a non-Collection value");
}
Collection<?> collection = (Collection<?>) value;
if (collection.isEmpty()) {
throw new IllegalArgumentException("Cannot generate variable name " +
"for non-typed Collection return type and an empty Collection value");
}
Object valueToCheck = peekAhead(collection);
valueClass = getClassForValue(valueToCheck);
}
pluralize = true;
} else {
valueClass = resolvedType;
ReactiveAdapter adapter = ReactiveAdapterRegistry.getSharedInstance().getAdapter(valueClass);
if (adapter != null && !adapter.getDescriptor().isNoValue()) {
reactiveSuffix = ClassUtils.getShortName(valueClass);
valueClass = ResolvableType.forMethodReturnType(method).getGeneric().toClass();
}
}
String name = ClassUtils.getShortNameAsProperty(valueClass);
return (pluralize ? pluralize(name) : name + reactiveSuffix);
}
}
5. invokeAndHandle()
1. ServletInvocableHandlerMethod
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
/**
* 返回结果处理器组合对象
*/
@Nullable
private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
/**
* Create an instance from a {@code HandlerMethod}.
*/
public ServletInvocableHandlerMethod(HandlerMethod handlerMethod) {
super(handlerMethod);
}
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 1... InvocableHandlerMethod
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// 2.. 处理@ResponseStatus注解
setResponseStatus(webRequest);
// 处理返回值,判断返回值是否为空
if (returnValue == null) {
// request的NotModified为true,有@ResponseStatus,RequestHandled为true,三个条件有一个成立,则设置请求处理完成并返回
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
}
// 返回值不为null,@ResponseStatus存在reason,这是请求处理完成并返回
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
// 前面都不成立,则设置RequestHandled为false即请求完成??????????
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
// 3... HandlerMethodReturnValueHandlerComposite 使用returnValueHandlers处理返回值
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
} catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
}
1. HandlerMethod
package org.springframework.web.method;
public class HandlerMethod {
// 处理请求的方法的参数
private final MethodParameter[] parameters;
/**
* Return the method parameters for this handler method.
*/
public MethodParameter[] getMethodParameters() {
return this.parameters;
}
@Nullable
protected static Object findProvidedArgument(MethodParameter parameter, @Nullable Object... providedArgs) {
if (!ObjectUtils.isEmpty(providedArgs)) {
for (Object providedArg : providedArgs) {
if (parameter.getParameterType().isInstance(providedArg)) {
return providedArg;
}
}
}
return null;
}
private void evaluateResponseStatus() {
ResponseStatus annotation = getMethodAnnotation(ResponseStatus.class);
if (annotation == null) {
// 1.
annotation = AnnotatedElementUtils.findMergedAnnotation(getBeanType(), ResponseStatus.class);
}
if (annotation != null) {
// 2.
this.responseStatus = annotation.code();
this.responseStatusReason = annotation.reason();
}
}
/**
* Return the specified response status, if any.
* @since 4.3.8
* @see ResponseStatus#code()
*/
@Nullable
protected HttpStatus getResponseStatus() {
return this.responseStatus;
}
/**
* Return the associated response status reason, if any.
* @since 4.3.8
* @see ResponseStatus#reason()
*/
@Nullable
protected String getResponseStatusReason() {
return this.responseStatusReason;
}
protected Method getBridgedMethod() {
return this.bridgedMethod;
}
/**
* Return the bean for this handler method.
*/
public Object getBean() {
return this.bean;
}
}
2. InvocableHandlerMethod
public class InvocableHandlerMethod extends HandlerMethod {
@Nullable
private HttpStatus responseStatus;
// 用于解析参数
private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();
// WebDataBinderFactory类型,可以创建WebDataBinder,用于参数解析器ArgumentResolver中
@Nullable
private WebDataBinderFactory dataBinderFactory;
// 获取参数名,用于MethodParameter中
private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
/**
* Create an instance from a {@code HandlerMethod}.
*/
public InvocableHandlerMethod(HandlerMethod handlerMethod) {
super(handlerMethod);
}
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 1.. 准备方法所需要的参数
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
// 2.. 具体调用method
return doInvoke(args);
}
/**
* Get the method argument values for the current request, checking the provided
* argument values and falling back to the configured argument resolvers.
* <p>The resulting array will be passed into {@link #doInvoke}.
*
* @since 5.1.2
*/
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 1... HandlerMethod 获取方法的参数
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
// 用于保存解析出参数的值
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
// 给parameter设置参数名解析器
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
// ... HandlerMethod。参数已经在providedArgs中提供,则直接设置到parameter
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
// 2... HandlerMethodArgumentResolverComposite
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
// 3... HandlerMethodArgumentResolverComposite
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
} catch (Exception ex) {
// Leave stack trace for later, exception may actually be resolved and handled...
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw ex;
}
}
return args;
}
/**
* Invoke the handler method with the given argument values.
*/
@Nullable
protected Object doInvoke(Object... args) throws Exception {
ReflectionUtils.makeAccessible(getBridgedMethod());
try {
// 1... HandlerMethod 执行
return getBridgedMethod().invoke(getBean(), args);
} catch (IllegalArgumentException ex) {
assertTargetBean(getBridgedMethod(), getBean(), args);
String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
throw new IllegalStateException(formatInvokeError(text, args), ex);
} catch (InvocationTargetException ex) {
// Unwrap for HandlerExceptionResolvers ...
Throwable targetException = ex.getTargetException();
if (targetException instanceof RuntimeException) {
throw (RuntimeException) targetException;
} else if (targetException instanceof Error) {
throw (Error) targetException;
} else if (targetException instanceof Exception) {
throw (Exception) targetException;
} else {
throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
}
}
}
// ------------------------------------------------------------------------------------------
/**
* Set the response status according to the {@link ResponseStatus} annotation.
*/
private void setResponseStatus(ServletWebRequest webRequest) throws IOException {
// 获得状态码,和 @ResponseStatus 注解相关
HttpStatus status = getResponseStatus();
if (status == null) {
return;
}
// 设置响应的状态码
HttpServletResponse response = webRequest.getResponse();
if (response != null) {
String reason = getResponseStatusReason();
// 有 reason ,则设置 status + reason
if (StringUtils.hasText(reason)) {
response.sendError(status.value(), reason);
}
// 无 reason ,则仅设置 status
else {
response.setStatus(status.value());
}
}
// To be picked up by RedirectView
// 为了 RedirectView ,所以进行设置
webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, status);
}
/**
* Return the specified response status, if any.
* @since 4.3.8
* @see ResponseStatus#code()
*/
@Nullable
protected HttpStatus getResponseStatus() {
return this.responseStatus;
}
}
2. HandlerMtdArgResolverComposite
- 3. RequestMappingHandlerAdapter
HandlerMethodArgumentResolver
接口的实现类,对实参进行解析
public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
private final List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>();
private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache = new ConcurrentHashMap<>(256);
/**
* Add the given {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}.
*/
public HandlerMethodArgumentResolverComposite addResolvers(
@Nullable List<? extends HandlerMethodArgumentResolver> resolvers) {
if (resolvers != null) {
// 1.
this.argumentResolvers.addAll(resolvers);
}
return this;
}
/**
* Whether the given {@linkplain MethodParameter method parameter} is
* supported by any registered {@link HandlerMethodArgumentResolver}.
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
// 1..
return getArgumentResolver(parameter) != null;
}
/**
* Find a registered {@link HandlerMethodArgumentResolver} that supports
* the given method parameter.
*/
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result == null) {
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
// 1... RequestParamMethodArgumentResolver, ServletModelAttributeMethodProcessor各有两个。26个值处理器进行判断
// RequestParamMethodArgumentResolver, ModelAttributeMethodProcessor
if (resolver.supportsParameter(parameter)) {
result = resolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// 1.. 缓存中直接获取
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
if (resolver == null) {
throw new IllegalArgumentException("Unsupported parameter type [" +
parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
}
// 2. AbstractNamedValueMethodArgumentResolver, ModelAttributeMethodProcessor
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
}
1. RequestParamMtdArgumentResolver
http://localhost:8080/spring_mymvc/param?id=10&name=ooxx&date=2020-01-01%2011:11:11
public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver
implements UriComponentsContributor {
private final boolean useDefaultResolution;
public RequestParamMethodArgumentResolver(@Nullable ConfigurableBeanFactory beanFactory, boolean useDefaultResolution) {
super(beanFactory);
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.name()));
}
else {
return true;
}
}
else {
if (parameter.hasParameterAnnotation(RequestPart.class)) {
return false;
}
parameter = parameter.nestedIfOptional();
if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {
return true;
}
// 1... true 无注解基本类型
else if (this.useDefaultResolution) {
return BeanUtils.isSimpleProperty(parameter.getNestedParameterType());
}
else {
return false;
}
}
}
// ------------------------------------------------------------------------------------------------
@Override
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
// 1.
RequestParam ann = parameter.getParameterAnnotation(RequestParam.class);
return (ann != null ? new RequestParamNamedValueInfo(ann) : new RequestParamNamedValueInfo());
}
/**
* Create a new NamedValueInfo based on the given NamedValueInfo with sanitized values.
*/
private NamedValueInfo updateNamedValueInfo(MethodParameter parameter, NamedValueInfo info) {
String name = info.name;
if (info.name.isEmpty()) {
// 1. @RequestParam 无name属性。形参名作为key
name = parameter.getParameterName();
if (name == null) {
throw new IllegalArgumentException(
"Name for argument type [" + parameter.getNestedParameterType().getName() +
"] not available, and parameter name information not found in class file either.");
}
}
String defaultValue = (ValueConstants.DEFAULT_NONE.equals(info.defaultValue) ? null : info.defaultValue);
return new NamedValueInfo(name, info.required, defaultValue);
}
@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.UNRESOLVABLE) {
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) {
// 99. ^-^
String[] paramValues = request.getParameterValues(name);
if (paramValues != null) {
arg = (paramValues.length == 1 ? paramValues[0] : paramValues);
}
}
return arg;
}
@Override
protected void handleMissingValue(String name, MethodParameter parameter, NativeWebRequest request)
throws Exception {
HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {
if (servletRequest == null || !MultipartResolutionDelegate.isMultipartRequest(servletRequest)) {
throw new MultipartException("Current request is not a multipart request");
}
else {
throw new MissingServletRequestPartException(name);
}
}
else {
throw new MissingServletRequestParameterException(name,
parameter.getNestedParameterType().getSimpleName());
}
}
// ------------------------------------------------------------------------
// AbstractNamedValueMethodArgumentResolver$NamedValueInfo
private static class RequestParamNamedValueInfo extends NamedValueInfo {
public RequestParamNamedValueInfo() {
super("", false, ValueConstants.DEFAULT_NONE);
}
public RequestParamNamedValueInfo(RequestParam annotation) {
super(annotation.name(), annotation.required(), annotation.defaultValue());
}
}
}
1. AbsNamedValueMtdArgResolver
public abstract class AbstractNamedValueMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Override
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// 1.. 单个参数name封装
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
MethodParameter nestedParameter = parameter.nestedIfOptional();
// 2.. 参数name为表达式,进行解析
Object resolvedName = resolveStringValue(namedValueInfo.name);
if (resolvedName == null) {
throw new IllegalArgumentException(
"Specified name must not resolve to null: [" + namedValueInfo.name + "]");
}
// 3... RequestParamMethodArgumentResolver 具体解析参数。模板方法,留给子类实现
Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
// 如果没有解析到参数
if (arg == null) {
// 判断namedValueInfo中是否包含默认值,如果包含,则设置默认值
if (namedValueInfo.defaultValue != null) {
arg = resolveStringValue(namedValueInfo.defaultValue);
}
// 如果结果为null也没有默认值而且namedValueInfo中设置required为true则进行后续的处理
else if (namedValueInfo.required && !nestedParameter.isOptional()) {
handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
}
arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
}
else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
arg = resolveStringValue(namedValueInfo.defaultValue);
}
// 如果binderFactory不为空,则用它创建binder并转换解析出的参数
if (binderFactory != null) {
// 4... DefaultDataBinderFactory => ExtendedServletRequestDataBinder
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
try {
// 5... DataBinder 入参转化为形参类型
arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
}
catch (ConversionNotSupportedException ex) {
throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
catch (TypeMismatchException ex) {
throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
}
// 无操作。对解析出的参数进行后置处理
handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
return arg;
}
/**
* Obtain the named value for the given method parameter.
*/
private NamedValueInfo getNamedValueInfo(MethodParameter parameter) {
NamedValueInfo namedValueInfo = this.namedValueInfoCache.get(parameter);
if (namedValueInfo == null) {
// 1... RequestParamMethodArgumentResolver => RequestParamNamedValueInfo
namedValueInfo = createNamedValueInfo(parameter);
// 2... RequestParamMethodArgumentResolver => RequestParamNamedValueInfo
namedValueInfo = updateNamedValueInfo(parameter, namedValueInfo);
this.namedValueInfoCache.put(parameter, namedValueInfo);
}
return namedValueInfo;
}
/**
* Resolve the given annotation-specified value,
* potentially containing placeholders and expressions.
*/
@Nullable
private Object resolveStringValue(String value) {
if (this.configurableBeanFactory == null) {
return value;
}
// 1.
String placeholdersResolved = this.configurableBeanFactory.resolveEmbeddedValue(value);
BeanExpressionResolver exprResolver = this.configurableBeanFactory.getBeanExpressionResolver();
if (exprResolver == null || this.expressionContext == null) {
return value;
}
// 2.
return exprResolver.evaluate(placeholdersResolved, this.expressionContext);
}
/**
* A {@code null} results in a {@code false} value for {@code boolean}s or an exception for other primitives.
*/
@Nullable
private Object handleNullValue(String name, @Nullable Object value, Class<?> paramType) {
if (value == null) {
if (Boolean.TYPE.equals(paramType)) {
return Boolean.FALSE;
}
else if (paramType.isPrimitive()) {
throw new IllegalStateException("Optional " + paramType.getSimpleName() + " parameter '" + name +
"' is present but cannot be translated into a null value due to being declared as a " +
"primitive type. Consider declaring it as object wrapper for the corresponding primitive type.");
}
}
return value;
}
/**
* Represents the information about a named value, including name, whether it's required and a default value.
*/
protected static class NamedValueInfo {
// 参数名
private final String name;
// 是否必须存在
private final boolean required;
// 默认值
@Nullable
private final String defaultValue;
public NamedValueInfo(String name, boolean required, @Nullable String defaultValue) {
this.name = name;
this.required = required;
this.defaultValue = defaultValue;
}
}
}
2. BeanUtils
public abstract class BeanUtils {
static {
Map<Class<?>, Object> values = new HashMap<>();
values.put(boolean.class, false);
values.put(byte.class, (byte) 0);
values.put(short.class, (short) 0);
values.put(int.class, 0);
values.put(long.class, (long) 0);
DEFAULT_TYPE_VALUES = Collections.unmodifiableMap(values);
}
public static boolean isSimpleProperty(Class<?> type) {
Assert.notNull(type, "'type' must not be null");
// 如果type是"简单"值类型 || (type是数组 & type的元素类型是否"简单"值类型) 就为ture;否则为false
return isSimpleValueType(type) || (type.isArray() && isSimpleValueType(type.getComponentType()));
}
public static boolean isSimpleValueType(Class<?> type) {
return (Void.class != type && void.class != type &&
(ClassUtils.isPrimitiveOrWrapper(type) ||
Enum.class.isAssignableFrom(type) ||
CharSequence.class.isAssignableFrom(type) ||
Number.class.isAssignableFrom(type) ||
Date.class.isAssignableFrom(type) ||
Temporal.class.isAssignableFrom(type) ||
URI.class == type ||
URL.class == type ||
Locale.class == type ||
Class.class == type));
}
}
2. ServletModelAttributeMtdProcessor
public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodProcessor {
@Override
protected final Object createAttribute(String attributeName, MethodParameter parameter,
WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception {
// 1..
String value = getRequestValueForAttribute(attributeName, request);
if (value != null) { // skip
Object attribute = createAttributeFromRequestValue(
value, attributeName, parameter, binderFactory, request);
if (attribute != null) {
return attribute;
}
}
// 2. 方法参数对象,无参构造创建
return super.createAttribute(attributeName, parameter, binderFactory, request);
}
@Nullable
protected String getRequestValueForAttribute(String attributeName, NativeWebRequest request) {
Map<String, String> variables = getUriTemplateVariables(request); // skip (size = 0)
String variableValue = variables.get(attributeName);
if (StringUtils.hasText(variableValue)) {
return variableValue;
}
// 1. 对象驼峰名,获取不到
String parameterValue = request.getParameter(attributeName);
if (StringUtils.hasText(parameterValue)) {
return parameterValue;
}
return null;
}
// --------------------------------------------------------------------------------
/**
* This implementation downcasts {@link WebDataBinder} to
* {@link ServletRequestDataBinder} before binding.
* @see ServletRequestDataBinderFactory
*/
@Override
protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {
ServletRequest servletRequest = request.getNativeRequest(ServletRequest.class);
Assert.state(servletRequest != null, "No ServletRequest");
ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder;
// 1... ServletRequestDataBinder
servletBinder.bind(servletRequest);
}
}
1. ModelAttributeMethodProcessor
attribute = createAttribute(name, parameter, binderFactory, webRequest);
=> 无参构造创建实例ServletRequestDataBinder
=>BeanWrapperImpl
进行整体属性赋值- ModelFactory
public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
/**
* Returns {@code true} if the parameter is annotated with
* {@link ModelAttribute} or, if in default resolution mode, for any
* method parameter that is not a simple type.
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
// @ModelAttribute 修饰 || 引用类型
return (parameter.hasParameterAnnotation(ModelAttribute.class) ||
(this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType())));
}
/**
* Resolve the argument from the model or if not found instantiate it with
* its default if it is available. The model attribute is then populated
* with request values via data binding and optionally validated
* if {@code @java.validation.Valid} is present on the argument.
* @throws BindException if data binding and validation result in an error
* and the next method parameter is not of type {@link Errors}
* @throws Exception if WebDataBinder initialization fails
*/
@Override
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");
Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");
// 1... ModelFactory => @ModelAttribute
String name = ModelFactory.getNameForParameter(parameter);
// 2.
ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
if (ann != null) {
mavContainer.setBinding(name, ann.binding());
}
Object attribute = null;
BindingResult bindingResult = null;
// Model中存在
if (mavContainer.containsAttribute(name)) {
attribute = mavContainer.getModel().get(name);
}
else {
// 3... Create attribute instance **无参构造实例化形参对象**
// ServletModelAttributeMethodProcessor 形参名获取入参
try {
attribute = createAttribute(name, parameter, binderFactory, webRequest);
}
catch (BindException ex) {
if (isBindExceptionRequired(parameter)) {
// No BindingResult parameter -> fail with BindException
throw ex;
}
// Otherwise, expose null/empty value and associated BindingResult
if (parameter.getParameterType() == Optional.class) {
attribute = Optional.empty();
}
bindingResult = ex.getBindingResult();
}
}
if (bindingResult == null) {
// Bean property binding and validation;
// skipped in case of binding failure on construction.
// 4... DefaultDataBinderFactory
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
if (binder.getTarget() != null) {
if (!mavContainer.isBindingDisabled(name)) {
// 5... ** ServletModelAttributeMethodProcessor ** 实例化对象属性填充
bindRequestParameters(binder, webRequest);
}
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new BindException(binder.getBindingResult());
}
}
// Value type adaptation, also covering java.util.Optional
if (!parameter.getParameterType().isInstance(attribute)) { // skip
attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
}
bindingResult = binder.getBindingResult();
}
// Add resolved attribute and BindingResult at the end of the model
Map<String, Object> bindingResultModel = bindingResult.getModel();
mavContainer.removeAttributes(bindingResultModel);
mavContainer.addAllAttributes(bindingResultModel);
return attribute;
}
protected Object createAttribute(String attributeName, MethodParameter parameter,
WebDataBinderFactory binderFactory, NativeWebRequest webRequest) throws Exception {
MethodParameter nestedParameter = parameter.nestedIfOptional();
Class<?> clazz = nestedParameter.getNestedParameterType();
Constructor<?> ctor = BeanUtils.findPrimaryConstructor(clazz);
if (ctor == null) {
Constructor<?>[] ctors = clazz.getConstructors();
if (ctors.length == 1) {
ctor = ctors[0];
}
else {
try {
// 1. 无参构造
ctor = clazz.getDeclaredConstructor();
}
catch (NoSuchMethodException ex) {
throw new IllegalStateException("No primary or default constructor found for " + clazz, ex);
}
}
}
// 2..
Object attribute = constructAttribute(ctor, attributeName, parameter, binderFactory, webRequest);
if (parameter != nestedParameter) {
attribute = Optional.of(attribute);
}
return attribute;
}
@SuppressWarnings("deprecation")
protected Object constructAttribute(Constructor<?> ctor, String attributeName, MethodParameter parameter,
WebDataBinderFactory binderFactory, NativeWebRequest webRequest) throws Exception {
// 1.. => null @Deprecated
Object constructed = constructAttribute(ctor, attributeName, binderFactory, webRequest);
if (constructed != null) {
return constructed;
}
if (ctor.getParameterCount() == 0) {
// 2. A single default constructor -> clearly a standard JavaBeans arrangement.
return BeanUtils.instantiateClass(ctor);
}
// A single data class constructor -> resolve constructor arguments from request parameters.
ConstructorProperties cp = ctor.getAnnotation(ConstructorProperties.class);
String[] paramNames = (cp != null ? cp.value() : parameterNameDiscoverer.getParameterNames(ctor));
Assert.state(paramNames != null, () -> "Cannot resolve parameter names for constructor " + ctor);
Class<?>[] paramTypes = ctor.getParameterTypes();
Assert.state(paramNames.length == paramTypes.length,
() -> "Invalid number of parameter names: " + paramNames.length + " for constructor " + ctor);
Object[] args = new Object[paramTypes.length];
WebDataBinder binder = binderFactory.createBinder(webRequest, null, attributeName);
String fieldDefaultPrefix = binder.getFieldDefaultPrefix();
String fieldMarkerPrefix = binder.getFieldMarkerPrefix();
boolean bindingFailure = false;
Set<String> failedParams = new HashSet<>(4);
for (int i = 0; i < paramNames.length; i++) {
String paramName = paramNames[i];
Class<?> paramType = paramTypes[i];
Object value = webRequest.getParameterValues(paramName);
if (value == null) {
if (fieldDefaultPrefix != null) {
value = webRequest.getParameter(fieldDefaultPrefix + paramName);
}
if (value == null && fieldMarkerPrefix != null) {
if (webRequest.getParameter(fieldMarkerPrefix + paramName) != null) {
value = binder.getEmptyValue(paramType);
}
}
}
try {
MethodParameter methodParam = new FieldAwareConstructorParameter(ctor, i, paramName);
if (value == null && methodParam.isOptional()) {
args[i] = (methodParam.getParameterType() == Optional.class ? Optional.empty() : null);
}
else {
args[i] = binder.convertIfNecessary(value, paramType, methodParam);
}
}
catch (TypeMismatchException ex) {
ex.initPropertyName(paramName);
args[i] = value;
failedParams.add(paramName);
binder.getBindingResult().recordFieldValue(paramName, paramType, value);
binder.getBindingErrorProcessor().processPropertyAccessException(ex, binder.getBindingResult());
bindingFailure = true;
}
}
if (bindingFailure) {
BindingResult result = binder.getBindingResult();
for (int i = 0; i < paramNames.length; i++) {
String paramName = paramNames[i];
if (!failedParams.contains(paramName)) {
Object value = args[i];
result.recordFieldValue(paramName, paramTypes[i], value);
validateValueIfApplicable(binder, parameter, ctor.getDeclaringClass(), paramName, value);
}
}
throw new BindException(result);
}
return BeanUtils.instantiateClass(ctor, args);
}
@Deprecated
@Nullable
protected Object constructAttribute(Constructor<?> ctor, String attributeName,
WebDataBinderFactory binderFactory, NativeWebRequest webRequest) throws Exception {
// 1.
return null;
}
}
3. ReqRespBodyMethodProcessor
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
@Override
public boolean supportsParameter(MethodParameter parameter) {
// 1. 该参数是否有 @RequestBody 注解
return parameter.hasParameterAnnotation(RequestBody.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
parameter = parameter.nestedIfOptional();
// 1.. 从请求体中解析出方法入参对象
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
String name = Conventions.getVariableNameForParameter(parameter);
// 数据绑定相关
if (binderFactory != null) {
// 2... DefaultDataBinderFactory
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
if (arg != null) {
// 3... AbstractMessageConverterMethodArgumentResolver
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());
}
}
// 返回方法入参对象,如果有必要,则通过 Optional 获取对应的方法入参
return adaptArgumentIfNecessary(arg, parameter);
}
@Override
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
Type paramType) throws IOException, HttpMediaTypeNotSupportedException,
HttpMessageNotReadableException {
// 创建 ServletServerHttpRequest 请求对象
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
Assert.state(servletRequest != null, "No HttpServletRequest");
ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
// 1... AbstractMessageConverterMethodArgumentResolver 读取请求体中的消息并转换成入参对象
Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
// 校验方法入参对象
if (arg == null && checkRequired(parameter)) {
throw new HttpMessageNotReadableException("Required request body is missing: " +
parameter.getExecutable().toGenericString(), inputMessage);
}
return arg;
}
}
1. AbsMsgCvterMtdArgResolver
public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver {
private static final Set<HttpMethod> SUPPORTED_METHODS =
EnumSet.of(HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH);
/**
* Constructor with converters and {@code Request~} and {@code ResponseBodyAdvice}.
* @since 4.2
*/
public AbstractMessageConverterMethodArgumentResolver(List<HttpMessageConverter<?>> converters,
@Nullable List<Object> requestResponseBodyAdvice) {
Assert.notEmpty(converters, "'messageConverters' must not be empty");
this.messageConverters = converters;
this.allSupportedMediaTypes = getAllSupportedMediaTypes(converters);
this.advice = new RequestResponseBodyAdviceChain(requestResponseBodyAdvice);
}
@SuppressWarnings("unchecked")
@Nullable
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
// 获取使用的 MediaType 对象
MediaType contentType;
boolean noContentType = false;
try {
// 1. 从请求头中获取 "Content-Type" => application/json
contentType = inputMessage.getHeaders().getContentType();
}
catch (InvalidMediaTypeException ex) {
throw new HttpMediaTypeNotSupportedException(ex.getMessage());
}
if (contentType == null) {
noContentType = true;
// 为空则默认为 application/octet-stream
contentType = MediaType.APPLICATION_OCTET_STREAM;
}
// 2. 获取方法参数的 containing class、目标类型,用于 HttpMessageConverter 解析
Class<?> contextClass = parameter.getContainingClass();
Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
if (targetClass == null) {
// 如果为空,则从方法参数中解析出来
ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
targetClass = (Class<T>) resolvableType.resolve();
}
// 3. 获取 HTTP 方法 => POST
HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
Object body = NO_VALUE;
// 开始从请求中解析方法入参
EmptyBodyCheckingHttpInputMessage message;
try {
// 将请求消息对象封装成 EmptyBodyCheckingHttpInputMessage,用于校验是否有请求体,没有的话设置为 `null`
message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
// 遍历 HttpMessageConverter
for (HttpMessageConverter<?> converter : this.messageConverters) {
Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
GenericHttpMessageConverter<?> genericConverter =
(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
// 4... AbstractJackson2HttpMessageConverter 如果该 HttpMessageConverter 能够读取当前请求体解析出方法入参
if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
(targetClass != null && converter.canRead(targetClass, contentType))) {
// 如果请求体不为空
if (message.hasBody()) {
HttpInputMessage msgToUse =
// 5... RequestResponseBodyAdviceChain
getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
// 6... AbstractJackson2HttpMessageConverter 通过该 HttpMessageConverter 从请求体中解析出方法入参对象
body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
// 调用 RequestResponseBodyAdvice 的 afterBodyRead 方法,存在 RequestBodyAdvice 则对方法入参进行修改
body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
}
// 如果请求体为空,则无需解析请求体
else {
// 调用 RequestResponseBodyAdvice 的 afterBodyRead 方法,存在 RequestBodyAdvice 则对方法入参进行修改
body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
}
break;
}
}
}
catch (IOException ex) {
throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
}
// 校验解析出来的方法入参对象是否为空
if (body == NO_VALUE) {
if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||
(noContentType && !message.hasBody())) {
return null;
}
throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
}
// 打印日志
MediaType selectedContentType = contentType;
Object theBody = body;
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(theBody, !traceOn);
return "Read \"" + selectedContentType + "\" to [" + formatted + "]";
});
// 返回方法入参对象
return body;
}
RequestResponseBodyAdviceChain getAdvice() {
return this.advice;
}
protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
Annotation[] annotations = parameter.getParameterAnnotations();
for (Annotation ann : annotations) {
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
binder.validate(validationHints);
break;
}
}
}
}
3. setResponseStatus()
@ResponseStatus
解析- InvocableHandlerMethod
4. HandlerMtdRtnValHandlerComposite
HandlerMethodReturnValueHandler
接口的实现类,对返回值进行处理ViewMethodReturnValueHandler
=>View
ModelMethodProcessor
=>Model
ModelAndViewMethodReturnValueHandler
=>ModelAndView
RequestResponseBodyMethodProcessor
=>@ResponseBody
public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler {
private final List<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<>();
/**
* Add the given {@link HandlerMethodReturnValueHandler HandlerMethodReturnValueHandlers}.
*/
public HandlerMethodReturnValueHandlerComposite addHandlers(
@Nullable List<? extends HandlerMethodReturnValueHandler> handlers) {
if (handlers != null) {
this.returnValueHandlers.addAll(handlers);
}
return this;
}
/**
* Iterate over registered {@link HandlerMethodReturnValueHandler HandlerMethodReturnValueHandlers} and invoke the one
* that supports it.
*
* @throws IllegalStateException if no suitable {@link HandlerMethodReturnValueHandler} is found.
*/
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// 1..
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
// 2... ViewNameMethodReturnValueHandler, RequestResponseBodyMethodProcessor
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
@Nullable
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
boolean isAsyncValue = isAsyncReturnValue(value, returnType);
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
continue;
}
// 1... ViewNameMethodReturnValueHandler, RequestResponseBodyMethodProcessor
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
}
1. ViewNameMtdReturnValueHandler
public class ViewNameMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
/**
* 重定向的表达式的数组
*/
@Nullable
private String[] redirectPatterns;
@Override
public boolean supportsReturnType(MethodParameter returnType) {
Class<?> paramType = returnType.getParameterType();
// 1. String是字符序列(void 或 String)
return (void.class == paramType || CharSequence.class.isAssignableFrom(paramType));
}
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// 如果是 String 类型
if (returnValue instanceof CharSequence) {
// 设置视图名到 mavContainer 中
String viewName = returnValue.toString();
mavContainer.setViewName(viewName);
// 1.. 如果是重定向,则标记到 mavContainer 中
if (isRedirectViewName(viewName)) {
mavContainer.setRedirectModelScenario(true);
}
}
// 如果是非 String 类型,而且非 void ,则抛出 UnsupportedOperationException 异常
else if (returnValue != null) {
// should not happen
throw new UnsupportedOperationException("Unexpected return type: " +
returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
}
}
protected boolean isRedirectViewName(String viewName) {
// 1. 符合 redirectPatterns 表达式,或者以 redirect: 开头
return (PatternMatchUtils.simpleMatch(this.redirectPatterns, viewName) || viewName.startsWith("redirect:"));
}
}
2. MAVMethodReturnValueHandler
/**
* Handles return values of type {@link ModelAndView} copying view and model
* information to the {@link ModelAndViewContainer}.
*
* <p>If the return value is {@code null}, the
* {@link ModelAndViewContainer#setRequestHandled(boolean)} flag is set to
* {@code true} to indicate the request was handled directly.
*
* <p>A {@link ModelAndView} return type has a set purpose. Therefore this
* handler should be configured ahead of handlers that support any return
* value type annotated with {@code @ModelAttribute} or {@code @ResponseBody}
* to ensure they don't take over.
*
* @author Rossen Stoyanchev
* @since 3.1
*/
public class ModelAndViewMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return ModelAndView.class.isAssignableFrom(returnType.getParameterType());
}
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue == null) {
mavContainer.setRequestHandled(true);
return;
}
ModelAndView mav = (ModelAndView) returnValue;
if (mav.isReference()) {
String viewName = mav.getViewName();
mavContainer.setViewName(viewName);
if (viewName != null && isRedirectViewName(viewName)) {
mavContainer.setRedirectModelScenario(true);
}
}
else {
View view = mav.getView();
mavContainer.setView(view);
if (view instanceof SmartView && ((SmartView) view).isRedirectView()) {
mavContainer.setRedirectModelScenario(true);
}
}
mavContainer.setStatus(mav.getStatus());
mavContainer.addAllAttributes(mav.getModel());
}
}
3. ReqRespBodyMtdProcessor
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
/**
* Complete constructor for resolving {@code @RequestBody} and handling
* {@code @ResponseBody}.
*/
public RequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters,
@Nullable ContentNegotiationManager manager,
@Nullable List<Object> requestResponseBodyAdvice) {
super(converters, manager, requestResponseBodyAdvice);
}
@Override
public boolean supportsReturnType(MethodParameter returnType) {
// 该方法或者所在类是否有 @ResponseBody 注解
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
returnType.hasMethodAnnotation(ResponseBody.class));
}
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
// 1. 设置已处理
mavContainer.setRequestHandled(true);
// 创建请求和响应
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
// Try even with null return value. ResponseBodyAdvice could get involved.
// 2... AbstractMessageConverterMethodProcessor 使用 HttpMessageConverter 对对象进行转换,并写入到响应
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
}
1. AbsMsgConverterMtdArgResolver
public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver {
private static final Set<HttpMethod> SUPPORTED_METHODS =
EnumSet.of(HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH);
private final RequestResponseBodyAdviceChain advice;
protected final List<HttpMessageConverter<?>> messageConverters;
/**
* Basic constructor with converters only.
*/
public AbstractMessageConverterMethodArgumentResolver(List<HttpMessageConverter<?>> converters) {
this(converters, null);
}
/**
* Constructor with converters and {@code Request~} and {@code ResponseBodyAdvice}.
* @since 4.2
*/
public AbstractMessageConverterMethodArgumentResolver(List<HttpMessageConverter<?>> converters,
@Nullable List<Object> requestResponseBodyAdvice) {
Assert.notEmpty(converters, "'messageConverters' must not be empty");
// 1.
this.messageConverters = converters;
this.allSupportedMediaTypes = getAllSupportedMediaTypes(converters);
// 2.
this.advice = new RequestResponseBodyAdviceChain(requestResponseBodyAdvice);
}
/**
* Return the configured {@link RequestBodyAdvice} and
* {@link RequestBodyAdvice} where each instance may be wrapped as a
* {@link org.springframework.web.method.ControllerAdviceBean ControllerAdviceBean}.
*/
RequestResponseBodyAdviceChain getAdvice() {
// 1.
return this.advice;
}
}
2. AbsMsgConverterMtdProcessor
- 2. AbstractGenericHttpMessageConverter
- 1. RequestResponseBodyAdviceChain
outputMessage.getBody().flush();
public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolver
implements HandlerMethodReturnValueHandler {
private final ContentNegotiationManager contentNegotiationManager;
/**
* Constructor with list of converters and ContentNegotiationManager as well
* as request/response body advice instances.
*/
protected AbstractMessageConverterMethodProcessor(List<HttpMessageConverter<?>> converters,
@Nullable ContentNegotiationManager manager,
@Nullable List<Object> requestResponseBodyAdvice) {
super(converters, requestResponseBodyAdvice);
this.contentNegotiationManager = (manager != null ? manager : new ContentNegotiationManager());
this.safeExtensions.addAll(this.contentNegotiationManager.getAllFileExtensions());
this.safeExtensions.addAll(SAFE_EXTENSIONS);
}
/**
* Writes the given return type to the given output message.
*
* @param value the value to write to the output message
* @param returnType the type of the value
* @param inputMessage the input messages. Used to inspect the {@code Accept} header.
* @param outputMessage the output message to write to
* @throws IOException thrown in case of I/O errors
* @throws HttpMediaTypeNotAcceptableException thrown when the conditions indicated
* by the {@code Accept} header on the request cannot be met by the
* message converters
* @throws HttpMessageNotWritableException thrown if a given message cannot
* be written by a converter, or if the content-type chosen by the server
* has no compatible converter.
*/
@SuppressWarnings({"rawtypes", "unchecked"})
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
ServletServerHttpRequest inputMessage,
ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
// 获得 body、valueType、targetType
Object body;
Class<?> valueType;
Type targetType;
// 1. 如果是字符串则直接赋值
if (value instanceof CharSequence) {
body = value.toString();
valueType = String.class;
targetType = String.class;
} else {
body = value;
// 2.. 获取返回结果的类型(返回值 body 不为空则直接获取其类型,否则从返回结果类型 returnType 获取其返回值类型)
valueType = getReturnValueType(body, returnType);
// 3.. 获取泛型 Object
targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());
}
// 4. 是否为 Resource 类型
if (isResourceType(value, returnType)) { // skip
// 设置响应头 Accept-Ranges
outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes");
// 数据不为空、请求头中的 Range 不为空、响应码为 200
if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null &&
outputMessage.getServletResponse().getStatus() == 200) {
Resource resource = (Resource) value;
try {
List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();
// 断点续传,客户端已下载一部分数据,此时需要设置响应码为 206
outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value());
// 获取哪一段数据需返回
body = HttpRange.toResourceRegions(httpRanges, resource);
valueType = body.getClass();
targetType = RESOURCE_REGION_LIST_TYPE;
} catch (IllegalArgumentException ex) {
outputMessage.getHeaders().set(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength());
outputMessage.getServletResponse().setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value());
}
}
}
// 选择使用的 MediaType
MediaType selectedMediaType = null;
// 获得响应中的 ContentType 的值
MediaType contentType = outputMessage.getHeaders().getContentType();
// 如果存在 ContentType 的值,并且不包含通配符,则使用它作为 selectedMediaType
boolean isContentTypePreset = contentType != null && contentType.isConcrete();
if (isContentTypePreset) {
if (logger.isDebugEnabled()) {
logger.debug("Found 'Content-Type:" + contentType + "' in response");
}
selectedMediaType = contentType;
} else {
HttpServletRequest request = inputMessage.getServletRequest();
// 5.. 从请求中,获得可接受的 MediaType 数组。默认实现是,从请求头 ACCEPT 中获取 => [*/*]
List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);
// 6.. 获得可产生的 MediaType 数组 => [application/json, application/*+json]
List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
// 如果 body 非空,并且无可产生的 MediaType 数组,则抛出 HttpMediaTypeNotAcceptableException 异常
if (body != null && producibleTypes.isEmpty()) {
throw new HttpMessageNotWritableException(
"No converter found for return value of type: " + valueType);
}
// 通过 acceptableTypes 来比对,将符合的 producibleType 添加到 mediaTypesToUse 结果数组中
List<MediaType> mediaTypesToUse = new ArrayList<>();
for (MediaType requestedType : acceptableTypes) {
for (MediaType producibleType : producibleTypes) {
// 7... MediaType
if (requestedType.isCompatibleWith(producibleType)) {
mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
}
}
}
// 如果没有符合的,并且 body 非空,则抛出 HttpMediaTypeNotAcceptableException 异常
if (mediaTypesToUse.isEmpty()) { // skip
if (body != null) {
throw new HttpMediaTypeNotAcceptableException(producibleTypes);
}
if (logger.isDebugEnabled()) {
logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes);
}
return;
}
// 按照 MediaType 的 specificity 和 quality 排序
MediaType.sortBySpecificityAndQuality(mediaTypesToUse);
// 选择其中一个最匹配的,主要考虑不包含通配符的。eg:application/json;q=0.8
for (MediaType mediaType : mediaTypesToUse) { // [application/json, application/*+json]
// 8... MimeType
if (mediaType.isConcrete()) {
selectedMediaType = mediaType; // application/json
break;
} else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {
selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
break;
}
}
if (logger.isDebugEnabled()) {
logger.debug("Using '" + selectedMediaType + "', given " +
acceptableTypes + " and supported " + producibleTypes);
}
}
// 如果匹配到,则进行写入逻辑
if (selectedMediaType != null) {
// 移除 quality。eg:application/json;q=0.8 => application/json
selectedMediaType = selectedMediaType.removeQualityValue();
// 遍历 messageConverters 数组
for (HttpMessageConverter<?> converter : this.messageConverters) {
// 判断 HttpMessageConverter 是否支持转换目标类型
GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
(GenericHttpMessageConverter<?>) converter : null);
// 9... AbstractGenericHttpMessageConverter
if (genericConverter != null ?
((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
converter.canWrite(valueType, selectedMediaType)) {
// 10... AbstractMessageConverterMethodArgumentResolver
// RequestResponseBodyAdviceChain.beforeBodyWrite()
// `@ControllerAdvice + implements ResponseBodyAdvice<?>`
body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
inputMessage, outputMessage);
// body 非空,则进行写入
if (body != null) {
// 这个变量的用途是,打印是匿名类,需要有 final
Object theBody = body;
LogFormatUtils.traceDebug(logger, traceOn ->
"Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
// 添加 CONTENT_DISPOSITION 头,一般情况下用不到
addContentDispositionHeader(inputMessage, outputMessage);
// 写入内容
if (genericConverter != null) {
// 11... AbstractGenericHttpMessageConverter
genericConverter.write(body, targetType, selectedMediaType, outputMessage);
} else {
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
}
} else {
if (logger.isDebugEnabled()) {
logger.debug("Nothing to write: null body");
}
}
// return 返回,结束整个逻辑
return;
}
}
}
// 如果到达此处,并且 body 非空,说明没有匹配的 HttpMessageConverter 转换器,则抛出 HttpMediaTypeNotAcceptableException 异常
if (body != null) {
Set<MediaType> producibleMediaTypes =
(Set<MediaType>) inputMessage.getServletRequest()
.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
if (isContentTypePreset || !CollectionUtils.isEmpty(producibleMediaTypes)) {
throw new HttpMessageNotWritableException(
"No converter for [" + valueType + "] with preset Content-Type '" + contentType + "'");
}
throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
}
}
private List<MediaType> getAcceptableMediaTypes(HttpServletRequest request)
throws HttpMediaTypeNotAcceptableException {
return this.contentNegotiationManager.resolveMediaTypes(new ServletWebRequest(request));
}
@SuppressWarnings("unchecked")
protected List<MediaType> getProducibleMediaTypes(
HttpServletRequest request, Class<?> valueClass, @Nullable Type targetType) {
// 先从请求 PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE 属性种获得。该属性的来源是 @RequestMapping(producer = xxx) 。
Set<MediaType> mediaTypes =
(Set<MediaType>) request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
// 如果非空,则使用该属性
if (!CollectionUtils.isEmpty(mediaTypes)) {
return new ArrayList<>(mediaTypes);
}
// 如果 allSupportedMediaTypes 非空,则遍历 HttpMessageConverter 数组,进行类型匹配
else if (!this.allSupportedMediaTypes.isEmpty()) {
List<MediaType> result = new ArrayList<>();
for (HttpMessageConverter<?> converter : this.messageConverters) {
if (converter instanceof GenericHttpMessageConverter && targetType != null) {
// 1... AbstractGenericHttpMessageConverter
if (((GenericHttpMessageConverter<?>) converter).canWrite(targetType, valueClass, null)) {
// 2... AbstractHttpMessageConverter
result.addAll(converter.getSupportedMediaTypes());
}
}
else if (converter.canWrite(valueClass, null)) {
result.addAll(converter.getSupportedMediaTypes());
}
}
return result;
}
// 其它,则返回 MediaType.ALL
else {
return Collections.singletonList(MediaType.ALL);
}
}
protected Class<?> getReturnValueType(@Nullable Object value, MethodParameter returnType) {
return (value != null ? value.getClass() : returnType.getParameterType());
}
private Type getGenericType(MethodParameter returnType) {
if (HttpEntity.class.isAssignableFrom(returnType.getParameterType())) {
return ResolvableType.forType(returnType.getGenericParameterType()).getGeneric().getType();
} else {
return returnType.getGenericParameterType();
}
}
protected boolean isResourceType(@Nullable Object value, MethodParameter returnType) {
Class<?> clazz = getReturnValueType(value, returnType);
return clazz != InputStreamResource.class && Resource.class.isAssignableFrom(clazz);
}
}
5. MappingJackson2HttpMsgCvter
public class MappingJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter {
/**
* Construct a new {@link MappingJackson2HttpMessageConverter} using default configuration
* provided by {@link Jackson2ObjectMapperBuilder}.
*/
public MappingJackson2HttpMessageConverter() {
this(Jackson2ObjectMapperBuilder.json().build());
}
/**
* Construct a new {@link MappingJackson2HttpMessageConverter} with a custom {@link ObjectMapper}.
* You can use {@link Jackson2ObjectMapperBuilder} to build it easily.
* @see Jackson2ObjectMapperBuilder#json()
*/
public MappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
// 1... AbstractJackson2HttpMessageConverter <= ObjectMapper
super(objectMapper, MediaType.APPLICATION_JSON, new MediaType("application", "*+json"));
}
@Override
protected void writePrefix(JsonGenerator generator, Object object) throws IOException {
if (this.jsonPrefix != null) { // skip
generator.writeRaw(this.jsonPrefix);
}
}
}
1. AbsHttpMessageConverter
public abstract class AbstractHttpMessageConverter<T> implements HttpMessageConverter<T> {
/**
* 支持的 MediaType
* application/json, application/*+json,
*/
private List<MediaType> supportedMediaTypes = Collections.emptyList();
protected boolean canRead(@Nullable MediaType mediaType) {
if (mediaType == null) {
return true;
}
for (MediaType supportedMediaType : getSupportedMediaTypes()) {
if (supportedMediaType.includes(mediaType)) {
return true;
}
}
return false;
}
@Override
public List<MediaType> getSupportedMediaTypes() {
// 1. [application/json, application/*+json]
return Collections.unmodifiableList(this.supportedMediaTypes);
}
// ------------------------------------------------------------------------------
protected boolean canWrite(@Nullable MediaType mediaType) {
if (mediaType == null || MediaType.ALL.equalsTypeAndSubtype(mediaType)) {
return true;
}
// 1..
for (MediaType supportedMediaType : getSupportedMediaTypes()) {
if (supportedMediaType.isCompatibleWith(mediaType)) { // ... MediaType
return true;
}
}
return false;
}
/**
* Add default headers to the output message.
* <p>This implementation delegates to {@link #getDefaultContentType(Object)} if a
* content type was not provided, set if necessary the default character set, calls
* {@link #getContentLength}, and sets the corresponding headers.
* @since 4.2
*/
protected void addDefaultHeaders(HttpHeaders headers, T t, @Nullable MediaType contentType) throws IOException {
if (headers.getContentType() == null) {
MediaType contentTypeToUse = contentType;
if (contentType == null || !contentType.isConcrete()) { // skip
contentTypeToUse = getDefaultContentType(t);
}
else if (MediaType.APPLICATION_OCTET_STREAM.equals(contentType)) { // skip
MediaType mediaType = getDefaultContentType(t);
contentTypeToUse = (mediaType != null ? mediaType : contentTypeToUse);
}
if (contentTypeToUse != null) {
if (contentTypeToUse.getCharset() == null) {
Charset defaultCharset = getDefaultCharset();
if (defaultCharset != null) {
contentTypeToUse = new MediaType(contentTypeToUse, defaultCharset);
}
}
// 1. application/json
headers.setContentType(contentTypeToUse);
}
}
if (headers.getContentLength() < 0 && !headers.containsKey(HttpHeaders.TRANSFER_ENCODING)) {
Long contentLength = getContentLength(t, headers.getContentType());
if (contentLength != null) {
headers.setContentLength(contentLength);
}
}
}
}
2. AbsGenericHttpMsgConverter
public abstract class AbstractGenericHttpMessageConverter<T> extends AbstractHttpMessageConverter<T>
implements GenericHttpMessageConverter<T> {
@Override
public boolean canWrite(@Nullable Type type, Class<?> clazz, @Nullable MediaType mediaType) {
// 1... AbstractJackson2HttpMessageConverter
return canWrite(clazz, mediaType);
}
/**
* This implementation sets the default headers by calling {@link #addDefaultHeaders},
* and then calls {@link #writeInternal}.
*/
@Override
public final void write(final T t, @Nullable final Type type, @Nullable MediaType contentType,
HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
final HttpHeaders headers = outputMessage.getHeaders();
// 1... AbstractHttpMessageConverter
addDefaultHeaders(headers, t, contentType);
if (outputMessage instanceof StreamingHttpOutputMessage) { // skip
StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage;
streamingOutputMessage.setBody(outputStream -> writeInternal(t, type, new HttpOutputMessage() {
@Override
public OutputStream getBody() {
return outputStream;
}
@Override
public HttpHeaders getHeaders() {
return headers;
}
}));
}
else {
// 2... AbstractJackson2HttpMessageConverter
writeInternal(t, type, outputMessage);
// 3.
outputMessage.getBody().flush();
}
}
}
3. AbsJackson2HttpMsgConverter
public abstract class AbstractJackson2HttpMessageConverter extends AbstractGenericHttpMessageConverter<Object> {
protected ObjectMapper objectMapper;
protected AbstractJackson2HttpMessageConverter(ObjectMapper objectMapper) {
// 1. ObjectMapper
this.objectMapper = objectMapper;
DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter();
prettyPrinter.indentObjectsWith(new DefaultIndenter(" ", "\ndata:"));
this.ssePrettyPrinter = prettyPrinter;
}
@Override
public boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) {
// 1... AbstractHttpMessageConverter
if (!canRead(mediaType)) {
return false;
}
// 2.. 获得方法入参的类型
JavaType javaType = getJavaType(type, contextClass);
AtomicReference<Throwable> causeRef = new AtomicReference<>();
// 3. 通过 ObjectMapper 判断是否能够反序列化
if (this.objectMapper.canDeserialize(javaType, causeRef)) {
// 4.
return true;
}
logWarningIfNecessary(javaType, causeRef.get());
return false;
}
protected JavaType getJavaType(Type type, @Nullable Class<?> contextClass) {
TypeFactory typeFactory = this.objectMapper.getTypeFactory();
// class com.listao.bean.User
return typeFactory.constructType(GenericTypeResolver.resolveType(type, contextClass));
}
// ------------------------------------------------------------------------------------------
@Override
public Object read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
// 1. 获得方法入参的类型
JavaType javaType = getJavaType(type, contextClass);
// 2.. 从请求中读取该类型的方法入参
return readJavaType(javaType, inputMessage);
}
private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) throws IOException {
MediaType contentType = inputMessage.getHeaders().getContentType(); // application/json
Charset charset = getCharset(contentType); // UTF-8
boolean isUnicode = ENCODINGS.containsKey(charset.name()); // true
try {
// 如果请求是 MappingJacksonInputMessage 类型,默认不是
if (inputMessage instanceof MappingJacksonInputMessage) { // skip
Class<?> deserializationView = ((MappingJacksonInputMessage) inputMessage).getDeserializationView();
if (deserializationView != null) {
ObjectReader objectReader = this.objectMapper.readerWithView(deserializationView).forType(javaType);
if (isUnicode) {
return objectReader.readValue(inputMessage.getBody());
}
else {
Reader reader = new InputStreamReader(inputMessage.getBody(), charset);
return objectReader.readValue(reader);
}
}
}
if (isUnicode) {
// **通过 ObjectMapper 从请求中读取该类型的方法入参** => ObjectMapper
return this.objectMapper.readValue(inputMessage.getBody(), javaType);
} else {
Reader reader = new InputStreamReader(inputMessage.getBody(), charset);
return this.objectMapper.readValue(reader, javaType);
}
}
catch (InvalidDefinitionException ex) {
throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex);
}
catch (JsonProcessingException ex) {
throw new HttpMessageNotReadableException("JSON parse error: " + ex.getOriginalMessage(), ex, inputMessage);
}
}
// -------------------------------------------------------------------------------------------
@Override
public boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {
// 1... AbstractHttpMessageConverter 判断是否支持该 MediaType,也就是 Content-Type
if (!canWrite(mediaType)) {
return false;
}
if (mediaType != null && mediaType.getCharset() != null) {
Charset charset = mediaType.getCharset();
if (!ENCODINGS.containsKey(charset.name())) {
return false;
}
}
AtomicReference<Throwable> causeRef = new AtomicReference<>();
// 2. 通过 ObjectMapper 判断是否能够序列化
if (this.objectMapper.canSerialize(clazz, causeRef)) {
return true;
}
logWarningIfNecessary(clazz, causeRef.get());
return false;
}
@Override
protected void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
// 获取编码方式
// 获取 Content-Type,例如 `application/json;charset=UTF-8`
MediaType contentType = outputMessage.getHeaders().getContentType();
// 从 Content-Type 获取编码方式,默认 UTF8
JsonEncoding encoding = getJsonEncoding(contentType);
// 1. (jackson-core 包)构建一个 Json 生成器 `generator`,指定`输出流(响应)`和编码
// 例如:UTF8JsonGenerator 对象
JsonGenerator generator = this.objectMapper.getFactory().createGenerator(outputMessage.getBody(), encoding);
try {
// 2... MappingJackson2HttpMessageConverter 设置前缀,默认没有
writePrefix(generator, object);
// 获得方法的返回结果对象 `value`,返回结果类型 `javaType`
Object value = object;
Class<?> serializationView = null;
FilterProvider filters = null;
JavaType javaType = null;
// 如果返回结果对象是 MappingJacksonValue 类型,没使用过
if (object instanceof MappingJacksonValue) { // skip
MappingJacksonValue container = (MappingJacksonValue) object;
value = container.getValue();
serializationView = container.getSerializationView();
filters = container.getFilters();
}
// 获取方法的返回结果的类型 `javaType`
if (type != null && TypeUtils.isAssignable(type, value.getClass())) {
javaType = getJavaType(type, null);
}
// 3. 创建 ObjectWriter 对象 `objectWriter`,没有特殊配置通过 `this.objectMapper.writer()` 生成
ObjectWriter objectWriter = (serializationView != null ?
this.objectMapper.writerWithView(serializationView) :
this.objectMapper.writer());
if (filters != null) { // skip
objectWriter = objectWriter.with(filters);
}
if (javaType != null && javaType.isContainerType()) { // skip
objectWriter = objectWriter.forType(javaType);
}
// 获取序列化配置
SerializationConfig config = objectWriter.getConfig();
if (contentType != null && contentType.isCompatibleWith(MediaType.TEXT_EVENT_STREAM) &&
config.isEnabled(SerializationFeature.INDENT_OUTPUT)) { // skip
objectWriter = objectWriter.with(this.ssePrettyPrinter);
}
// 4. **【重点】**通过 `objectWriter` 将返回结果进行序列化,设置到 `generator` 中
objectWriter.writeValue(generator, value);
// 设置后缀,默认没有,子类无实现
writeSuffix(generator, object);
// 5. 让 `generator` 刷出数据,以 Json 格式输出,也就是会往响应中刷出 Json 格式的返回结果
generator.flush();
}
catch (InvalidDefinitionException ex) {
throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex);
}
catch (JsonProcessingException ex) {
throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getOriginalMessage(), ex);
}
}
}
1. MediaType
public class MediaType extends MimeType implements Serializable {
public static final String ALL_VALUE = "*/*";
public static final String APPLICATION_ATOM_XML_VALUE = "application/atom+xml";
public static final String APPLICATION_CBOR_VALUE = "application/cbor";
public static final String APPLICATION_FORM_URLENCODED_VALUE = "application/x-www-form-urlencoded";
public static final String APPLICATION_JSON_VALUE = "application/json";
@Deprecated
public static final String APPLICATION_JSON_UTF8_VALUE = "application/json;charset=UTF-8";
public static final String APPLICATION_OCTET_STREAM_VALUE = "application/octet-stream";
public static final String APPLICATION_PDF_VALUE = "application/pdf";
public static final String APPLICATION_PROBLEM_JSON_VALUE = "application/problem+json";
@Deprecated
public static final String APPLICATION_PROBLEM_JSON_UTF8_VALUE = "application/problem+json;charset=UTF-8";
public static final String APPLICATION_PROBLEM_XML_VALUE = "application/problem+xml";
public static final String APPLICATION_RSS_XML_VALUE = "application/rss+xml";
public static final String APPLICATION_STREAM_JSON_VALUE = "application/stream+json";
public static final String APPLICATION_XHTML_XML_VALUE = "application/xhtml+xml";
public static final String APPLICATION_XML_VALUE = "application/xml";
public static final String IMAGE_GIF_VALUE = "image/gif";
public static final String IMAGE_JPEG_VALUE = "image/jpeg";
public static final String IMAGE_PNG_VALUE = "image/png";
public static final String MULTIPART_FORM_DATA_VALUE = "multipart/form-data";
public static final String MULTIPART_MIXED_VALUE = "multipart/mixed";
public static final String MULTIPART_RELATED_VALUE = "multipart/related";
public static final String TEXT_EVENT_STREAM_VALUE = "text/event-stream";
public static final String TEXT_HTML_VALUE = "text/html";
public static final String TEXT_MARKDOWN_VALUE = "text/markdown";
public static final String TEXT_PLAIN_VALUE = "text/plain";
public static final String TEXT_XML_VALUE = "text/xml";
static {
// Not using "valueOf' to avoid static init cost
ALL = new MediaType("*", "*");
APPLICATION_ATOM_XML = new MediaType("application", "atom+xml");
APPLICATION_CBOR = new MediaType("application", "cbor");
APPLICATION_FORM_URLENCODED = new MediaType("application", "x-www-form-urlencoded");
APPLICATION_JSON = new MediaType("application", "json");
APPLICATION_JSON_UTF8 = new MediaType("application", "json", StandardCharsets.UTF_8);
APPLICATION_OCTET_STREAM = new MediaType("application", "octet-stream");
APPLICATION_PDF = new MediaType("application", "pdf");
APPLICATION_PROBLEM_JSON = new MediaType("application", "problem+json");
APPLICATION_PROBLEM_JSON_UTF8 = new MediaType("application", "problem+json", StandardCharsets.UTF_8);
APPLICATION_PROBLEM_XML = new MediaType("application", "problem+xml");
APPLICATION_RSS_XML = new MediaType("application", "rss+xml");
APPLICATION_STREAM_JSON = new MediaType("application", "stream+json");
APPLICATION_XHTML_XML = new MediaType("application", "xhtml+xml");
APPLICATION_XML = new MediaType("application", "xml");
IMAGE_GIF = new MediaType("image", "gif");
IMAGE_JPEG = new MediaType("image", "jpeg");
IMAGE_PNG = new MediaType("image", "png");
MULTIPART_FORM_DATA = new MediaType("multipart", "form-data");
MULTIPART_MIXED = new MediaType("multipart", "mixed");
MULTIPART_RELATED = new MediaType("multipart", "related");
TEXT_EVENT_STREAM = new MediaType("text", "event-stream");
TEXT_HTML = new MediaType("text", "html");
TEXT_MARKDOWN = new MediaType("text", "markdown");
TEXT_PLAIN = new MediaType("text", "plain");
TEXT_XML = new MediaType("text", "xml");
}
public MediaType(String type, String subtype) {
super(type, subtype, Collections.emptyMap());
}
public boolean isCompatibleWith(@Nullable MediaType other) {
return super.isCompatibleWith(other);
}
}
2. MimeType
public class MimeType implements Comparable<MimeType>, Serializable {
private static final long serialVersionUID = 4085923477777865903L;
protected static final String WILDCARD_TYPE = "*";
private static final String PARAM_CHARSET = "charset";
private static final BitSet TOKEN;
static {
// variable names refer to RFC 2616, section 2.2
BitSet ctl = new BitSet(128);
for (int i = 0; i <= 31; i++) {
ctl.set(i);
}
ctl.set(127);
BitSet separators = new BitSet(128);
separators.set('(');
separators.set(')');
separators.set('<');
separators.set('>');
separators.set('@');
separators.set(',');
separators.set(';');
separators.set(':');
separators.set('\\');
separators.set('\"');
separators.set('/');
separators.set('[');
separators.set(']');
separators.set('?');
separators.set('=');
separators.set('{');
separators.set('}');
separators.set(' ');
separators.set('\t');
TOKEN = new BitSet(128);
TOKEN.set(0, 128);
TOKEN.andNot(ctl);
TOKEN.andNot(separators);
}
public MimeType(String type, String subtype, @Nullable Map<String, String> parameters) {
Assert.hasLength(type, "'type' must not be empty");
Assert.hasLength(subtype, "'subtype' must not be empty");
checkToken(type);
checkToken(subtype);
this.type = type.toLowerCase(Locale.ENGLISH);
this.subtype = subtype.toLowerCase(Locale.ENGLISH);
if (!CollectionUtils.isEmpty(parameters)) {
Map<String, String> map = new LinkedCaseInsensitiveMap<>(parameters.size(), Locale.ENGLISH);
parameters.forEach((attribute, value) -> {
checkParameters(attribute, value);
map.put(attribute, value);
});
this.parameters = Collections.unmodifiableMap(map);
}
else {
this.parameters = Collections.emptyMap();
}
}
public boolean isCompatibleWith(@Nullable MimeType other) {
if (other == null) {
return false;
}
if (isWildcardType() || other.isWildcardType()) {
return true;
} else if (getType().equals(other.getType())) {
if (getSubtype().equals(other.getSubtype())) {
return true;
}
// Wildcard with suffix? e.g. application/*+xml
if (isWildcardSubtype() || other.isWildcardSubtype()) {
int thisPlusIdx = getSubtype().lastIndexOf('+');
int otherPlusIdx = other.getSubtype().lastIndexOf('+');
if (thisPlusIdx == -1 && otherPlusIdx == -1) {
return true;
} else if (thisPlusIdx != -1 && otherPlusIdx != -1) {
String thisSubtypeNoSuffix = getSubtype().substring(0, thisPlusIdx);
String otherSubtypeNoSuffix = other.getSubtype().substring(0, otherPlusIdx);
String thisSubtypeSuffix = getSubtype().substring(thisPlusIdx + 1);
String otherSubtypeSuffix = other.getSubtype().substring(otherPlusIdx + 1);
if (thisSubtypeSuffix.equals(otherSubtypeSuffix) &&
(WILDCARD_TYPE.equals(thisSubtypeNoSuffix) || WILDCARD_TYPE.equals(otherSubtypeNoSuffix))) {
return true;
}
}
}
}
return false;
}
public boolean isWildcardType() {
return WILDCARD_TYPE.equals(getType()); // */*
}
}
6. ReqRespBodyAdviceChain
class RequestResponseBodyAdviceChain implements RequestBodyAdvice, ResponseBodyAdvice<Object> {
private final List<Object> requestBodyAdvice = new ArrayList<>(4);
private final List<Object> responseBodyAdvice = new ArrayList<>(4);
/**
* Create an instance from a list of objects that are either of type
* {@code ControllerAdviceBean} or {@code RequestBodyAdvice}.
*/
public RequestResponseBodyAdviceChain(@Nullable List<Object> requestResponseBodyAdvice) {
// 1..
this.requestBodyAdvice.addAll(getAdviceByType(requestResponseBodyAdvice, RequestBodyAdvice.class));
this.responseBodyAdvice.addAll(getAdviceByType(requestResponseBodyAdvice, ResponseBodyAdvice.class));
}
@SuppressWarnings("unchecked")
static <T> List<T> getAdviceByType(@Nullable List<Object> requestResponseBodyAdvice, Class<T> adviceType) {
if (requestResponseBodyAdvice != null) {
List<T> result = new ArrayList<>();
for (Object advice : requestResponseBodyAdvice) {
Class<?> beanType = (advice instanceof ControllerAdviceBean ?
((ControllerAdviceBean) advice).getBeanType() : advice.getClass());
if (beanType != null && adviceType.isAssignableFrom(beanType)) {
result.add((T) advice);
}
}
return result;
}
return Collections.emptyList();
}
// ----------------------------------------------------------------------------------------------------
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage request, MethodParameter parameter,
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
for (RequestBodyAdvice advice : getMatchingAdvice(parameter, RequestBodyAdvice.class)) {
if (advice.supports(parameter, targetType, converterType)) {
request = advice.beforeBodyRead(request, parameter, targetType, converterType);
}
}
return request;
}
@SuppressWarnings("unchecked")
private <A> List<A> getMatchingAdvice(MethodParameter parameter, Class<? extends A> adviceType) {
List<Object> availableAdvice = getAdvice(adviceType);
if (CollectionUtils.isEmpty(availableAdvice)) {
return Collections.emptyList();
}
List<A> result = new ArrayList<>(availableAdvice.size());
for (Object advice : availableAdvice) {
if (advice instanceof ControllerAdviceBean) {
ControllerAdviceBean adviceBean = (ControllerAdviceBean) advice;
if (!adviceBean.isApplicableToBeanType(parameter.getContainingClass())) {
continue;
}
advice = adviceBean.resolveBean();
}
if (adviceType.isAssignableFrom(advice.getClass())) {
result.add((A) advice);
}
}
return result;
}
private List<Object> getAdvice(Class<?> adviceType) {
if (RequestBodyAdvice.class == adviceType) {
return this.requestBodyAdvice;
}
else if (ResponseBodyAdvice.class == adviceType) {
return this.responseBodyAdvice;
}
else {
throw new IllegalArgumentException("Unexpected adviceType: " + adviceType);
}
}
// ----------------------------------------------------------------------------------------------------
@Override
@Nullable
public Object beforeBodyWrite(@Nullable Object body, MethodParameter returnType, MediaType contentType,
Class<? extends HttpMessageConverter<?>> converterType,
ServerHttpRequest request, ServerHttpResponse response) {
// 1..
return processBody(body, returnType, contentType, converterType, request, response);
}
@SuppressWarnings("unchecked")
@Nullable
private <T> Object processBody(@Nullable Object body, MethodParameter returnType, MediaType contentType,
Class<? extends HttpMessageConverter<?>> converterType,
ServerHttpRequest request, ServerHttpResponse response) {
// 1.. `implements ResponseBodyAdvice<>`
for (ResponseBodyAdvice<?> advice : getMatchingAdvice(returnType, ResponseBodyAdvice.class)) {
// 2... MyResponseBodyAdvice
if (advice.supports(returnType, converterType)) {
// 3... MyResponseBodyAdvice
body = ((ResponseBodyAdvice<T>) advice).beforeBodyWrite((T) body, returnType,
contentType, converterType, request, response);
}
}
return body;
}
}
@ControllerAdvice
public class MyResponseBodyAdvice implements ResponseBodyAdvice<User> {
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public User beforeBodyWrite(User body, MethodParameter returnType, MediaType selectedContentType, Class<?
extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
System.out.println("-------- @ControllerAdvice#ResponseBodyAdvice --------");
return body;
}
}
7. JsonViewRequestBodyAdvice
public class JsonViewRequestBodyAdvice extends RequestBodyAdviceAdapter {
@Override
public boolean supports(MethodParameter methodParameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType) {
return (AbstractJackson2HttpMessageConverter.class.isAssignableFrom(converterType) &&
methodParameter.getParameterAnnotation(JsonView.class) != null);
}
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter methodParameter,
Type targetType, Class<? extends HttpMessageConverter<?>> selectedConverterType) throws IOException {
JsonView ann = methodParameter.getParameterAnnotation(JsonView.class);
Assert.state(ann != null, "No JsonView annotation");
Class<?>[] classes = ann.value();
if (classes.length != 1) {
throw new IllegalArgumentException(
"@JsonView only supported for request body advice with exactly 1 class argument: " + methodParameter);
}
return new MappingJacksonInputMessage(inputMessage.getBody(), inputMessage.getHeaders(), classes[0]);
}
}
@JsonView
可以过滤序列化对象的字段属性,可以有选择的序列化对象
public class User {
@JsonView(View.Summary.class)
private Long id;
@JsonView(View.Summary.class)
private String firstname;
@JsonView(View.Summary.class)
private String lastname;
private String email;
private String address;
private String postalCode;
private String city;
private String country;
}
@RestController
public class UserRestController{
@Autowired
private UserService userService;
@RequestMapping("/user")
@JsonView(View.Summary.class)
public List<User> getUsers(){
return userService.listUsers();
}
}
6. getModelAndView()
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
implements BeanFactoryAware, InitializingBean {
@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
// 1... ModelFactory 更新model(设置sessionAttributes和给model设置BindingResult)
modelFactory.updateModel(webRequest, mavContainer);
// 2. @ResponseBody 进入,返回 null。情况一,如果 mavContainer 已处理,则返回“空”的 ModelAndView 对象。
if (mavContainer.isRequestHandled()) {
return null;
}
// 情况二,如果mavContainer未处理,则基于`mavContainer`生成ModelAndView对象
ModelMap model = mavContainer.getModel();
// 3. 创建 ModelAndView 对象,并设置相关属性
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
// 如果mavContainer里的view不是引用,也就是不是string类型,则设置到mv中
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
// 4. FlashMap
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request != null) {
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
}
return mav;
}
}
1. ModelAndViewContainer
public class ModelAndViewContainer {
// 默认使用的model对象,使用的情况最多
private final ModelMap defaultModel = new BindingAwareModelMap();
private final SessionStatus sessionStatus = new SimpleSessionStatus();
// redirect类型的model
@Nullable
private ModelMap redirectModel;
public ModelMap getDefaultModel() {
// 1.
return this.defaultModel;
}
/**
* Return the {@link SessionStatus} instance to use that can be used to
* signal that session processing is complete.
*/
public SessionStatus getSessionStatus() {
// 1.
return this.sessionStatus;
}
}
2. SessionAttributesHandler
SessionAttributesHandler
SessionAttributeStore
public class SessionAttributesHandler {
// 具体的参数存储管理类
private final SessionAttributeStore sessionAttributeStore;
// 存储@SessionAttributes注解里value的值
private final Set<String> attributeNames = new HashSet<>();
// 存储@SessionAttributes注解里types对应的值
private final Set<Class<?>> attributeTypes = new HashSet<>();
// 存储所有已知可以被当前处理器处理的属性名,可以在构造方法中将所有attributeName的值设置进去,还可以在调用isHandlerSessionAttribute的时候回添加
private final Set<String> knownAttributeNames = Collections.newSetFromMap(new ConcurrentHashMap<>(4));
/**
* Create a new session attributes handler. Session attribute names and types
* are extracted from the {@code @SessionAttributes} annotation, if present,
* on the given type.
* @param handlerType the controller type
* @param sessionAttributeStore used for session access
*/
public SessionAttributesHandler(Class<?> handlerType, SessionAttributeStore sessionAttributeStore) {
Assert.notNull(sessionAttributeStore, "SessionAttributeStore may not be null");
this.sessionAttributeStore = sessionAttributeStore;
// 3. @SessionAttributes
SessionAttributes ann = AnnotatedElementUtils.findMergedAnnotation(handlerType, SessionAttributes.class);
if (ann != null) {
Collections.addAll(this.attributeNames, ann.names());
Collections.addAll(this.attributeTypes, ann.types());
}
this.knownAttributeNames.addAll(this.attributeNames);
}
/**
* Store a subset of the given attributes in the session. Attributes not
* declared as session attributes via {@code @SessionAttributes} are ignored.
* @param request the current request
* @param attributes candidate attributes for session storage
*/
public void storeAttributes(WebRequest request, Map<String, ?> attributes) {
attributes.forEach((name, value) -> {
// 1..
if (value != null && isHandlerSessionAttribute(name, value.getClass())) {
this.sessionAttributeStore.storeAttribute(request, name, value);
}
});
}
public boolean isHandlerSessionAttribute(String attributeName, Class<?> attributeType) {
Assert.notNull(attributeName, "Attribute name must not be null");
// 2.
if (this.attributeNames.contains(attributeName) || this.attributeTypes.contains(attributeType)) {
this.knownAttributeNames.add(attributeName);
return true;
}
else {
return false;
}
}
}
3. @SessionAttribute
- 当向
Request
作用域设置数据时,同时也向Session
中保存一份
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface SessionAttributes {
}
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SessionAttribute {
}
7. Cache-Control
handle() => handleInternal()
protected static final String HEADER_CACHE_CONTROL = "Cache-Control";
// 响应不包含'Cache-Control'头
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { // skip
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
} else {
// 1... WebContentGenerator
prepareResponse(response);
}
}
1. WebContentGenerator
public abstract class WebContentGenerator extends WebApplicationObjectSupport {
/**
* HTTP method "GET".
*/
public static final String METHOD_GET = "GET";
/**
* HTTP method "HEAD".
*/
public static final String METHOD_HEAD = "HEAD";
/**
* HTTP method "POST".
*/
public static final String METHOD_POST = "POST";
private static final String HEADER_PRAGMA = "Pragma";
private static final String HEADER_EXPIRES = "Expires";
protected static final String HEADER_CACHE_CONTROL = "Cache-Control";
/**
* 缓存过期时间,正数表示需要缓存,负数表示不做任何事情(也就是说保留上次的缓存设置),
* <p>
* 1、cacheSeconds =0时,则将设置如下响应头数据:
* <p>
* Pragma:no-cache // HTTP 1.0的不缓存响应头
* <p>
* Expires:1L // useExpiresHeader=true时,HTTP 1.0
* <p>
* Cache-Control :no-cache // useCacheControlHeader=true时,HTTP 1.1
* <p>
* Cache-Control :no-store // useCacheControlNoStore=true时,该设置是防止Firefox缓存
* <p>
* 2、cacheSeconds > 0时,则将设置如下响应头数据:
* <p>
* Expires:System.currentTimeMillis() + cacheSeconds * 1000L // useExpiresHeader=true时,HTTP 1.0
* <p>
* Cache-Control :max-age=cacheSeconds // useCacheControlHeader=true时,HTTP 1.1
* <p>
* 3、cacheSeconds < 0时,则什么都不设置,即保留上次的缓存设置。
*/
private int cacheSeconds = -1;
// deprecated fields
/**
* 是否使用Http1.0协议过期响应,如果true则会在响应头添加Expires,需要配合cacheSeconds使用
* Use HTTP 1.0 expires header?
*/
private boolean useExpiresHeader = false;
/**
* 是否使用HTTP1.1协议的缓存控制响应头,如果true则会在响应头添加;需要配合cacheSeconds使用
* Use HTTP 1.1 cache-control header?
*/
private boolean useCacheControlHeader = true;
/**
* 是否使用HTTP 1.1协议的缓存控制响应头,如果true则会在响应头添加;需要配合cacheSeconds使用
* Use HTTP 1.1 cache-control header value "no-store"?
*/
private boolean useCacheControlNoStore = true;
protected final void prepareResponse(HttpServletResponse response) {
if (this.cacheControl != null) {
applyCacheControl(response, this.cacheControl);
} else {
applyCacheSeconds(response, this.cacheSeconds);
}
if (this.varyByRequestHeaders != null) {
for (String value : getVaryRequestHeadersToAdd(response, this.varyByRequestHeaders)) {
response.addHeader("Vary", value);
}
}
}
}