《看透springMvc源代码分析与实践》学习笔记
SpringMVC 版本 4.1.5.RELEASE
HandlerAdapter在执行invokeHandleMethod
使用了大量的组件,在这里对这些组件进行集中分析。
ModelAndViewContainer
ModelAndViewContainer
承担着整个请求过程中数据的传递工作。它除了保存Model和View外还有一些别的功能。
//org.springframework.web.method.support.ModelAndViewContainer
public class ModelAndViewContainer {
//视图:可以是实际视图也,可是String类型的逻辑视图
private Object view;
//默认使用的Model
private final ModelMap defaultModel = new BindingAwareModelMap();
//redirect类型的Model
private ModelMap redirectModel;
//如果为true,则在处理器返回redirect视图时,不使用defaultModel。在`RequestMappingHandlerAdapter`中设置。
private boolean ignoreDefaultModelOnRedirect = false;
//处理器返回redirect视图的标志
private boolean redirectModelScenario = false;
//用于设置 SessionAttribute是否已经使用完成标志,如果为true,在modelFactory方
private final SessionStatus sessionStatus = new SimpleSessionStatus();
//请求是否已经完成标志:如果是,则不往下处理,直接返回。
private boolean requestHandled = false;
}
defaultModel和
redirectModel
defaultModel
:默认使用model,redirectModel
用于传递redirect时的参数。
ModelAndViewContainer的getModel
方法会根据条件返回这两个model中的一个:
//org.springframework.web.method.support.ModelAndViewContainer
public ModelMap getModel() {
if (useDefaultModel()) {
return this.defaultModel;
} else {
return (this.redirectModel != null) ? this.redirectModel : new ModelMap();
}
}
private boolean useDefaultModel() {
return (!this.redirectModelScenario || (this.redirectModel == null && !this.ignoreDefaultModelOnRedirect));
}
返回true
:即使用defaultModel的情况- redirectModelScenario = false 即不是redirect视图时。
- ignoreDefaultModelOnRedirect = false 且 redirectModel == null
返回false
:使用redirectModel的情况- redirectModelScenario = true && redirectModel != null
- redirectModelScenario = true && ignoreDefaultModelOnRedirect = true
redirectModelScenario
redirectModelScenario
:判断处理器返回的是不是redirect视图,它是在returnValueHandlers
中设置的,如果returnValueHandlers如果判断到是redirect视图会将
redirectModelScenario设为true.
returnValueHandlers处理前,ModelAndViewContainer的getModel返回的一定是defaultModel.
处理后,才有可能
是redirectModel
sessionStatus
sessionStatus
:用来标识SessionAttribute是否已经使用完成。
如果为true,在modelFactory方法updateModel
将SessionAttribute清空。
如果为false,则将当前Model对应的属性设置到SessionAttribute中。
SessionAttributesHandler 和 SessionAttributeStore
SessionAttributesHandler
用来处理@SessionAttributes
注释的参数,它仅关注handler可以处理哪些参数,某个参数在当前SessionAttributes等操作。- 具体的存储工作交给
SessionAttributeStore
来做,默认是用DefaultSessionAttributeStore
。 - 而保存数据的容器,
默认使用的是Session
.
这部分代码比较简单,不过多解析。
ModelFactory
ModelFactory
是用来维护Model的,它有两个功能:
- 初始化Model
- 处理器执行后,将Model中相应的参数更新到SessionAttributes中。
创建ModelFactory
在RequestMappingHandlerAdapter.invokeHandleMethod
中执行getModelFactory
,
最终return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
创建并返回ModelFactory.
- 其中:
attrMethods
: 当前HandlerMethod所在类中局部@ModelAttribute
+@ControllerAdvice中全局@ModelAttribute
//org.springframework.web.method.annotation.ModelFactory
public ModelFactory(List<InvocableHandlerMethod> invocableMethods, WebDataBinderFactory dataBinderFactory,
SessionAttributesHandler sessionAttributesHandler) {
if (invocableMethods != null) {
for (InvocableHandlerMethod method : invocableMethods) {
this.modelMethods.add(new ModelMethod(method));
}
}
this.dataBinderFactory = dataBinderFactory;
this.sessionAttributesHandler = sessionAttributesHandler;
}
ModelFactory.$ModelMethod
//org.springframework.web.method.annotation.ModelFactory.$ModelMethod
private ModelMethod(InvocableHandlerMethod handlerMethod) {
this.handlerMethod = handlerMethod;
for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
//dependencies:表示当前@ModelAttribute注释类方法,是否有@ModelAttribute的入参。
if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
this.dependencies.add(getNameForParameter(parameter));
}
}
}
//检查mavContainer是否包含所有的dependencies属性
public boolean checkDependencies(ModelAndViewContainer mavContainer) {
for (String name : this.dependencies) {
if (!mavContainer.containsAttribute(name)) {
return false;
}
}
return true;
}
初始化Model
初始化Model主要是在处理器执行前,将相应的数据设置到Model中,调用initModel
方法完成。
//org.springframework.web.method.annotation.ModelFactory
public final class ModelFactory {
public void initModel(NativeWebRequest request, ModelAndViewContainer mavContainer, HandlerMethod handlerMethod){
//从sessionAttributesHandler中取出保存的session参数,并merge到mavContainer的Model中
Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
mavContainer.mergeAttributes(sessionAttributes);
//执行@ModelAttribute注释了的方法,并将结果保存到mavContainer的Model中
invokeModelAttributeMethods(request, mavContainer);
/**
* 找到:`入参`注释了@ModelAttribute且也在@SessionAttributes 注释中
* 设置为`sessionAttributesHandler`中的值(@SessionAttributes优先级高于@SessionAttributes)
*/
for (String name : findSessionAttributeArguments(handlerMethod)) {
if (!mavContainer.containsAttribute(name)) {
Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
if (value == null) {
throw new HttpSessionRequiredException("Expected session attribute '" + name + "'");
}
mavContainer.addAttribute(name, value);
}
}
}
}
invokeModelAttributeMethods
//org.springframework.web.method.annotation.ModelFactory
private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer mavContainer)
throws Exception {
while (!this.modelMethods.isEmpty()) {
//遍历@ModelAttribute方法
InvocableHandlerMethod attrMethod = getNextModelMethod(mavContainer).getHandlerMethod();
//获取@ModelAttribute中配置的value为参数名
String modelName = attrMethod.getMethodAnnotation(ModelAttribute.class).value();
if (mavContainer.containsAttribute(modelName)) {
continue;
}
//反射执行方法,获取返回值
Object returnValue = attrMethod.invokeForRequest(request, mavContainer);
//如果方法返回值不是Void
if (!attrMethod.isVoid()){
/*
* 如果所在方法设置了value,则使用其作为returnValueName
* 如果没有设置则按照如下策略:
* String -> string
* UFOModel -> UFOModel
* List<Double>,Set<Doule>,Double[] -> doubleList
*/
String returnValueName = getNameForReturnValue(returnValue, attrMethod.getReturnType());
if (!mavContainer.containsAttribute(returnValueName)) {
mavContainer.addAttribute(returnValueName, returnValue);
}
}
/**
* 如果方法返回值是Void---则不做任何处理
* 因为在invokeForRequest处理过程中,已经将属性设置到Model中了(Model作为@ModelAttribute注释方法的入参)。
*/
}
}
自此初始化工作就完成了。可以看出来Model中参数的优先级如下:
FlashMap > @SessionAttributes > @ModelAttribute的方法设置的参数 > @ModelAttribute注释的入参且该入参在@SessionAttributes中。
其中 @ModelAttribute的方法设置的参数: @ControllerAdvice全局 > 局部
更新Model
更新Model通过updateModel
方法完成的.
//org.springframework.web.method.annotation.ModelFactory
public void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer) throws Exception {
ModelMap defaultModel = mavContainer.getDefaultModel();
if (mavContainer.getSessionStatus().isComplete()){
//将SessionAttributes清空
this.sessionAttributesHandler.cleanupAttributes(request);
}else {
//将defaultModel中对应的参数设置到@SessionAttributes中
this.sessionAttributesHandler.storeAttributes(request, defaultModel);
}
if (!mavContainer.isRequestHandled() && mavContainer.getModel() == defaultModel) {
//????给model中的参数设置BindResult....
updateBindingResult(request, defaultModel);
}
}
ServletInvocableHandlerMethod
HandlerMethod
HandlerMethod
用于封装Handler和其中具体处理请求的method,分别对应的属性中的bean和method
.
HandlerMethod的属性都是final的
,创建之后就不允许修改了。
//org.springframework.web.method.HandlerMethod
public class HandlerMethod {
//Handler本身,如果传入的bean是String类型,则需要beanFactory找到对应的bean.
private final Object bean;
private final Method method;
private final BeanFactory beanFactory;
//bridgedMethod
private final Method bridgedMethod;
private final MethodParameter[] parameters;
}
MethodParameter
//org.springframework.core.MethodParameter
public class MethodParameter {
//参数所在方法
private final Method method;
//参数的构造方法
private final Constructor<?> constructor;
//参数的序号:从0开始
private final int parameterIndex;
//嵌套级别。复合参数会用到:比如List<String> params ,则params的嵌套级别是1, String 的嵌套级别是2
private int nestingLevel = 1;
//保存每层嵌套参数的序号;
Map<Integer, Integer> typeIndexesPerLevel;
//容器的类型: 即参数所属方法所在的类
private volatile Class<?> containingClass;
//参数的类型
private volatile Class<?> parameterType;
//
private volatile Type genericParameterType;
//参数的注释
private volatile Annotation[] parameterAnnotations;
//参数名称查找器:: 由于反射后无法获取参数名称,故提供此工具
private volatile ParameterNameDiscoverer parameterNameDiscoverer;
//参数名称
private volatile String parameterName;
}
InvocableHandlerMethod
InvocableHandlerMethod
继承自HandlerMethod,在父类的基础上增加了调用的功能,即它可以直接调用内部属性method对应的方法(实际上是bridgedMethod)
.
InvocableHandlerMethod增加了三个属性:
//org.springframework.web.method.support.InvocableHandlerMethod
public class InvocableHandlerMethod extends HandlerMethod {
//用于创建WebDataBinder,用来帮助argumentResolvers解析参数
private WebDataBinderFactory dataBinderFactory;
//用来解析参数
private HandlerMethodArgumentResolverComposite argumentResolvers = new HandlerMethodArgumentResolverComposite();
//用来获取参数名
private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
}
DefaultParameterNameDiscoverer–反射获取参数列表
StandardReflectionParameterNameDiscoverer
:需要满足如下两个条件- a. jdk >=1.8
- b. javac增加参数-parameters: javac -parameters
static void jdk18DisCover(Method method) throws NoSuchMethodException { Parameter[] parameters = method.getParameters(); Arrays.stream(parameters).forEach(param -> { System.out.println(param.getName()); }); }
LocalVariableTableParameterNameDiscoverer
static void jdkLowerDisCover(Method method) throws NoSuchMethodException {
LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
String[] paramNames = u.getParameterNames(method);
Arrays.stream(paramNames).forEach(name -> {
System.out.println(name);
});
}
invokeForRequest
InvocableHandlerMethod调用Method的方法是:invokeForRequest
//org.springframework.web.method.support.InvocableHandlerMethod
public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,Object... providedArgs) {
//参数绑定
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
//log略.....
//反射执行方法
Object returnValue = doInvoke(args);
return returnValue;
}
getMethodArgumentValues
getMethodArgumentValues:用来进行参数绑定
private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,Object... providedArgs) {
//获取方法参数
MethodParameter[] parameters = getMethodParameters();
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
//给每一个MethodParameter 设置 参数名称解析器
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
GenericTypeResolver.resolveParameterType(parameter, getBean().getClass());
//如果提供的参数列表 包含parameter,则跳出本次循环;而调用本方法时:providedArgs= null,故执行不到下面的逻辑
args[i] = resolveProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
//使用argumentResolvers解析参数
if (this.argumentResolvers.supportsParameter(parameter)) {
//argumentResolvers解析参数,在之后会讲到
args[i] = this.argumentResolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
continue;
}
//如果没解析出参数,则抛出异常
if (args[i] == null) {
throw new IllegalStateException("No suitable resolver for argument", i);
}
}
return args;
}
doInvoke
doInvoke
实际调用的bridgedMethod
protected Object doInvoke(Object... args) throws Exception {
//略 try-catch
ReflectionUtils.makeAccessible(getBridgedMethod());
return getBridgedMethod().invoke(getBean(), args);
}
ServletInvocableHandlerMethod
ServletInvocableHandlerMethod
集成自InvocableHandlerMethod
,在父类的基础上增加了三个功能:
- 对
@HttpStatus
的支持:该注释用于处理器方法或返回值上。 - 对返回值的处理: 它是由
returnValueHandlers
属性完成的。 - 异步方法处理: 使用
ConcurrentResultHandlerMethod和ConcurrentResultMethodParameter
处理,后续会讲到。
invokeAndHandle
处理请求使用invokeAndHandle
方法:
public void invokeAndHandle(ServletWebRequest webRequest,
ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
//设置ResponseStatus(
setResponseStatus(webRequest);
//根据条件判断,直接返回。
if (returnValue == null) {
if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
} else if (StringUtils.hasText(this.responseReason)) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
//处理返回值
this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
HandlerMethodArgumentResolver
HandlerMethodArgumentResolver
用来为处理器解析参数,主要用于在InvocableHandlerMethod
中。
接口定义
HandlerMethodArgumentResolver 接口很简单,只有两个方法:
//org.springframework.web.method.support.HandlerMethodArgumentResolver
public interface HandlerMethodArgumentResolver {
//判断是否可以解析传入的参数
boolean supportsParameter(MethodParameter parameter);
//用于实际解析参数
Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;
}
实现类
HandlerMethodArgumentResolver实现类一般由两种定义方式:
XXXMethodArgumentResolver
: 表示一种参数解析器XXXMethodProcessor
: 不仅可以处理参数,还可以处理返回值
实现类分析
RequestMappingHandlerAdapter
默认使用了太多种参数解析器,这里分析如下两种:
ModelMethodProcessor
//org.springframework.web.method.annotation.
public class ModelMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return Model.class.isAssignableFrom(parameter.getParameterType());
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
return mavContainer.getModel();
}
}
PathVariableMethodArgumentResolver
PathVariableMethodArgumentResolver
用于解析url路径中的值,它的实现比较复杂,它集成自AbstractNamedValueMethodArgumentResolver
。
PathVariableMethodArgumentResolver
public class PathVariableMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver
implements UriComponentsContributor {
@Override
public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
Class<?> paramType = parameter.getParameterType();
//NamedValueInfo子类:name,required,defaultValue
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
Object arg = resolveName(namedValueInfo.name, parameter, webRequest);
if (arg == null) {
if (namedValueInfo.defaultValue != null) {
//获取默认值
arg = resolveDefaultValue(namedValueInfo.defaultValue);
}else if (namedValueInfo.required && !parameter.getParameterType().getName().equals("java.util.Optional")) {
//模板方法,由子类实现。 PathVariableMethodArgumentResolver中实现此方法:抛出异常;
handleMissingValue(namedValueInfo.name, parameter);
}
arg = handleNullValue(namedValueInfo.name, arg, paramType);
}else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
arg = resolveDefaultValue(namedValueInfo.defaultValue);
}
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
arg = binder.convertIfNecessary(arg, paramType, parameter);
}
handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
return arg;
}
protected static class NamedValueInfo {
private final String name;
private final boolean required;
private final String defaultValue;
public NamedValueInfo(String name, boolean required, String defaultValue) {
this.name = name;
this.required = required;
this.defaultValue = defaultValue;
}
}
}
PathVariableMethodArgumentResolver
public class PathVariableMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver
implements UriComponentsContributor {
@Override
public boolean supportsParameter(MethodParameter parameter) {
if (!parameter.hasParameterAnnotation(PathVariable.class)) {
return false;
}
if (Map.class.isAssignableFrom(parameter.getParameterType())) {
String paramName = parameter.getParameterAnnotation(PathVariable.class).value();
return StringUtils.hasText(paramName);
}
return true;
}
@Override
@SuppressWarnings("unchecked")
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
Map<String, String> uriTemplateVars =
(Map<String, String>) request.getAttribute(
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
return (uriTemplateVars != null) ? uriTemplateVars.get(name) : null;
}
}
HandlerMethodReturnValueHandler
HandlerMethodReturnValueHandler
作用在ServletInvocableHandlerMethod
中,作用是处理处理器执行后的返回值,主要有三个功能。
- 将相应的参数添加到Model
- 设置View
- 如果请求已经处理完,
mavContainer.setRequestHandled(true);
接口定义
//org.springframework.web.method.support.HandlerMethodReturnValueHandler
public interface HandlerMethodReturnValueHandler {
boolean supportsReturnType(MethodParameter returnType);
void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}
实现类分析
ViewNameMethodReturnValueHandler
public class ViewNameMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
@Override
public boolean supportsReturnType(MethodParameter returnType) {
Class<?> paramType = returnType.getParameterType();
return (void.class.equals(paramType) || String.class.equals(paramType));
}
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue == null) {
return;
}else if (returnValue instanceof String) {
String viewName = (String) returnValue;
mavContainer.setViewName(viewName);
if (isRedirectViewName(viewName)) {
mavContainer.setRedirectModelScenario(true);
}
}else {
//抛出异常略....
}
}
protected boolean isRedirectViewName(String viewName) {
if (PatternMatchUtils.simpleMatch(this.redirectPatterns, viewName)) {
return true;
}
return viewName.startsWith("redirect:");
}
}