赞
踩
本文对invokeHandlerMethod方法进行分析
- /**
- *
- * 如果视图解析是必要的调用 {@link RequestMapping}注解的处理方法准备一个{@link ModelAndView}
- *
- * @since 4.2
- * @see #createInvocableHandlerMethod(HandlerMethod)
- */
- protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
- HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
- //创建一个ServletWebRequest对象
- ServletWebRequest webRequest = new ServletWebRequest(request, response);
- try {
- // 获取容器中全局配置的InitBinder和当前HandlerMethod所对应的Controller中
- // 配置的InitBinder,用于进行参数的绑定
- // 并创建一个WebDataBinderFactory对象
- WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
- // 获取容器中全局配置的ModelAttribute和当前当前HandlerMethod所对应的Controller
- // 中配置的ModelAttribute,这些配置的方法将会在目标方法调用之前进行调用
- // 他要用binderFactory是因为他需要处理 对应处理方法的model属性
- ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
- // 将handlerMethod封装为一个ServletInvocableHandlerMethod对象,
- // 该对象用于对当前request的整体调用流程进行了封装
- // 比如使用WebDataBinder格式化参数 使用argumentresolvers解析参数 处理返回数据
- ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
- //设置参数解析器 解析的是handlerMethod的方法参数
- invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
- //设置返回值处理器
- invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
- //设置用于参数绑定的factory
- invocableMethod.setDataBinderFactory(binderFactory);
- //设置ParameterNameDiscoverer该对象按照一定规则获取当前参数的名称
- invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
- //创建一个ModelAndViewContainer对象
- //我们要将处理方法的model 和视图封装起来需要用到该类
- ModelAndViewContainer mavContainer = new ModelAndViewContainer();
- //添加所有闪存属性.
- mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
- modelFactory.initModel(webRequest, mavContainer, invocableMethod);
- mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
- // 获取当前的AsyncWebRequest,这里AsyncWebRequest的主要作用是用于判断目标
- // handler的返回值是否为WebAsyncTask或DefferredResult,如果是这两种中的一种,
- // 则说明当前请求的处理应该是异步的。所谓的异步,指的是当前请求会将Controller中
- // 封装的业务逻辑放到一个线程池中进行调用,待该调用有返回结果之后再返回到response中。
- // 这种处理的优点在于用于请求分发的线程能够解放出来,从而处理更多的请求,只有待目标任务
- // 完成之后才会回来将该异步任务的结果返回。
- AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
- asyncWebRequest.setTimeout(this.asyncRequestTimeout);
- //将异步任务的线程池,request和interceptors封装到WebAsyncManager中
- 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();
- if (logger.isDebugEnabled()) {
- logger.debug("Found concurrent result value [" + result + "]");
- }
- invocableMethod = invocableMethod.wrapConcurrentResult(result);
- }
- // 对请求参数进行处理,调用目标HandlerMethod,并且将返回值封装为一个ModelAndView对象
- invocableMethod.invokeAndHandle(webRequest, mavContainer);
- if (asyncManager.isConcurrentHandlingStarted()) {
- return null;
- }
- // 对封装的ModelAndView进行处理,主要是判断当前请求是否进行了重定向,如果进行了重定向,
- // 还会判断是否需要将FlashAttributes封装到新的请求中
- return getModelAndView(mavContainer, modelFactory, webRequest);
- }
- finally {
- // 调用request destruction callbacks和对SessionAttributes进行处理
- webRequest.requestCompleted();
- }
- }
该方法主要做了四件事件,获取当前容器中使用@InitBinder注解注册的属性转换器;获取使用@ModelAttribute但没有@RequestMapping的方法,这些方法会在调用handlerMethod之前调用;请求的异步执行处理;处理请求参数调用目标方法处理返回值谢谢。
a.getDataBinderFactory(handlerMethod),使用该方法为每个handlerMethod方法创建一个WebDataBinderFactory,WebDataBinderFactory
在@ModelAttribute方法和handlerMethod方法的参数绑定执行
- private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
- //HandlerMethod对象的处理类类型
- Class<?> handlerType = handlerMethod.getBeanType();
- //handlerMethod所对应的控制器中的所有@InitBinder方法注解的方法缓存
- Set<Method> methods = this.initBinderCache.get(handlerType);
- if (methods == null) {
- // 在当前bean中查找所有标注了@InitBinder注解的方法,这里INIT_BINDER_METHODS就是一个
- // 选择器,表示只获取使用@InitBinder标注的方法
- methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
- //放入缓存里
- this.initBinderCache.put(handlerType, methods);
- }
- List<InvocableHandlerMethod> initBinderMethods = new ArrayList<InvocableHandlerMethod>();
- 这里initBinderAdviceCache是在RequestMappingHandlerAdapter初始化时同步初始化的,
- // 其内包含的方法有如下两个特点:①当前方法所在类使用@ControllerAdvice进行标注了;
- // ②当前方法使用@InitBinder进行了标注。也就是说其内保存的方法可以理解为是全局类型
- // 的参数绑定方法,如下这种
- // @ControllerAdvice
- // public class ControllerInitBinderExample{
- // @InitBinder
- // }
- // 获取使用@ControllerAdvice修饰的bean中的@InitBinder方法缓存;在afterPropertiesSet()方法中找到的
- for (Entry<ControllerAdviceBean, Set<Method>> entry : this.initBinderAdviceCache.entrySet()) {
- //检查是否应该应用于给定的bean类型{@code @ControllerAdvice}实例。
- //比如配置了@ControllerAdvice的扫描包路径决定可否应用
- if (entry.getKey().isApplicableToBeanType(handlerType)) {
- Object bean = entry.getKey().resolveBean();
- for (Method method : entry.getValue()) {
- initBinderMethods.add(createInitBinderMethod(bean, method));
- }
- }
- }
- // 这里是将当前HandlerMethod所在bean中的InitBinder添加到需要执行的initBinderMethods中。
- // 这里从添加的顺序可以看出,全局类型的InitBinder会在当前bean中的InitBinder之前执行
- for (Method method : methods) {
- Object bean = handlerMethod.getBean();
- initBinderMethods.add(createInitBinderMethod(bean, method));
- }
-
- return createDataBinderFactory(initBinderMethods);
- }
b.getModelFactory(handlerMethod, binderFactory)方法;
- private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
- // 这里SessionAttributeHandler的作用是声明几个属性,使其能够在多个请求之间共享,
- // 并且其能够保证当前request返回的model中始终保有这些属性
- SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
- Class<?> handlerType = handlerMethod.getBeanType();
- //从缓存中找到该handlermethod对应的类加了@ModelAttribute的方法
- Set<Method> methods = this.modelAttributeCache.get(handlerType);
- if (methods == null) {
- //没有找到,搜索该bean对应的类中的所有@ModelAttribute方法
- //但是 没有使用@RequestMapping标注的方法,并将这些方法缓存起来
- methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
- this.modelAttributeCache.put(handlerType, methods);
- }
- List<InvocableHandlerMethod> attrMethods = new ArrayList<InvocableHandlerMethod>();
- // Global methods first
- // 获取那些加了@ControllerAdvice的bean的 所有加了@ModelAttribute方法
- for (Entry<ControllerAdviceBean, Set<Method>> entry : this.modelAttributeAdviceCache.entrySet()) {
- if (entry.getKey().isApplicableToBeanType(handlerType)) {
- Object bean = entry.getKey().resolveBean();
- for (Method method : entry.getValue()) {
- attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
- }
- }
- }
- // 将当前方法所属的类中使用@ModelAttribute标注的方法添加到需要执行的attrMethods中。从这里的添加顺序
- // 可以看出,全局类型的方法将会先于局部类型的方法执行
- for (Method method : methods) {
- Object bean = handlerMethod.getBean();
- attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
- }
- //将需要执行的方法等数据封装为ModelFactory对象
- return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
- }
方法和getDataBinderFactory很类似,我们需要知道的ModelFactory主要是管理Model属性的这些model属性可以来自@ModelAttribute方法的执行产生的以及@SessionAttribute注解传递session的。其具体的执行过程实际上是在后面的ModelFactory.initModel()方法中进行。
c.创建了ServletInvocableHandlerMethod对象;真正的handlerMethod的请求处理调用在该类中进行
d.创建ModelAndViewContainer对象;记录在控制器方法调用中的模型属性和视图。
e.modelFactory.initModel(webRequest, mavContainer, invocableMethod);
- * 按以下顺序填充模型:
- * <ol>
- * <li>检索列为{@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 {
- //获取request的sessionAttributesHandler
- Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
- // 将@SessionAttribute声明的参数封装到ModelAndViewContainer中
- container.mergeAttributes(sessionAttributes);
- //调用@ModelAttributeMethod方法谢谢
- invokeModelAttributeMethods(request, container);
- for (String name : findSessionAttributeArguments(handlerMethod)) {
- //当前handlerMethod的container的model属性不包含name,添加进去
- if (!container.containsAttribute(name)) {
- //获取到当前attributeName对应在session中的值
- Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
- if (value == null) {
- throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);
- }
- //添加到模型的属性中
- container.addAttribute(name, value);
- }
- }
- }
这里initModel()方法主要做了两件事:①保证@SessionAttribute声明的参数的存在;②调用使用@ModelAttribute标注的方法。我们直接阅读invokeModelAttributeMethods()方法的源码:
- /**
- * 调用模型属性方法以填充model。 仅当模型中尚未存在属性时才添加model。
- */
- private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer container)
- throws Exception {
-
- while (!this.modelMethods.isEmpty()) {
- InvocableHandlerMethod modelMethod = getNextModelMethod(container).getHandlerMethod();
- // 获取当前方法中标注的ModelAttribute属性,然后判断当前request中是否有与该属性中name字段
- // 标注的值相同的属性,如果存在,并且当前ModelAttribute设置了不对该属性进行绑定,那么
- // 就直接略过当前方法的执行
- ModelAttribute ann = modelMethod.getMethodAnnotation(ModelAttribute.class);
- if (container.containsAttribute(ann.name())) {
- if (!ann.binding()) {
- container.setBindingDisabled(ann.name());
- }
- continue;
- }
- // 通过ArgumentResolver对方法参数进行处理,并且调用目标方法
- Object returnValue = modelMethod.invokeForRequest(request, container);
- // 如果当前方法的返回值不为空,则判断当前@ModelAttribute是否设置了需要绑定返回值,
- // 如果设置了,则将返回值绑定到请求中,后续handler可以直接使用该参数
- if (!modelMethod.isVoid()){
- String returnValueName = getNameForReturnValue(returnValue, modelMethod.getReturnType());
- if (!ann.binding()) {
- container.setBindingDisabled(returnValueName);
- }
- // 如果request中不包含该参数,则将该返回值添加到ModelAndViewContainer中,
- if (!container.containsAttribute(returnValueName)) {
- container.addAttribute(returnValueName, returnValue);
- }
- }
- }
- }
这里就是对@ModelAttribute方法的真正调用处。
f.请求异步处理支持,不看也罢
g.在进行所需要的前置方法和异步任务调用后,开始通过c创建的ServletInvocableHandlerMethod类的对象调执行真正的业务处理逻辑
- /**
- * 调用方法,并使用一个配置的{@link HandlerMethodReturnValueHandler}s处理返回值
- * @param webRequest the current request
- * @param mavContainer the ModelAndViewContainer for this request
- * @param providedArgs "given" arguments matched by type (not resolved)
- */
- public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
- Object... providedArgs) throws Exception {
- // 对目标handler的参数进行处理,并且调用目标handler
- Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
- // 设置相关的返回状态
- setResponseStatus(webRequest);
- // 如果请求处理完成,则设置requestHandled属性
- if (returnValue == null) {
- if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
- mavContainer.setRequestHandled(true);
- return;
- }
- }
- else if (StringUtils.hasText(getResponseStatusReason())) {
- // 如果请求失败,但是有错误原因,那么也会设置requestHandled属性
- mavContainer.setRequestHandled(true);
- return;
- }
-
- mavContainer.setRequestHandled(false);
- try {
- // 遍历当前容器中所有ReturnValueHandler,判断哪种handler支持当前返回值的处理,
- // 如果支持,则使用该handler处理该返回值
- this.returnValueHandlers.handleReturnValue(
- returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
- }
- catch (Exception ex) {
- if (logger.isTraceEnabled()) {
- logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
- }
- throw ex;
- }
- }
这里清晰的看到真正对请求的处理委托给了invokeForRequest(webRequest,mavContainer,providedArgs);该方法由父类InvocableHandlerMethod实现。ServletInvocableHandlerMethod主要是对返回值的处理。
h.invokeForRequest(webRequest, mavContainer, providedArgs);
- /**
- * 在给定请求的上下文中解析其参数值后调用该方法。
- * <p>参数值通常通过{@link HandlerMethodArgumentResolver}来解决。
- * 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.
- * @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
- */
- public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
- Object... providedArgs) throws Exception {
- // 将request中的参数转换为当前handler的参数形式
- Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
- if (logger.isTraceEnabled()) {
- logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
- "' with arguments " + Arrays.toString(args));
- }
- // 这里doInvoke()方法主要是结合处理后的参数,使用反射对目标方法进行调用
- Object returnValue = doInvoke(args);
- if (logger.isTraceEnabled()) {
- logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
- "] returned [" + returnValue + "]");
- }
- return returnValue;
- }
-
- /**
- *获取当前请求的方法参数值。
- */
- private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
- Object... providedArgs) throws Exception {
- // 获取当前handler所声明的所有参数,主要包括参数名,参数类型,参数位置,所标注的注解等等属性
- MethodParameter[] parameters = getMethodParameters();
- Object[] args = new Object[parameters.length];
- for (int i = 0; i < parameters.length; i++) {
- MethodParameter parameter = parameters[i];
- parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
- //调用方提供了参数providedArgs;我们真正的请求处理是不会有providedArgs
- //所以肯定为null
- args[i] = resolveProvidedArgument(parameter, providedArgs);
- if (args[i] != null) {
- continue;
- }
- // 如果在调用方提供的参数中不能找到当前类型的参数值,则遍历Spring容器中所有的
- // ArgumentResolver,判断哪种类型的Resolver支持对当前参数的解析,这里的判断
- // 方式比较简单,比如RequestParamMethodArgumentResolver就是判断当前参数
- // 是否使用@RequestParam注解进行了标注
- if (this.argumentResolvers.supportsParameter(parameter)) {
- try {
- args[i] = this.argumentResolvers.resolveArgument(
- parameter, mavContainer, request, this.dataBinderFactory);
- continue;
- }
- catch (Exception ex) {
- if (logger.isDebugEnabled()) {
- logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex);
- }
- throw ex;
- }
- }
- //没有找到对参数的合适处理 抛出异常
- if (args[i] == null) {
- throw new IllegalStateException("Could not resolve method parameter at index " +
- parameter.getParameterIndex() + " in " + parameter.getMethod().toGenericString() +
- ": " + getArgumentResolutionErrorMessage("No suitable resolver for", i));
- }
- }
- return args;
- }
-
- /**
- * Invoke the handler method with the given argument values.
- */
- protected Object doInvoke(Object... args) throws Exception {
- ReflectionUtils.makeAccessible(getBridgedMethod());
- try {
- return getBridgedMethod().invoke(getBean(), args);
- }
- catch (IllegalArgumentException ex) {
- assertTargetBean(getBridgedMethod(), getBean(), args);
- String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
- throw new IllegalStateException(getInvocationErrorMessage(text, args), ex);
- }
- catch (InvocationTargetException ex) {
- // Unwrap for HandlerExceptionResolvers ...
- Throwable targetException = ex.getTargetException();
- if (targetException instanceof RuntimeException) {
- throw (RuntimeException) targetException;
- }
- else if (targetException instanceof Error) {
- throw (Error) targetException;
- }
- else if (targetException instanceof Exception) {
- throw (Exception) targetException;
- }
- else {
- String text = getInvocationErrorMessage("Failed to invoke handler method", args);
- throw new IllegalStateException(text, targetException);
- }
- }
- }
主要使用了argument-resolvers对方法参数的转换处理和反射调用处理器的方法处理请求。
i.使用returnValueHandlers处理返回值returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
- /**
- * 迭代已注册的{@link HandlerMethodReturnValueHandler}并调用支持它的那个。
- * @throws IllegalStateException if no suitable {@link HandlerMethodReturnValueHandler} is found.
- */
- @Override
- public void handleReturnValue(Object returnValue, MethodParameter returnType,
- ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
- // 获取能够处理当前返回值的Handler,比如如果返回值是ModelAndView类型,那么这里的handler就是
- // ModelAndViewMethodReturnValueHandler
- HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
- if (handler == null) {
- throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
- }
- handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
- }
j.回到RequestMappingHandlerAdapter的invokeHandlerMethod(request,response,handlerMethod)的 getModelAndView(mavContainer, modelFactory, webRequest);方法
- private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
- ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
-
- modelFactory.updateModel(webRequest, mavContainer);
- if (mavContainer.isRequestHandled()) {
- return null;
- }
- ModelMap model = mavContainer.getModel();
- ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
- if (!mavContainer.isViewReference()) {
- mav.setView((View) mavContainer.getView());
- }
- if (model instanceof RedirectAttributes) {
- Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
- HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
- RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
- }
- return mav;
- }
通过ModelAndViewContainer包装ModelAndView对象并且支持对重定向的处理。
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。