SpringMVC4-组件(二)-HandlerAdapter-2

《看透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;
}

defaultModelredirectModel
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的,它有两个功能:

  1. 初始化Model
  2. 处理器执行后,将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:");
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值