springmvc–HandlerAdapter
处理器适配器
文章目录
- springmvc--`HandlerAdapter`处理器适配器
- 1 用途
- 2 `springmvc`默认的`HandlerAdapter`
- 3 `HandlerAdapter`接口
- 4 `RequestMappingHandlerAdapter`的构造方法
- 5 `afterPropertiesSet()`方法(`InitializingBean`接口唯一方法)
- 6 `supports(Object handler)`方法,判断处理器适配器是否支持该处理器方法
- 7 `getLastModified(HttpServletRequest request, Object handler)`方法,获取上一次修改请求的时间
- 8 `handle(HttpServletRequest request, HttpServletResponse response, Object handler)`方法, 完成处理器方法的调用
- 8.1 `checkRequest(HttpServletRequest request) `方法,检查请求
- 8.2 `invokeHandlerMethod()`方法,执行处理器方法并返回一个`ModelAndView`对象
- 9 `ControllerAdviceBean`类,封装`@ControllerAdvice`注解的`bean`
- 10 `HandlerTypePredicate`,断言器,用来匹配请求处理组件的类型
- 11 `ServletWebRequest`类,适配`HttpServletRequest`
- 12 `WebDataBinderFactory`数据绑定器工厂
- 13 `SessionAttributesHandler` 会话属性处理器
- 13.1 `SessionAttributeStore`策略接口,定义操作`session域属性`的规范
- 13.2 `SessionAttributesHandler`的构造方法
- 13.3 `retrieveAttributes(WebRequest request)`方法,得到`session`中的已知属性的值
- 13.4 `isHandlerSessionAttribute(String attributeName, Class<?> attributeType)`方法,判断属性名称或属性类型是否与用户通过`@SessionAttributes`注解指定的属性匹配
- 13.5 `cleanupAttributes(WebRequest request)`方法,清理会话属性
- 13.6 `storeAttributes(WebRequest request, Map
1 用途
帮助
DispatcherServlet
调用处理器方法。
DispatcherServlet
不管实际如何调用该处理器方法,它只需要调用handlerAdapter.handle(processedRequest, response, mappedHandler.getHandler())
方法,即完成了处理器方法调用。具体的调用逻辑由HandlerAdapter
实现类完成。
2 springmvc
默认的HandlerAdapter
springmvc
会自动的注册4
种类型的HandlerAdapter
,它们之间的关系如下面类图所示
注意:
HandlerMapping
与HandlerAdapter
是一一对应的,而上篇文章说的是RequestMappingHandlerMapping
,所以这里只看RequestMappingHandlerAdapter
(支持@RequestMapping
注解)。况且另外3
种方式基本上被淘汰了。
3 HandlerAdapter
接口
public interface HandlerAdapter {
/**
* 该处理器适配器是否支持该处理器方法
*/
boolean supports(Object handler);
/**
* 完成该处理器方法的调用
*/
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
/**
* Same contract as for HttpServlet's {@code getLastModified} method.
* Can simply return -1 if there's no support in the handler class.
* @param request current HTTP request
* @param handler the handler to use
* @return the lastModified value for the given handler
* @see javax.servlet.http.HttpServlet#getLastModified
* @see org.springframework.web.servlet.mvc.LastModified#getLastModified
*/
long getLastModified(HttpServletRequest request, Object handler);
}
该接口中有
3
个方法,下面我们就来看一下这3
个方法的具体原理
4 RequestMappingHandlerAdapter
的构造方法
//消息转换器
private List<HttpMessageConverter<?>> messageConverters;
public RequestMappingHandlerAdapter() {
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());
}
构造方法只是向里面放入几个
HttpMessageConverter
ByteArrayHttpMessageConverter
StringHttpMessageConverter
SourceHttpMessageConverter
AllEncompassingFormHttpMessageConverter
5 afterPropertiesSet()
方法(InitializingBean
接口唯一方法)
该方法在
bean
初始化阶段被调用
/**
* 这3个属性类型是典型的组合模式。
* XXXResolverComposite持有多个解析器,内部通过遍历这些解析器,使用第一个可以完成解析的解析器
* 进行解析
*/
@Nullable
private HandlerMethodArgumentResolverComposite argumentResolvers;
@Nullable
private HandlerMethodArgumentResolverComposite initBinderArgumentResolvers;
@Nullable
private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
@Override
public void afterPropertiesSet() {
// Do this first, it may add ResponseBody advice beans
//找出所有标注@ControllerAdvice注解的bean,并进行初步处理,见5.1
initControllerAdviceCache();
//用户未进行手动配置,就会使用springmvc默认配置的解析器,见5.2
if (this.argumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.initBinderArgumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
5.1 找出所有标注@ControllerAdvice
注解的bean
,并进行初步处理
//过滤器,有@ModelAttribute注解没有@RequestMapping注解的方法
public static final MethodFilter MODEL_ATTRIBUTE_METHODS = method ->
(!AnnotatedElementUtils.hasAnnotation(method, RequestMapping.class) &&
AnnotatedElementUtils.hasAnnotation(method, ModelAttribute.class));
//过滤器,有@InitBinder注解的方法
public static final MethodFilter INIT_BINDER_METHODS = method ->
AnnotatedElementUtils.hasAnnotation(method, InitBinder.class);
//保存RequestBodyAdvice对象和ResponseBodyAdvice
private final List<Object> requestResponseBodyAdvice = new ArrayList<>();
private void initControllerAdviceCache() {
if (getApplicationContext() == null) {
return;
}
//获取容器中有@ControllerAdvice注解的bean,并封装为ControllerAdviceBean,见9
List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();
//下面开始对@ControllerAdvice注解的bean进行处理
for (ControllerAdviceBean adviceBean : adviceBeans) {
Class<?> beanType = adviceBean.getBeanType();
if (beanType == null) {
throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
}
/**
* 会对@ControllerAdvice注解标注的bean过滤
* 得到有@ModelAttribute注解没有@RequestMapping注解的方法
*/
Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);
//缓存
if (!attrMethods.isEmpty()) {
this.modelAttributeAdviceCache.put(adviceBean, attrMethods);
}
/**
* 会对@ControllerAdvice注解标注的bean过滤
* 得到有@InitBinder注解的方法
*/
Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);
//缓存
if (!binderMethods.isEmpty()) {
this.initBinderAdviceCache.put(adviceBean, binderMethods);
}
/**
* RequestBodyAdvice允许用户在读取和类型转换之前自定义请求体
* ResponseBodyAdvice允许用户@ResponseBody方法之后,HttpMessageConverter写入正文之前
* 自定义响应体
*/
if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
requestResponseBodyAdviceBeans.add(adviceBean);
}
}
/**
* 使用<mvc:annotation-driven>,会自动向requestResponseBodyAdvice属性中填充两个值
* JsonViewRequestBodyAdvice和JsonViewResponseBodyAdvice
*/
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");
}
}
}
总结一下该方法做的事情
- 找到容器中有
@ControllerAdvice
注解的bean
,并封装为ControllerAdviceBean
- 对这些
bean
中的方法分类
- 有
@ModelAttribute
注解没有@RequestMapping
注解的方法,保存到modelAttributeAdviceCache
属性中- 有
@InitBinder
注解的方法,保存到initBinderAdviceCache
属性中- 实现了
RequestBodyAdvice
接口或ResponseBodyAdvice
接口的方法,保存到requestResponseBodyAdvice
属性中注意:
springmvc
配置文件中使用<mvc:annotation-driven>
,会自动向requestResponseBodyAdvice
属性中填充两个值
JsonViewRequestBodyAdvice
JsonViewResponseBodyAdvice
5.2 springmvc
三种解析器(参数,绑定器,返回值)的默认配置
/**
* 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
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));
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());
}
// Catch-all
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
resolvers.add(new ServletModelAttributeMethodProcessor(true));
return resolvers;
}
/**
* Return the list of argument resolvers to use for {@code @InitBinder}
* methods including built-in and custom resolvers.
*/
private List<HandlerMethodArgumentResolver> getDefaultInitBinderArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(20);
// Annotation-based argument resolution
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 ExpressionValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new SessionAttributeMethodArgumentResolver());
resolvers.add(new RequestAttributeMethodArgumentResolver());
// Type-based argument resolution
resolvers.add(new ServletRequestMethodArgumentResolver());
resolvers.add(new ServletResponseMethodArgumentResolver());
// Custom arguments
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(getCustomArgumentResolvers());
}
// Catch-all
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), 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));
handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
this.contentNegotiationManager, this.requestResponseBodyAdvice));
// 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;
}
这么多的解析器,不需要知道原理,看看名字,知道它对应的解析类型就行了。
6 supports(Object handler)
方法,判断处理器适配器是否支持该处理器方法
该方法在
AbstractHandlerMethodAdapter
中实现
/**
* 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) {
//只要这两个条件成立,就表明支持该处理器方法
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
RequestMappingHandlerAdapter
中实现supportsInternal()
方法
/**
* Always return {@code true} since any method argument and return value
* type will be processed in some way. A method argument not recognized
* by any HandlerMethodArgumentResolver is interpreted as a request parameter
* if it is a simple type, or as a model attribute otherwise. A return value
* not recognized by any HandlerMethodReturnValueHandler will be interpreted
* as a model attribute.
*/
@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
return true;
}
从上面代码我们可以发现,只要处理器方法是
HandlerMethod
类型的,就支持
7 getLastModified(HttpServletRequest request, Object handler)
方法,获取上一次修改请求的时间
该方法在
AbstractHandlerMethodAdapter
中实现
/**
* This implementation expects the handler to be an {@link HandlerMethod}.
*/
@Override
public final long getLastModified(HttpServletRequest request, Object handler) {
return getLastModifiedInternal(request, (HandlerMethod) handler);
}
RequestMappingHandlerAdapter
中实现getLastModifiedInternal()
方法
/**
* 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
,不支持这个功能
8 handle(HttpServletRequest request, HttpServletResponse response, Object handler)
方法, 完成处理器方法的调用
该方法在
AbstractHandlerMethodAdapter
中实现
/**
* This implementation expects the handler to be an {@link HandlerMethod}.
*/
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
RequestMappingHandlerAdapter
中实现handleInternal()
方法,真正的方法调用逻辑
//一个响应头属性
protected static final String HEADER_CACHE_CONTROL = "Cache-Control";
//缓存的秒数,0表示不缓存
private int cacheSecondsForSessionAttributeHandlers = 0;
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
//检查请求,见8.1
checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required.
/**
* 会话同步
* synchronizeOnSession默认是false
*/
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
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 {
// No synchronization on session demanded at all...
//执行处理器方法,返回一个ModelAndView对象,见8.2
mav = invokeHandlerMethod(request, response, handlerMethod);
}
//不包含此响应头
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
//用户使用@SessionAttributes注解指定了属性
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
//为资源设置缓存时间
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
8.1 checkRequest(HttpServletRequest request)
方法,检查请求
//支持的请求方式,默认为null,表示支持所有
@Nullable
private Set<String> supportedMethods;
//session是否必须
private boolean requireSession = false;
/**
* Check the given request for supported methods and a required session, if any.
* @param request current HTTP request
* @throws ServletException if the request cannot be handled because a check failed
* @since 4.2
*/
protected final void checkRequest(HttpServletRequest request) throws ServletException {
// Check whether we should support the request method.
//当前请求方式
String method = request.getMethod();
/**
* 如果用户设置了supportedMethods属性,表明用户只想支持它指定的方法
* 比如,如果设置POST,GET,那么将只能处理POST,GET,其他方式的请求过来,就会抛出异常
*/
if (this.supportedMethods != null && !this.supportedMethods.contains(method)) {
throw new HttpRequestMethodNotSupportedException(method, this.supportedMethods);
}
// Check whether a session is required.
/**
* request.getSession(false)获取当前请求对应的session,
* false表示如果不存在不会创建新的session
*/
if (this.requireSession && request.getSession(false) == null) {
throw new HttpSessionRequiredException("Pre-existing session required but none found");
}
}
检查是否支持该请求方式
检查该请求是否必须存在
session
8.2 invokeHandlerMethod()
方法,执行处理器方法并返回一个ModelAndView
对象
//忽略重定向时的默认模型
private boolean ignoreDefaultModelOnRedirect = false;
/**
* Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
* if view resolution is required.
* @since 4.2
* @see #createInvocableHandlerMethod(HandlerMethod)
*/
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
//创建一个请求的适配器对象,见11
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
//获取数据绑定器工厂,见8.2.1
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
//获取模型工厂,见8.2.2
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
//扩展HandlerMethod,使其支持处理方法返回值,见8.2.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);
//创建模型视图容器,见15
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
/**
* RequestContextUtils.getInputFlashMap(request)方法会得到
* org.springframework.web.servlet.DispatcherServlet.INPUT_FLASH_MAP
* 中的域属性值,见15.1
* 而addAllAttributes()方法则会将上面得到的域属性值保存到模型中,见15.2
*/
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
//初始化模型,见14.3
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
//设置忽略重定向时的默认模型
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
/***************************异步web请求start*********************************/
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
/*****************************end***************************************/
/**
* 执行处理器方法和处理返回值
*
* 这里面又是一大段的逻辑,我们放到下一篇文章讲解
* 见springmvc--5--ServletInvocableHandlerMethod增强的处理器方法
*/
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
//获取ModelAndView对象,见8.2.4
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
//设置请求已处理完成,见11.1
webRequest.requestCompleted();
}
}
该方法是核心方法,它会完成请求处理的大部分工作
- 创建数据绑定器工厂(解析
@InitBinder
注解)- 创建模型工厂(解析
@ModelAttribute
注解)- 解析
@SessionAttributes
注解- 解析处理器方法参数
- 调用处理器方法
- 解析处理器方法的返回值
- 创建对应的
ModelAndView
对象
8.2.1 getDataBinderFactory(handlerMethod)
方法, 获取数据绑定器工厂
//缓存有@InitBinder注解的方法
private final Map<Class<?>, Set<Method>> initBinderCache = new ConcurrentHashMap<>(84);
//方法过滤器,过滤得到有@InitBinder注解的方法
public static final MethodFilter INIT_BINDER_METHODS = method ->
AnnotatedElementUtils.hasAnnotation(method, InitBinder.class);
/**
* 全局方法,实际上就是@ControllerAdvice注解类中有@InitBinder注解的方法,
* 见5.1,搜索容器中@ControllerAdvice注解类中有@InitBinder注解的方法
* 这种方式配置数据绑定器对所有的处理器方法生效
*/
private final Map<ControllerAdviceBean, Set<Method>> initBinderAdviceCache = new LinkedHashMap<>();
private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
//获取处理器方法所在类的clazz对象
Class<?> handlerType = handlerMethod.getBeanType();
//先从缓存中获取该类拥有@InitBinder注解的方法对象
Set<Method> methods = this.initBinderCache.get(handlerType);
//第一次,使用内省得到所有有@InitBinder注解的方法,并放入缓存中
if (methods == null) {
methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
this.initBinderCache.put(handlerType, methods);
}
List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();
// Global methods first
//首先是全局@InitBinder注解方法,遍历
this.initBinderAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
//判断@ControllerAdvice中的方法是否对该处理器方法生效,见9.3
if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
//获取ControllerAdviceBean对应的bean对象,见9.4
Object bean = controllerAdviceBean.resolveBean();
for (Method method : methodSet) {
//创建并初始化一个绑定器方法,见8.2.1.1
initBinderMethods.add(createInitBinderMethod(bean, method));
}
}
});
//其次是拥有@InitBinder注解的方法
for (Method method : methods) {
//获取处理器方法所在类的对象
Object bean = handlerMethod.getBean();
//创建并初始化一个绑定器方法,见8.2.1.1
initBinderMethods.add(createInitBinderMethod(bean, method));
}
//创建数据绑定工厂,见8.2.1.2
return createDataBinderFactory(initBinderMethods);
}
总结:
- 首先将全局
@InitBinder
注解方法和其所在类对象封装为InvocableHandlerMethod
- 接下来将当前处理器方法所在类对象和这个对象中的
@InitBinder
注解方法封装为InvocableHandlerMethod
- 根据这些
InvocableHandlerMethod
构建一个ServletRequestDataBinderFactory
工厂对象,用来创建ServletRequestDataBinder
数据绑定器对象
8.2.1.1 创建并初始化一个绑定器方法InvocableHandlerMethod
//默认的参数名发现器
private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
/**
* 默认为ConfigurableWebBindingInitializer
* springmvc配置文件中使用`<mvc:annotation-driven>`,会自动向该属性中填充一个对象
*/
@Nullable
private WebBindingInitializer webBindingInitializer;
private InvocableHandlerMethod createInitBinderMethod(Object bean, Method method) {
/**
* 可调用的处理器方法,扩展了HandlerMethod
* 添加了解析处理器方法参数和数据绑定器工厂的功能
*/
InvocableHandlerMethod binderMethod = new InvocableHandlerMethod(bean, method);
/**
* 设置绑定器的参数解析器
* 类型为HandlerMethodArgumentResolverComposite
*/
if (this.initBinderArgumentResolvers != null) {
binderMethod.setHandlerMethodArgumentResolvers(this.initBinderArgumentResolvers);
}
/**
* 设置数据绑定器工厂
* 类型为DefaultDataBinderFactory
*/
binderMethod.setDataBinderFactory(new DefaultDataBinderFactory(this.webBindingInitializer));
//设置参数名发现器
binderMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
return binderMethod;
}
8.2.1.2 创建数据绑定器工厂ServletRequestDataBinderFactory
/**
* Template method to create a new InitBinderDataBinderFactory instance.
* <p>The default implementation creates a ServletRequestDataBinderFactory.
* This can be overridden for custom ServletRequestDataBinder subclasses.
* @param binderMethods {@code @InitBinder} methods
* @return the InitBinderDataBinderFactory instance to use
* @throws Exception in case of invalid state or arguments
*/
protected InitBinderDataBinderFactory createDataBinderFactory(List<InvocableHandlerMethod> binderMethods)
throws Exception {
//具体的构造过程见12.1
return new ServletRequestDataBinderFactory(binderMethods, getWebBindingInitializer());
}
8.2.2 getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory)
方法,获取模型工厂
//缓存controller中没有@RequestMapping注解且有@ModelAttribute注解的方法列表
private final Map<Class<?>, Set<Method>> modelAttributeCache = new ConcurrentHashMap<>(64);
//过滤得到没有@RequestMapping注解且有@ModelAttribute注解的方法
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) {
//获取session属性处理器,见8.2.2.1
SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
//获取处理器方法所在类的clazz对象
Class<?> handlerType = handlerMethod.getBeanType();
//下面实际上就是得到有特定条件的方法,并缓存
Set<Method> methods = this.modelAttributeCache.get(handlerType);
if (methods == null) {
methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
//缓存
this.modelAttributeCache.put(handlerType, methods);
}
List<InvocableHandlerMethod> attrMethods = new ArrayList<>();
// Global methods first
//全局方法 @ControllerAdvice注解中的方法
this.modelAttributeAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
Object bean = controllerAdviceBean.resolveBean();
for (Method method : methodSet) {
//创建并初始化一个模型属性方法,见8.2.2.2
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
}
});
//当前处理器方法所在类中的方法
for (Method method : methods) {
Object bean = handlerMethod.getBean();
//创建并初始化一个模型属性方法,见8.2.2.2
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
//创建模型工厂,见14.2
return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
}
总结该方法的流程大致如下所示:
- 首先得到当前处理器方法所在类的会话属性处理器
SessionAttributesHandler
- 得到当前处理器方法所在类中没有
@RequestMapping
注解且有@ModelAttribute
注解的方法列表- 创建并初始化一个模型属性方法(再次封装方法列表中的每一个方法)
- 创建模型工厂对象
8.2.2.1 获取session属性处理器
/**
* session属性处理器缓存
* 保存了每一个controller中@SessionAttributes注解信息
*/
private final Map<Class<?>, SessionAttributesHandler> sessionAttributesHandlerCache = new ConcurrentHashMap<>(64);
//session属性操作策略
private SessionAttributeStore sessionAttributeStore = new DefaultSessionAttributeStore();
/**
* Return the {@link SessionAttributesHandler} instance for the given handler type
* (never {@code null}).
*/
private SessionAttributesHandler getSessionAttributesHandler(HandlerMethod handlerMethod) {
//是以controller的clazz对象为键存储的
return this.sessionAttributesHandlerCache.computeIfAbsent(
handlerMethod.getBeanType(),
type -> new SessionAttributesHandler(type, this.sessionAttributeStore));
}
8.2.2.2 创建并初始化一个模型属性方法InvocableHandlerMethod
private InvocableHandlerMethod createModelAttributeMethod(WebDataBinderFactory factory, Object bean, Method method) {
InvocableHandlerMethod attrMethod = new InvocableHandlerMethod(bean, method);
/**
* 设置模型属性方法的参数解析器
* 类型为HandlerMethodArgumentResolverComposite
*/
if (this.argumentResolvers != null) {
attrMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
/**
* 设置模型属性方法的参数名发现器
* 类型为DefaultParameterNameDiscoverer
*/
attrMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
/**
* 设置模型属性方法的数据绑定器工厂
* 类型为ServletRequestDataBinderFactory,见8.2.1.2
*/
attrMethod.setDataBinderFactory(factory);
return attrMethod;
}
8.2.3 创建ServletInvocableHandlerMethod
/**
* 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);
}
只是简单的将
HandlerMethod
处理器方法包装为一个web
环境的处理器方法ServletInvocableHandlerMethod
8.2.4 获取ModelAndView
对象
@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
/**
* 将模型中与用户通过@SessionAttributes注解声明的匹配的属性存储到session域中
* 有必要的话就将数据绑定属性保存到模型中,见14.6
*/
modelFactory.updateModel(webRequest, mavContainer);
//请求已被完全处理
if (mavContainer.isRequestHandled()) {
return null;
}
/**
* 获取当前使用的模型,见15.2
* 重定向使用重定向模型,否则使用默认模型
*/
ModelMap model = mavContainer.getModel();
//初始化一个ModelAndView对象,见16.1
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
//非视图名,即视图对象
if (!mavContainer.isViewReference()) {
//强转为视图对象并设置到ModelAndView中
mav.setView((View) mavContainer.getView());
}
//重定向
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request != null) {
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
}
return mav;
}
对于需要响应视图的请求,在此方法中会创建一个
ModelAndView
对象,同时保存模型数据,视图和响应状态对于不需要响应视图的请求,比如有
@ResponseBody
注解,就直接返回null
9 ControllerAdviceBean
类,封装@ControllerAdvice
注解的bean
下面是属性
public class ControllerAdviceBean implements Ordered {
/**
* Reference to the actual bean instance or a {@code String} representing
* the bean name.
*/
private final Object beanOrName;
private final boolean isSingleton;
/**
* Reference to the resolved bean instance, potentially lazily retrieved
* via the {@code BeanFactory}.
*/
@Nullable
private Object resolvedBean;
@Nullable
private final Class<?> beanType;
private final HandlerTypePredicate beanTypePredicate;
@Nullable
private final BeanFactory beanFactory;
@Nullable
private Integer order;
}
9.1 构造方法
/**
* Create a {@code ControllerAdviceBean} using the given bean name,
* {@code BeanFactory}, and {@link ControllerAdvice @ControllerAdvice}
* annotation.
* @param beanName the name of the bean
* @param beanFactory a {@code BeanFactory} to retrieve the bean type initially
* and later to resolve the actual bean
* @param controllerAdvice the {@code @ControllerAdvice} annotation for the
* bean, or {@code null} if not yet retrieved
* @since 5.2
*/
public ControllerAdviceBean(String beanName, BeanFactory beanFactory, @Nullable ControllerAdvice controllerAdvice) {
Assert.hasText(beanName, "Bean name must contain text");
Assert.notNull(beanFactory, "BeanFactory must not be null");
Assert.isTrue(beanFactory.containsBean(beanName), () -> "BeanFactory [" + beanFactory +
"] does not contain specified controller advice bean '" + beanName + "'");
//保存beanName
this.beanOrName = beanName;
//是否单例
this.isSingleton = beanFactory.isSingleton(beanName);
//bean的类型
this.beanType = getBeanType(beanName, beanFactory);
//bean对象类型断言
this.beanTypePredicate = (controllerAdvice != null ? createBeanTypePredicate(controllerAdvice) :
createBeanTypePredicate(this.beanType));
this.beanFactory = beanFactory;
}
//根据@ControllerAdvice注解信息构建一个HandlerTypePredicate
private static HandlerTypePredicate createBeanTypePredicate(@Nullable ControllerAdvice controllerAdvice) {
if (controllerAdvice != null) {
return HandlerTypePredicate.builder()
.basePackage(controllerAdvice.basePackages())
.basePackageClass(controllerAdvice.basePackageClasses())
.assignableType(controllerAdvice.assignableTypes())
.annotation(controllerAdvice.annotations())
.build();
}
return HandlerTypePredicate.forAnyHandlerType();
}
该构造方法解析得到
@ControllerAdvice
注解的bean
的名字和类型,并创建对应的HandlerTypePredicate
9.2 获取容器中有@ControllerAdvice
注解的bean
,并封装为ControllerAdviceBean
/**
* 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<>();
//获取容器中所有bean的名字并遍历
for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, Object.class)) {
//非代理类(没有使用作用域代理模式,@Scope注解指定)
if (!ScopedProxyUtils.isScopedTarget(name)) {
//得到bean中@ControllerAdvice注解信息
ControllerAdvice controllerAdvice = context.findAnnotationOnBean(name, ControllerAdvice.class);
//有@ControllerAdvice注解
if (controllerAdvice != null) {
// Use the @ControllerAdvice annotation found by findAnnotationOnBean()
// in order to avoid a subsequent lookup of the same annotation.
//构建ControllerAdviceBean,见9.1
adviceBeans.add(new ControllerAdviceBean(name, context, controllerAdvice));
}
}
}
//根据PriorityOrdered接口和Order接口排序
OrderComparator.sort(adviceBeans);
return adviceBeans;
}
该方法会获取到容器中所有
@ControllerAdvice
注解的bean
,并封装为ControllerAdviceBean
,然后根据PriorityOrdered
接口和Order
接口排序。
9.3 判断@ControllerAdvice
中的方法是否对该处理器方法所在类生效
/**
* Check whether the given bean type should be advised by this
* {@code ControllerAdviceBean}.
* @param beanType the type of the bean to check
* @since 4.0
* @see ControllerAdvice
*/
public boolean isApplicableToBeanType(@Nullable Class<?> beanType) {
//使用断言器,判断是否对该类生效,见10.2
return this.beanTypePredicate.test(beanType);
}
9.4 获取ControllerAdviceBean
对应的bean
对象
/**
* Get the bean instance for this {@code ControllerAdviceBean}, if necessary
* resolving the bean name through the {@link BeanFactory}.
* <p>As of Spring Framework 5.2, once the bean instance has been resolved it
* will be cached if it is a singleton, thereby avoiding repeated lookups in
* the {@code BeanFactory}.
*/
public Object resolveBean() {
if (this.resolvedBean == null) {
// this.beanOrName must be a String representing the bean name if
// this.resolvedBean is null.
//调用工厂的getBean()方法实例化
Object resolvedBean = obtainBeanFactory().getBean((String) this.beanOrName);
// Don't cache non-singletons (e.g., prototypes).
//非单例的就不缓存
if (!this.isSingleton) {
return resolvedBean;
}
//单例的缓存
this.resolvedBean = resolvedBean;
}
return this.resolvedBean;
}
10 HandlerTypePredicate
,断言器,用来匹配请求处理组件的类型
它是专门用来匹配@ControllerAdvice
注解指定类的一个断言器,它有一个嵌套类Builder
(建造者模式),方便创建一个HandlerTypePredicate
对象。
public final class HandlerTypePredicate implements Predicate<Class<?>> {
//包名
private final Set<String> basePackages;
//类型
private final List<Class<?>> assignableTypes;
//注解
private final List<Class<? extends Annotation>> annotations;
}
很明显,它的三个属性分别对应的
@ControllerAdvice
注解中的三个属性,这3
个属性用来指定@ControllerAdvice
注解类中方法的生效类,比如我们常用的@ExceptionHandler
(全局异常处理器),可以在ControllerAdvice
注解中指定能够捕获哪些类抛出的异常。并且它实现了
spring
定义的Predicate
断言接口,所以它的核心方法是test()
方法。
10.1 嵌套类Builder
(建造者模式)
public static class Builder {
//这三个属性分别对应的@ControllerAdvice注解中的三个
private final Set<String> basePackages = new LinkedHashSet<>();
private final List<Class<?>> assignableTypes = new ArrayList<>();
private final List<Class<? extends Annotation>> annotations = new ArrayList<>();
/**
* Match handlers declared under a base package, e.g. "org.example".
* @param packages one or more base package classes
*/
public Builder basePackage(String... packages) {
Arrays.stream(packages).filter(StringUtils::hasText).forEach(this::addBasePackage);
return this;
}
/**
* Type-safe alternative to {@link #forBasePackage(String...)} to specify a
* base package through a class.
* @param packageClasses one or more base package names
*/
public Builder basePackageClass(Class<?>... packageClasses) {
Arrays.stream(packageClasses).forEach(clazz -> addBasePackage(ClassUtils.getPackageName(clazz)));
return this;
}
private void addBasePackage(String basePackage) {
this.basePackages.add(basePackage.endsWith(".") ? basePackage : basePackage + ".");
}
/**
* Match handlers that are assignable to a given type.
* @param types one or more handler super types
*/
public Builder assignableType(Class<?>... types) {
this.assignableTypes.addAll(Arrays.asList(types));
return this;
}
/**
* Match types that are annotated with one of the given annotations.
* @param annotations one or more annotations to check for
*/
@SuppressWarnings("unchecked")
public final Builder annotation(Class<? extends Annotation>... annotations) {
this.annotations.addAll(Arrays.asList(annotations));
return this;
}
//最后这个build()方法中创建一个HandlerTypePredicate对象
public HandlerTypePredicate build() {
return new HandlerTypePredicate(this.basePackages, this.assignableTypes, this.annotations);
}
}
这就是
9.1
中使用的一种类似于Stream
的原理
10.2 test()
方法(Predicate
断言接口的唯一方法)
@Override
public boolean test(Class<?> controllerType) {
/**
* 未在@ControllerAdvice注解中指定生效类
* 说明对所有的类都生效
*/
if (!hasSelectors()) {
return true;
}
//部分类生效,进行三种方式的判断
else if (controllerType != null) {
//包名
for (String basePackage : this.basePackages) {
if (controllerType.getName().startsWith(basePackage)) {
return true;
}
}
//类型
for (Class<?> clazz : this.assignableTypes) {
if (ClassUtils.isAssignable(clazz, controllerType)) {
return true;
}
}
//注解
for (Class<? extends Annotation> annotationClass : this.annotations) {
if (AnnotationUtils.findAnnotation(controllerType, annotationClass) != null) {
return true;
}
}
}
return false;
}
//判断是否配置@ControllerAdvice注解属性
private boolean hasSelectors() {
return (!this.basePackages.isEmpty() || !this.assignableTypes.isEmpty() || !this.annotations.isEmpty());
}
11 ServletWebRequest
类,适配HttpServletRequest
下面是它的类图
我们再来看一下这3
个接口提供的方法
RequestAttributes
接口,它提供了对请求域属性操作的方法和获取Session
信息的相关方法。它的实现类ServletRequestAttributes
持有一个HttpServletRequest
、HttpServletResponse
、HttpSession
对象,这些接口方法的实现最终是借助原生的HttpServletRequest
方法实现的。WebRequest
接口,进一步扩展,它提供了很多方法,比如访问请求参数、请求头信息、请求描述、上下文路径,这些都可以通过HttpServletRequest
对象的原生方法得到NativeWebRequest
接口,提供获取本次请求响应对象的方法,后面两种方法可以获取指定请求响应对象的上级包装对象
下面是getNativeRequest()
方法内部实现
/**
* Return an appropriate request object of the specified type, if available,
* unwrapping the given request as far as necessary.
* @param request the servlet request to introspect
* @param requiredType the desired type of request object
* @return the matching request object, or {@code null} if none
* of that type is available
*/
@SuppressWarnings("unchecked")
@Nullable
public static <T> T getNativeRequest(ServletRequest request, @Nullable Class<T> requiredType) {
if (requiredType != null) {
if (requiredType.isInstance(request)) {
return (T) request;
}
else if (request instanceof ServletRequestWrapper) {
//递归,获取匹配的类型
return getNativeRequest(((ServletRequestWrapper) request).getRequest(), requiredType);
}
}
return null;
}
11.1 requestCompleted()
方法,设置请求已完成
该方法定义在
AbstractRequestAttributes
类中
//请求是否活跃,完成了就不活跃为false
private volatile boolean requestActive = true;
/**
* Signal that the request has been completed.
* <p>Executes all request destruction callbacks and updates the
* session attributes that have been accessed during request processing.
*/
public void requestCompleted() {
executeRequestDestructionCallbacks();
updateAccessedSessionAttributes();
//设置本次请求状态为已完成
this.requestActive = false;
}
12 WebDataBinderFactory
数据绑定器工厂
默认使用的是ServletRequestDataBinderFactory
,它的类图如下
我们看一下WebDataBinderFactory
接口
public interface WebDataBinderFactory {
/**
* Create a {@link WebDataBinder} for the given object.
* @param webRequest the current request
* @param target the object to create a data binder for,
* or {@code null} if creating a binder for a simple type
* @param objectName the name of the target object
* @return the created {@link WebDataBinder} instance, never null
* @throws Exception raised if the creation and initialization of the data binder fails
*/
WebDataBinder createBinder(NativeWebRequest webRequest, @Nullable Object target, String objectName)
throws Exception;
}
很明显,这是一个工厂接口,它用来创建一个
WebDataBinder
数据绑定器对象
12.1 ServletRequestDataBinderFactory
的构造方法
/**
* Create a new instance.
* @param binderMethods one or more {@code @InitBinder} methods
* @param initializer provides global data binder initialization
*/
public ServletRequestDataBinderFactory(@Nullable List<InvocableHandlerMethod> binderMethods,
@Nullable WebBindingInitializer initializer) {
super(binderMethods, initializer);
}
父类InitBinderDataBinderFactory
构造方法
//保存有@InitBinder注解的方法
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());
}
父类DefaultDataBinderFactory
构造方法
/**
* 绑定器的初始化器
* 使用这个初始化器来初始化绑定器
*/
@Nullable
private final WebBindingInitializer initializer;
/**
* Create a new {@code DefaultDataBinderFactory} instance.
* @param initializer for global data binder initialization
* (or {@code null} if none)
*/
public DefaultDataBinderFactory(@Nullable WebBindingInitializer initializer) {
this.initializer = initializer;
}
构造方法只是简单的将两个参数对象赋值给属性
12.2 createBinder(NativeWebRequest webRequest, @Nullable Object target, String objectName)
方法,创建属性对应的数据绑定器
/**
* Create a new {@link WebDataBinder} for the given target object and
* initialize it through a {@link WebBindingInitializer}.
* @throws Exception in case of invalid state or arguments
*/
@Override
@SuppressWarnings("deprecation")
public final WebDataBinder createBinder(
NativeWebRequest webRequest, @Nullable Object target, String objectName) throws Exception {
//创建数据绑定器实例,类型为ExtendedServletRequestDataBinder
WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest);
//初始化器初始化数据绑定器
if (this.initializer != null) {
this.initializer.initBinder(dataBinder, webRequest);
}
//初始化数据绑定器
initBinder(dataBinder, webRequest);
return dataBinder;
}
13 SessionAttributesHandler
会话属性处理器
管理
@SessionAttributes
注解声明的属性,实际过程委派给SessionAttributeStore
实例完成
下面是SessionAttributesHandler
类的属性
public class SessionAttributesHandler {
//session属性名
private final Set<String> attributeNames = new HashSet<>();
//session属性类型
private final Set<Class<?>> attributeTypes = new HashSet<>();
//已知的属性名
private final Set<String> knownAttributeNames = Collections.newSetFromMap(new ConcurrentHashMap<>(4));
//session属性存储策略
private final SessionAttributeStore sessionAttributeStore;
}
13.1 SessionAttributeStore
策略接口,定义操作session域属性
的规范
public interface SessionAttributeStore {
/**
* 存储属性要session域中
* @param request 当前请求
* @param attributeName 属性名
* @param attributeValue 属性值
*/
void storeAttribute(WebRequest request, String attributeName, Object attributeValue);
/**
* 获取session域attributeName对应属性值
*/
@Nullable
Object retrieveAttribute(WebRequest request, String attributeName);
/**
* 清除session域attributeName对应属性
*/
void cleanupAttribute(WebRequest request, String attributeName);
}
很明显,这个接口定义了操作
session域属性
的规范,策略模式主要是为了解耦。我们可以看一下springmvc
对该接口的默认实现
@Override
public void storeAttribute(WebRequest request, String attributeName, Object attributeValue) {
Assert.notNull(request, "WebRequest must not be null");
Assert.notNull(attributeName, "Attribute name must not be null");
Assert.notNull(attributeValue, "Attribute value must not be null");
String storeAttributeName = getAttributeNameInSession(request, attributeName);
//最终借助了原生request的setAttribute()方法,完成session属性存储
request.setAttribute(storeAttributeName, attributeValue, WebRequest.SCOPE_SESSION);
}
@Override
@Nullable
public Object retrieveAttribute(WebRequest request, String attributeName) {
Assert.notNull(request, "WebRequest must not be null");
Assert.notNull(attributeName, "Attribute name must not be null");
String storeAttributeName = getAttributeNameInSession(request, attributeName);
//最终借助了原生request的getAttribute()方法,完成session属性获取
return request.getAttribute(storeAttributeName, WebRequest.SCOPE_SESSION);
}
13.2 SessionAttributesHandler
的构造方法
/**
* 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;
//找到处理器方法所在类上@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);
}
- 构造方法会获取到对应类上的
@SessionAttributes
注解信息,然后将用户设置在注解上的信息保存到SessionAttributesHandler
会话属性处理器中。@SessionAttributes
注解是用来声明session
域中必须存在的属性。
13.3 retrieveAttributes(WebRequest request)
方法,得到session
中的已知属性的值
/**
* Retrieve "known" attributes from the session, i.e. attributes listed
* by name in {@code @SessionAttributes} or attributes previously stored
* in the model that matched by type.
* @param request the current request
* @return a map with handler session attributes, possibly empty
*/
public Map<String, Object> retrieveAttributes(WebRequest request) {
Map<String, Object> attributes = new HashMap<>();
for (String name : this.knownAttributeNames) {
Object value = this.sessionAttributeStore.retrieveAttribute(request, name);
if (value != null) {
attributes.put(name, value);
}
}
return attributes;
}
该方法会得到
@SessionAttributes
注解声明的session
域属性键值对。
13.4 isHandlerSessionAttribute(String attributeName, Class<?> attributeType)
方法,判断属性名称或属性类型是否与用户通过@SessionAttributes
注解指定的属性匹配
/**
* Whether the attribute name or type match the names and types specified
* via {@code @SessionAttributes} on the underlying controller.
* <p>Attributes successfully resolved through this method are "remembered"
* and subsequently used in {@link #retrieveAttributes(WebRequest)} and
* {@link #cleanupAttributes(WebRequest)}.
* @param attributeName the attribute name to check
* @param attributeType the type for the attribute
*/
public boolean isHandlerSessionAttribute(String attributeName, Class<?> attributeType) {
Assert.notNull(attributeName, "Attribute name must not be null");
//属性名或属性类型
if (this.attributeNames.contains(attributeName) || this.attributeTypes.contains(attributeType)) {
this.knownAttributeNames.add(attributeName);
return true;
}
else {
return false;
}
}
判断属性名称或属性类型是否与用户通过
@SessionAttributes
注解指定的属性匹配
13.5 cleanupAttributes(WebRequest request)
方法,清理会话属性
/**
* Remove "known" attributes from the session, i.e. attributes listed
* by name in {@code @SessionAttributes} or attributes previously stored
* in the model that matched by type.
* @param request the current request
*/
public void cleanupAttributes(WebRequest request) {
for (String attributeName : this.knownAttributeNames) {
this.sessionAttributeStore.cleanupAttribute(request, attributeName);
}
}
删除所有
@SessionAttributes
注解声明的session
域属性
13.6 storeAttributes(WebRequest request, Map<String, ?> attributes)
方法,将模型中与用户@SessionAttributes
注解声明的匹配的属性存储到session
域中
/**
* 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) -> {
//存储之前会过滤
if (value != null && isHandlerSessionAttribute(name, value.getClass())) {
this.sessionAttributeStore.storeAttribute(request, name, value);
}
});
}
只会将模型中与用户通过
@SessionAttributes
注解声明的匹配的属性存储到session
域中
14 ModelFactory
模型工厂
作用:
- 在控制器方法调用之前初始化
Model
,会将@ModelAttribute
注解指定属性保存到Model
中- 在控制器方法调用之后更新
Model
数据
首先看一下它的属性
public final class ModelFactory {
//模型方法
private final List<ModelMethod> modelMethods = new ArrayList<>();
//数据绑定器工厂
private final WebDataBinderFactory dataBinderFactory;
//session属性处理器
private final SessionAttributesHandler sessionAttributesHandler;
}
14.1 ModelMethod
模型方法
该类是
ModelFactory
的嵌套类
private static class ModelMethod {
//模型属性方法
private final InvocableHandlerMethod handlerMethod;
//@ModelAttribute注解标注的参数名
private final Set<String> dependencies = new HashSet<>();
public ModelMethod(InvocableHandlerMethod handlerMethod) {
this.handlerMethod = handlerMethod;
/**
* 遍历模型属性方法的参数
* 参数上有@ModelAttribute注解就将参数名保存到dependencies属性中
*/
for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
this.dependencies.add(getNameForParameter(parameter));
}
}
}
public InvocableHandlerMethod getHandlerMethod() {
return this.handlerMethod;
}
//判断视图模型容器中是否存在@ModelAttribute注解标注的参数名
public boolean checkDependencies(ModelAndViewContainer mavContainer) {
for (String name : this.dependencies) {
if (!mavContainer.containsAttribute(name)) {
return false;
}
}
return true;
}
@Override
public String toString() {
return this.handlerMethod.getMethod().toGenericString();
}
}
@ModelAttribute
既可以标注在方法
上又可以标注在方法参数
上该类会对在方法上标注
@ModelAttribute
注解的方法进行初步处理,它会将方法参数中有@ModelAttribute
注解的参数名解析并封装到属性中
14.2 构造方法
/**
* 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;
}
构造方法没做什么事情,只是简单的为
@ModelAttribute
注解方法创建一个ModelMethod
方法实例,另外给其他属性初始化一些值
14.3 initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod)
方法,初始化模型
/**
* Populate the model in the following order:
* <ol>
* <li>Retrieve "known" session attributes listed as {@code @SessionAttributes}.
* <li>Invoke {@code @ModelAttribute} methods
* <li>Find {@code @ModelAttribute} method arguments also listed as
* {@code @SessionAttributes} and ensure they're present in the model raising
* an exception if necessary.
* </ol>
* @param request the current request
* @param container a container with the model to be initialized
* @param handlerMethod the method for which the model is initialized
* @throws Exception may arise from {@code @ModelAttribute} methods
*/
public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod)
throws Exception {
//得到@SessionAttributes注解声明的所有session域属性键值对,见13.3
Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
/**
* 合并模型数据,见15.3
* 合并request和session域属性,之前在15.1中得到了特定的request域属性
* 现在在13.3中有得到了所有@SessionAttributes注解声明的所有session域属性
* 在此处进行两个的合并
*/
container.mergeAttributes(sessionAttributes);
//执行模型属性方法,见14.4
invokeModelAttributeMethods(request, container);
//获取模型方法所有中Session属性参数名
for (String name : findSessionAttributeArguments(handlerMethod)) {
//模型视图容器中不包含该参数名
if (!container.containsAttribute(name)) {
//取得参数名对应的session属性值
Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
if (value == null) {
throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);
}
//保存到模型视图容器中
container.addAttribute(name, value);
}
}
}
该方法主要作用是填充模型属性,按如下顺序
@SessionAttributes
注解声明的所有session域属性
- 模型属性方法的返回值
- 模型方法中所有匹配
@SessionAttributes
注解声明的属性的标注@ModelAttribute
注解参数
14.4 invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer container)
方法,执行模型属性方法
/**
* 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 {
//遍历执行所有@ModelAttribute注解方法(包括全局的)
while (!this.modelMethods.isEmpty()) {
//顺序获取模型方法
InvocableHandlerMethod modelMethod = getNextModelMethod(container).getHandlerMethod();
//得到方法上的@ModelAttribute注解信息
ModelAttribute ann = modelMethod.getMethodAnnotation(ModelAttribute.class);
Assert.state(ann != null, "No ModelAttribute annotation");
//模型视图容器中包含用户在的@ModelAttribute注解name属性指定的名字
if (container.containsAttribute(ann.name())) {
//@ModelAttribute注解上声明关闭数据绑定
if (!ann.binding()) {
//@ModelAttribute注解上声明的所有属性都禁用数据绑定
container.setBindingDisabled(ann.name());
}
continue;
}
//执行模型方法,见14.4.2
Object returnValue = modelMethod.invokeForRequest(request, container);
//模型方法有返回值
if (!modelMethod.isVoid()){
//获取方法返回值的名字,见14.4.3
String returnValueName = getNameForReturnValue(returnValue, modelMethod.getReturnType());
//该返回值禁用数据绑定
if (!ann.binding()) {
container.setBindingDisabled(returnValueName);
}
//返回值保存到模型中
if (!container.containsAttribute(returnValueName)) {
container.addAttribute(returnValueName, returnValue);
}
}
}
}
14.4.1 顺序获取模型方法
private ModelMethod getNextModelMethod(ModelAndViewContainer container) {
for (ModelMethod modelMethod : this.modelMethods) {
/**
* checkDependencies(container)方法见14.1
* 会把模型方法参数上有@ModelAttribute注解的参数和视图模型容器中的属性一一比较
* 只要有一个标注了@ModelAttribute注解的参数在视图模型容器中找不到同名属性,就返回false
* 说明该模型方法还未执行过,需要执行.
* 否则返回true,从列表中移除该标注了@ModelAttribute注解的方法
*/
if (modelMethod.checkDependencies(container)) {
this.modelMethods.remove(modelMethod);
return modelMethod;
}
}
//按顺序获取模型方法
ModelMethod modelMethod = this.modelMethods.get(0);
this.modelMethods.remove(modelMethod);
return modelMethod;
}
它会先检查所有模型方法,如果发现这样一个方法(所有标注了
@ModelAttribute
注解的参数都能在视图模型容器中找同名属性),就删掉方法缓存,然后返回该方法对象。否则按照顺序获取模型工厂中的模型方法。
14.4.2 执行模型方法
/**
* Invoke the method after resolving its argument values in the context of the given request.
* <p>Argument values are commonly resolved through
* {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}.
* The {@code providedArgs} parameter however may supply argument values to be used directly,
* i.e. without argument resolution. Examples of provided argument values include a
* {@link WebDataBinder}, a {@link SessionStatus}, or a thrown exception instance.
* Provided argument values are checked before argument resolvers.
* <p>Delegates to {@link #getMethodArgumentValues} and calls {@link #doInvoke} with the
* resolved arguments.
* @param request the current request
* @param mavContainer the ModelAndViewContainer for this request
* @param providedArgs "given" arguments matched by type, not resolved
* @return the raw value returned by the invoked method
* @throws Exception raised if no suitable argument resolver can be found,
* or if the method raised an exception
* @see #getMethodArgumentValues
* @see #doInvoke
*/
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
return doInvoke(args);
}
该方法的源码我们放到下一篇文章再看(
springmvc--5--ServletInvocableHandlerMethod增强的处理器方法
)我先来简单说一下过程:首先一个一个的解析方法参数,解析时遍历我们在
5.2
中设置的默认的参数解析器,找到第一个能解析方法参数的解析器,进行解析方法参数,得到参数值。所有参数解析成功后使用反射invoke()
方法完成方法调用。
14.4.3 获取方法返回值的名字
/**
* Derive the model attribute name for the given return value. Results will be
* based on:
* <ol>
* <li>the method {@code ModelAttribute} annotation value
* <li>the declared return type if it is more specific than {@code Object}
* <li>the actual return value type
* </ol>
* @param returnValue the value returned from a method invocation
* @param returnType a descriptor for the return type of the method
* @return the derived name (never {@code null} or empty String)
*/
public static String getNameForReturnValue(@Nullable Object returnValue, MethodParameter returnType) {
//获取模型方法上的@ModelAttribute注解信息
ModelAttribute ann = returnType.getMethodAnnotation(ModelAttribute.class);
/**
* 有@ModelAttribute注解且指定了返回值的存储名
* 以@ModelAttribute注解的value属性为key,保存模型方法的返回到模型中
*/
if (ann != null && StringUtils.hasText(ann.value())) {
return ann.value();
}
//使用默认的名字将返回值保存到模型中
else {
Method method = returnType.getMethod();
Assert.state(method != null, "No handler method");
Class<?> containingClass = returnType.getContainingClass();
Class<?> resolvedType = GenericTypeResolver.resolveReturnType(method, containingClass);
return Conventions.getVariableNameForReturnType(method, resolvedType, returnValue);
}
}
14.5 findSessionAttributeArguments(HandlerMethod handlerMethod)
方法,获取模型方法所有中Session属性
参数名
/**
* 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()) {
//参数上有@ModelAttribute注解
if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
//参数名
String name = getNameForParameter(parameter);
//参数类型
Class<?> paramType = parameter.getParameterType();
//参数是@SessionAttributes注解指定的属性
if (this.sessionAttributesHandler.isHandlerSessionAttribute(name, paramType)) {
result.add(name);
}
}
}
return result;
}j
该方法会得到模型方法中满足下面条件的参数名
- 参数上有
@ModelAttribute
注解- 参数名或类型匹配用户
@SessionAttributes
注解指定的参数名或类型
14.6 updateModel(NativeWebRequest request, ModelAndViewContainer container)
方法, 将@SessionAttributes
注解声明的属性保存到session
域
/**
* Promote model attributes listed as {@code @SessionAttributes} to the session.
* Add {@link BindingResult} attributes where necessary.
* @param request the current request
* @param container contains the model to update
* @throws Exception if creating BindingResult attributes fails
*/
public void updateModel(NativeWebRequest request, ModelAndViewContainer container) throws Exception {
//得到默认模型,见15.4
ModelMap defaultModel = container.getDefaultModel();
//当前会话已完成,见15.5
if (container.getSessionStatus().isComplete()){
//清理@SessionAttributes注解声明的会话属性,见13.5
this.sessionAttributesHandler.cleanupAttributes(request);
}
//当前会话未完成
else {
//只会将模型中与用户通过@SessionAttributes注解声明的匹配的属性存储到session域中,见13.6
this.sessionAttributesHandler.storeAttributes(request, defaultModel);
}
//请求未被完全处理,且使用默认模型(即非重定向)
if (!container.isRequestHandled() && container.getModel() == defaultModel) {
//将需要数据绑定的属性添加到模型中,见14.7
updateBindingResult(request, defaultModel);
}
}
- 该方法会判断会话状态,如果会话已完成,就清理
@SessionAttributes
注解声明的会话属性,否则将将模型中与用户通过@SessionAttributes
注解声明的匹配的属性存储到session
域中- 如果非重定向且请求为被完全处理,就将数据绑定的属性添加到模型中
14.7 updateBindingResult(NativeWebRequest request, ModelMap model)
方法,将数据绑定的属性添加到模型中
/**
* 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);
//该属性需要进行数据绑定
if (value != null && isBindingCandidate(name, value)) {
//MODEL_KEY_PREFIX="org.springframework.validation.BindingResult."
String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + name;
//模型中不包含该数据绑定属性
if (!model.containsAttribute(bindingResultKey)) {
//使用数据绑定器工厂创建该属性的数据绑定器,见12.2
WebDataBinder dataBinder = this.dataBinderFactory.createBinder(request, value, name);
//保存属性
model.put(bindingResultKey, dataBinder.getBindingResult());
}
}
}
}
/**
* Whether the given attribute requires a {@link BindingResult} in the model.
*/
private boolean isBindingCandidate(String attributeName, Object value) {
/**
* MODEL_KEY_PREFIX="org.springframework.validation.BindingResult."
* 以这个开头的属性不需要数据绑定
*/
if (attributeName.startsWith(BindingResult.MODEL_KEY_PREFIX)) {
return false;
}
//用户通过@SessionAttributes注解指定的属性需要进行数据绑定
if (this.sessionAttributesHandler.isHandlerSessionAttribute(attributeName, value.getClass())) {
return true;
}
//非集合,非数组,非Map,非简单类型的属性都需要进行数据绑定
return (!value.getClass().isArray() && !(value instanceof Collection) &&
!(value instanceof Map) && !BeanUtils.isSimpleValueType(value.getClass()));
}
下面是三种过滤条件,表明需要进行数据绑定的属性
- 以
org.springframework.validation.BindingResult.
开头的属性不需要数据绑定- 用户通过
@SessionAttributes
注解指定的属性需要进行数据绑定- 非
集合
,非数组
,非Map
,非简单类型
的属性都需要进行数据绑定
15 ModelAndViewContainer
模型视图容器
我们先来看一下它的属性
public class ModelAndViewContainer {
//忽略重定向时的默认模型
private boolean ignoreDefaultModelOnRedirect = false;
//视图名
@Nullable
private Object view;
//默认模型
private final ModelMap defaultModel = new BindingAwareModelMap();
//重定向模型
@Nullable
private ModelMap redirectModel;
private boolean redirectModelScenario = false;
//响应状态码
@Nullable
private HttpStatus status;
private final Set<String> noBinding = new HashSet<>(4);
//禁用绑定的属性
private final Set<String> bindingDisabled = new HashSet<>(4);
//会话状态
private final SessionStatus sessionStatus = new SimpleSessionStatus();
//请求是否被完全处理
private boolean requestHandled = false;
}
15.1 数据来源
/**
* Return read-only "input" flash attributes from request before redirect.
* @param request current request
* @return a read-only Map, or {@code null} if not found
* @see FlashMap
*/
@SuppressWarnings("unchecked")
@Nullable
public static Map<String, ?> getInputFlashMap(HttpServletRequest request) {
return (Map<String, ?>) request.getAttribute(DispatcherServlet.INPUT_FLASH_MAP_ATTRIBUTE);
}
/**
* Name of request attribute that holds a read-only {@code Map<String,?>}
* with "input" flash attributes saved by a previous request, if any.
* @see org.springframework.web.servlet.support.RequestContextUtils#getInputFlashMap(HttpServletRequest)
*/
//org.springframework.web.servlet.DispatcherServlet.INPUT_FLASH_MAP
public static final String INPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".INPUT_FLASH_MAP";
该方法会得到
request
中所有域属性名为org.springframework.web.servlet.DispatcherServlet.INPUT_FLASH_MAP
的值
15.2 addAllAttributes(@Nullable Map<String, ?> attributes)
方法,添加数据到视图模型容器中
/**
* Copy all attributes to the underlying model.
* A shortcut for {@code getModel().addAllAttributes(Map)}.
*/
public ModelAndViewContainer addAllAttributes(@Nullable Map<String, ?> attributes) {
//由getModel()方法判断将数据添加到defaultModel还是redirectModel中
getModel().addAllAttributes(attributes);
return this;
}
/**
* Return the model to use -- either the "default" or the "redirect" model.
* The default model is used if {@code redirectModelScenario=false} or
* there is no redirect model (i.e. RedirectAttributes was not declared as
* a method argument) and {@code ignoreDefaultModelOnRedirect=false}.
*/
public ModelMap getModel() {
//非重定向
if (useDefaultModel()) {
return this.defaultModel;
}
//重定向
else {
if (this.redirectModel == null) {
this.redirectModel = new ModelMap();
}
return this.redirectModel;
}
}
/**
* Whether to use the default model or the redirect model.
*/
private boolean useDefaultModel() {
/**
* 只有非重定向场景或
* 重定向模型为null且ignoreDefaultModelOnRedirect属性为false(即忽略重定向)
*/
return (!this.redirectModelScenario || (this.redirectModel == null && !this.ignoreDefaultModelOnRedirect));
}
数据会添加到重定向模型或默认模型中
15.3 mergeAttributes(@Nullable Map<String, ?> attributes)
方法,合并模型数据
/**
* Copy attributes in the supplied {@code Map} with existing objects of
* the same name taking precedence (i.e. not getting replaced).
* A shortcut for {@code getModel().mergeAttributes(Map<String, ?>)}.
*/
public ModelAndViewContainer mergeAttributes(@Nullable Map<String, ?> attributes) {
/**
* 由getModel()方法判断将数据添加到defaultModel还是redirectModel中
* mergeAttributes(attributes)方法会合并两个map集合的值
* 注意:同key的直接跳过,不会覆盖
*/
getModel().mergeAttributes(attributes);
return this;
}
合并两个map
集合
/**
* Copy all attributes in the supplied {@code Map} into this {@code Map},
* with existing objects of the same name taking precedence (i.e. not getting
* replaced).
*/
public ModelMap mergeAttributes(@Nullable Map<String, ?> attributes) {
if (attributes != null) {
attributes.forEach((key, value) -> {
if (!containsKey(key)) {
put(key, value);
}
});
}
return this;
}
15.4 getDefaultModel()
方法,获取默认模型
/**
* Return the "default" model created at instantiation.
* <p>In general it is recommended to use {@link #getModel()} instead which
* returns either the "default" model (template rendering) or the "redirect"
* model (redirect URL preparation). Use of this method may be needed for
* advanced cases when access to the "default" model is needed regardless,
* e.g. to save model attributes specified via {@code @SessionAttributes}.
* @return the default model (never {@code null})
* @since 4.1.4
*/
public ModelMap getDefaultModel() {
//默认模型
return this.defaultModel;
}
15.5 getSessionStatus()
方法,获取会话状态
/**
* Return the {@link SessionStatus} instance to use that can be used to
* signal that session processing is complete.
*/
public SessionStatus getSessionStatus() {
return this.sessionStatus;
}
默认的
SessionStatus
的类型为SimpleSessionStatus
我们先来看一下SessionStatus
接口方法
public interface SessionStatus {
/**
* 设置当前会话已完成
*/
void setComplete();
/**
* 当前会话是否完成
*/
boolean isComplete();
}
它的简单实现SimpleSessionStatus
public class SimpleSessionStatus implements SessionStatus {
//默认是false,表示会话未完成
private boolean complete = false;
@Override
public void setComplete() {
this.complete = true;
}
@Override
public boolean isComplete() {
return this.complete;
}
}
15.6 getViewName()
方法,获取视图名
/**
* Return the view name to be resolved by the DispatcherServlet via a
* ViewResolver, or {@code null} if a View object is set.
*/
@Nullable
public String getViewName() {
return (this.view instanceof String ? (String) this.view : null);
}
模型视图容器中保存的可能是视图名也可能是视图对象
15.7 isViewReference()
方法,判断是不是视图名
/**
* Whether the view is a view reference specified via a name to be
* resolved by the DispatcherServlet via a ViewResolver.
*/
public boolean isViewReference() {
return (this.view instanceof String);
}
16 ModelAndView
模型视图
持有模型和视图,以便控制器可以在单个返回值中同时返回视图和模型
下面是它的属性
public class ModelAndView {
//视图对象或视图名
@Nullable
private Object view;
//模型
@Nullable
private ModelMap model;
//响应状态
@Nullable
private HttpStatus status;
//是否通过clear()方法清除了该实例
private boolean cleared = false;
}
16.1 构造方法
/**
* Create a new ModelAndView given a view name, model, and HTTP status.
* @param viewName name of the View to render, to be resolved
* by the DispatcherServlet's ViewResolver
* @param model a Map of model names (Strings) to model objects
* (Objects). Model entries may not be {@code null}, but the
* model Map may be {@code null} if there is no model data.
* @param status an HTTP status code to use for the response
* (to be set just prior to View rendering)
* @since 4.3
*/
public ModelAndView(@Nullable String viewName, @Nullable Map<String, ?> model, @Nullable HttpStatus status) {
this.view = viewName;
if (model != null) {
getModelMap().addAllAttributes(model);
}
this.status = status;
}
/**
* Return the underlying {@code ModelMap} instance (never {@code null}).
*/
public ModelMap getModelMap() {
if (this.model == null) {
this.model = new ModelMap();
}
return this.model;
}
根据模型、视图名、响应状态构建一个模型视图对象