1

Spring MVC组件之HandlerAdapter - yaoxtao

 2 years ago
source link: https://www.cnblogs.com/YaoxTao/p/16637783.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Spring MVC组件之HandlerAdapter

HandlerAdapter概述

HandlerAdapter组件是一个处理器Handler的适配器。HandlerAdapter组件的主要作用是适配特定的Handler来处理相应的请求。

在SpringMvc的源码中, HandlerAdapter是一个接口。该接口主要定义了三个方法。

         1.boolean supports(Object handler)

              判断HandlerAdapter组件是否支持这个handler实例。

         2.ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception

             HandlerAdapter组件使用handler实例来处理具体的请求。

          3.long getLastModified(HttpServletRequest request, Object handler)

             获取资源的最后修改值,该方法已经被遗弃。

HandlerAdapter类图

 374540-20220829231057154-1354824849.png

从以上类图中可以看出, HandlerAdapter接口系列的继承结构是比较简单的。HandlerFunctionAdapter,HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter,SimpleServletHandlerAdapter这四个类直接继承了HandlerAdapter接口。只有RequestMappingHandlerAdapter类稍微复杂一些。RequestMappingHandlerAdapter类是间接继承了HandlerAdapter这个接口。RequestMappingHandlerAdapter类首先是继承了AbstractHandlerMethodAdapter这个抽象类,而AbstractHandlerMethodAdapter这个抽象类再继承了HandlerAdapter接口。

RequestMappingHandlerAdapter

AbstractHandlerMethodAdapter

AbstractHandlerMethodAdapter是一个抽象类,它继承了HandlerAdapter接口,分别实现了HandlerAdapter接口的supports,handle,getLastModified这三个方法。

getLastModified方法已经弃用,就不多做描述。

supports方法

    @Override

    public final boolean supports(Object handler) {

        return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));

}

supports方法主要是判断该适配器是否支持这个handler。代码中的逻辑主要是判断handler是否是HandlerMethod类的实例,再综合了supportsInternal方法的返回值。

supportsInternal方法在AbstractHandlerMethodAdapter类中只是一个虚方法,主要是提供给子类RequestMappingHandlerAdapter来做具体的实现。

    @Override

    @Nullable

    public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)

           throws Exception {

 

        return handleInternal(request, response, (HandlerMethod) handler);

}

在handle方法中,又直接调用了handleInternal方法。handleInternal方法在AbstractHandlerMethodAdapter类中也只是一个虚方法的声明,专门是提供给子类来实现,handleInternal这个虚方法,最终是在RequestMappingHandlerAdapter类中给出了具体的逻辑实现。

AbstractHandlerMethodAdapter实现了Ordered接口。该接口主要是用于排序。

AbstractHandlerMethodAdapter还继承了WebContentGenerator类,该类是一个web内容生成器的超类,它提供了如浏览器缓存控制、是否必须有session开启、支持的请求方法类型(GET、POST等)等一些属性和方法。

WebContentGenerator中有个checkRequest方法,主要是对请求进行检测。子类RequestMappingHandlerAdapter中会调用这个方法。

RequestMappingHandlerAdapter

Spring MVC容器在初始化HandlerAdapter类型的组件时,默认初始化的就是RequestMappingHandlerAdapter这个组件。

RequestMappingHandlerAdapter除了继承至AbstractHandlerMethodAdapter这个父类,它还实现了InitializingBean和BeanFactoryAware接口。

我们知道在Spring中如果一个类实现了InitializingBean接口,Spring容器就会在实例化该Bean时,会调用这个Bean的afterPropertiesSet方法。

 @Override

    public void afterPropertiesSet() {

        // Do this first, it may add ResponseBody advice beans

        initControllerAdviceCache();

 

        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);

        }

}

在RequestMappingHandlerAdapter类中的afterPropertiesSet方法中,主要做了以下四件事情。

1.initControllerAdviceCache方法主要是初始化RequestMappingHandlerAdapter类中,initBinderAdviceCache,modelAttributeAdviceCache,          requestResponseBodyAdvice这三个属性。

在initControllerAdviceCache方法中会首先在Spring容器中,获取所带有@ControllerAdvice注解的bean。

然后依次遍历每个bean,在每个bean查找有@InitBinder注解的方法,再将这些方法初始化initBinderAdviceCache这个属性。

再次查找每个bean中有@ModelAttribute注解的方法,再将这些方法初始化到modelAttributeAdviceCache这个属性。

将实现了RequestBodyAdvice接口和ResponseBodyAdvice接口的bean,先收集起来。最后放入requestResponseBodyAdvice这个属性列表的最前面。

2.初始化RequestMappingHandlerAdapter类的argumentResolvers属性。

在RequestMappingHandlerAdapter类的argumentResolvers属性为空的情况下,会调用getDefaultArgumentResolvers()方法,来构造默认解析器的列表。

解析器的列表是按照注释解析,类型解析,自定义解析,所有类型解析的四种类型的顺序来进行构造的。

3.初始化RequestMappingHandlerAdapter类的initBinderArgumentResolvers属性。

在RequestMappingHandlerAdapter类的initBinderArgumentResolvers属性为空的情况下,会调用getDefaultInitBinderArgumentResolvers ()方法,来构造默认解析器的列表。

解析器的列表同样是按照”注释解析”, ”类型解析”, ”自定义解析”, ”所有类型解析”这四种类型的顺序来进行构造。

4.初始化RequestMappingHandlerAdapter类的returnValueHandlers属性,在RequestMappingHandlerAdapter类的returnValueHandlers属性为空的情况下,会调用getDefaultReturnValueHandlers ()方法,来构造默认解析器的列表。

解析器的列表是按照”单个意图”, ”注释解析”, ”多个意图”, ”类型解析”, ”自定义解析”, ”所有类型解析”等这几种类型的顺序来进行构造的。

RequestMappingHandlerAdapter继承了AbstractHandlerMethodAdapter类,它主要重写了父类AbstractHandlerMethodAdapter提供的三个模板方法。

1.supportsInternal方法

在supportsInternal方法中,直接返回了true。所以真正起作用的还是父类中的supports方法。

2.getLastModifiedInternal方法

该方法直接返回了-1。

3.handleInternal方法

这个方法是实际处理请求的方法。整个方法大致分了三个步骤。

1)         准备好处理请求的所有参数

2)         使用处理器处理请求

3)         将不同的类型的返回值统一成ModelAndView类型返回

 protected ModelAndView handleInternal(HttpServletRequest request,

           HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

 

        ModelAndView mav;

        checkRequest(request);

 

        // Execute invokeHandlerMethod in synchronized block if required.

        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...

           mav = invokeHandlerMethod(request, response, handlerMethod);

        }

 

        if (!response.containsHeader(HEADER_CACHE_CONTROL)) {

           if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {

               applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);

           }

           else {

               prepareResponse(response);

           }

        }

 

        return mav;

    }

从以上代码中可以看出,handleInternal的方法中主要是调用了checkRequest,invokeHandlerMethod等这几个方法。

  • checkRequest方法

checkRequest方法主要是对请求进行检测。RequestMappingHandlerAdapter类的

checkRequest方法其实调用的是父类WebContentGenerator的checkRequest方法。

在checkRequest方法中,主要是做了两个检测。

    1. 检测请求的方法是否被支持,在AbstractHandlerMethodAdapter类中由于restrictDefaultSupportedMethods的值设置为false,所以对请求方法的检测不会执行。
    2. 检测请求的session是否存在
  • invokeHandlerMethod方法

1.首先会用request和response构建一个ServletWebRequest对象。

2.构建一个WebDataBinderFactory对象

3.构建一个ModelFactory对象

4.创建一个ServletInvocableHandlerMethod的实例,实际请求的处理就是通过它来执行的。ServletInvocableHandlerMethod实例创建后,会把argumentResolvers,returnValueHandlers,parameterNameDiscoverer这些属性值赋值给这个实例。

ServletInvocableHandlerMethod实例处理请求使用的是invokeAndHandle方法。在invokeAndHandle方法中,会先调用父类的invokeForRequest方法。再对Response的状态进行设置,最后使用HandlerMethodReturnValueHandler来处理返回值。

在invokeForRequest方法中,会先使用getMethodArgumentValues这个方法来获取方法调用的所有参数。再调用doInvoke(args)方法。doInvoke(args)方法是实际执行请求处理的方法,它是HandlerMethod系列的最核心的方法。在doInvoke(args)方法中,通过getBridgedMethod()获取Method的桥方法。再利用java的反射技术,调用BridgedMethod的invoke(getBean(), args)方法,将具体的handler执行。

WebDataBinderFactory

在RequestMappingHandlerAdapter类中,通过getDataBinderFactory方法,创建了一个WebDataBinderFactory类型的实例。

 private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {

        Class<?> handlerType = handlerMethod.getBeanType();

        Set<Method> methods = this.initBinderCache.get(handlerType);

        if (methods == null) {

           methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);

           this.initBinderCache.put(handlerType, methods);

        }

        List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();

        // Global methods first

        this.initBinderAdviceCache.forEach((controllerAdviceBean, methodSet) -> {

           if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {

               Object bean = controllerAdviceBean.resolveBean();

               for (Method method : methodSet) {

                   initBinderMethods.add(createInitBinderMethod(bean, method));

               }

           }

        });

        for (Method method : methods) {

           Object bean = handlerMethod.getBean();

           initBinderMethods.add(createInitBinderMethod(bean, method));

        }

        return createDataBinderFactory(initBinderMethods);

    }

在getDataBinderFactory方法中,会先通过handlerMethod所属bean类型,查找带有@InitBinder注解的方法。

这里使用了initBinderCache缓存,一般是先在缓存中查找,在找不到的情况下,再通过MethodIntrospector.selectMethods方法去查找。最后把查找到的methods放入到缓存中。
    在initBinderAdviceCache缓存中,缓存的是全局的@InitBinder注解的方法。所谓全局的方法就是@ControllerAdvice注解类里面的@InitBinder注解的方法。

代码中会先将全局的method构建成InvocableHandlerMethod对象放入initBinderMethods的集合中。再将handlerMethod所属bean类型的method放入集合。最后通过initBinderMethods集合,来创建一个ServletRequestDataBinderFactory对象。

WebDataBinderFactory是一个工厂类,专门用来创建DataBinder类型的对象。DataBinder类型的对象用于参数绑定,主要功能就是实现参数跟String之间的类型转换,ArgumentResolver在进行参数解析的过程中会用到WebDataBinder,另外ModelFactory在更新Model时也会用到它。

ModelFactory

在RequestMappingHandlerAdapter类中,通过getModelFactory方法,创建了一个ModelFactory类型的实例。

private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {

        SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);

        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

       this.modelAttributeAdviceCache.forEach((controllerAdviceBean, methodSet) -> {

           if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {

               Object bean = controllerAdviceBean.resolveBean();

               for (Method method : methodSet) {

                   attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));

               }

           }

        });

        for (Method method : methods) {

           Object bean = handlerMethod.getBean();

           attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));

        }

        return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);

    }

ModelFactory类型实例的创建和WebDataBinderFactory的创建过程十分类似。都是先在handlerMethod所属bean类型查找@ModelAttribute注解的局部方法。再从modelAttributeAdviceCache缓存中,查找全局的@ModelAttribute注解的方法。

将全局和局部的@ModelAttribute注解方法,组合起来形成一个HandlerMothed的列表。全局的@ModelAttribute注解方法会放在这个列表的前面。

ModelFactory与WebDataBinderFactory的创建过程不同的是,多了一个SessionAttributesHandler实例的获取。最后会把HandlerMothed的列表,SessionAttributesHandler的实例和binderFactory作为参数,来构建ModelFactory的实例。

ModelFactory类是专门用来维护Model的。他主要做了两件事件。

  1. ModelFactory类的initModel方法初始化Model

initModel方法主要是在处理器Handler执行前,将相应的数据设置到Model中。

public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod)

           throws Exception {

 

        Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);

        container.mergeAttributes(sessionAttributes);

        invokeModelAttributeMethods(request, container);

 

        for (String name : findSessionAttributeArguments(handlerMethod)) {

           if (!container.containsAttribute(name)) {

               Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);

               if (value == null) {

                   throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);

               }

               container.addAttribute(name, value);

           }

        }

    }

从initModel方法的代码中可以看出,initModel方法一共做了三件事件。

  • 从SessionAttributesHandler对象中取出sessionAttributes,合并到ModelAndViewContainer的对象中。
  • 调用invokeModelAttributeMethods方法,该方法主要是依次执行所有@ModelAttribute注解的方法,并将最终的结果放到Model中。
  • 找到即是@ModelAttribute注解又是@SessionAttribute注解的参数名称,再遍历所有的参数,判断这个参数是否已经在Model中,如果没有,则把参数名和参数值存到Model中。
  1. ModelFactory类的updateModel方法,主要是将参数更新到SessionAttributes。updateModel方法主要做了两件事情。
  • 对SessionAttributes中的进行设置。
  • 给Model中需要的参数设置BindingResult,以备视图使用。

ServletInvocableHandlerMethod

 

374540-20220829231739857-2031463875.png

从类图中可以看出,ServletInvocableHandlerMethod类继承了InvocableHandlerMethod类,而InvocableHandlerMethod类又继承了HandlerMethod这个父类。

HandlerMethod

HandlerMethod类主要是封装了处理程序方法的信息,并提供了对方法参数、方法返回值、方法注释等信息的访问。

HandlerMethod类中的属性

Object 

Web控制器方法所在的Web控制器 bean。可以是字符串,代表 bean 的名称; 也可以是 bean 实例对象本身。

beanType

Class

Web控制器方法所在的Web控制器bean 的类型, 如果该bean被代理,这里记录的是被代理的用户类信息

method

Method

Web控制器方法

bridgedMethod

Method 

被桥接的Web控制器方法

parameters

MethodParameter[]

Web控制器方法的参数信息: 所在类所在方法,参数,索引,参数类型

responseStatus

HttpStatus 

注解@ResponseStatus的code属性

responseStatusReason

String 

注解@ResponseStatus的reason属性

InvocableHandlerMethod

InvocableHandlerMethod类继承了HandlerMethod类,它在父类的基础上增加了三个属性。

  1. dataBinderFactory:WebDataBinderFactory类型,主要是用于@InitBinder注释的参数。
  2. argumentResolvers:HandlerMethodArgumentResolverComposite类型,用于参数解析器。
  3. parameterNameDiscoverer:ParameterNameDiscoverer类型,用于获取参数名。

InvocableHandlerMethod类提供了一个重要的方法invokeForRequest,该方法主要是使用反射方式对Method进行调用。

其实@InitBinder注解和@ModelAttribute注解的方法,都是封装成了InvocableHandlerMethod类型的实例。调用InvocableHandlerMethod类型实例的invokeForRequest方法,就是对这两个注解下的方法进行了调用。

 @Nullable

    public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,

           Object... providedArgs) throws Exception {

 

        Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);

        if (logger.isTraceEnabled()) {

           logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +

                   "' with arguments " + Arrays.toString(args));

        }

        Object returnValue = doInvoke(args);

        if (logger.isTraceEnabled()) {

           logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +

                   "] returned [" + returnValue + "]");

        }

        return returnValue;

    }

invokeForRequest方法中主要做了两件事件。1.是通过getMethodArgumentValues方法,准备好了调用方法所需要的参数。2. 在doInvoke方法中,通过桥接方法,使用反射方式对方法进行了调用。

ServletInvocableHandlerMethod

ServletInvocableHandlerMethod类又继承了InvocableHandlerMethod类。它在提供了invokeAndHandle方法。

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,

           Object... providedArgs) throws Exception {

 

       Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);

        setResponseStatus(webRequest);

 

        if (returnValue == null) {

           if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {

               disableContentCachingIfNecessary(webRequest);

               mavContainer.setRequestHandled(true);

               return;

           }

        }

        else if (StringUtils.hasText(getResponseStatusReason())) {

           mavContainer.setRequestHandled(true);

           return;

        }

 

        mavContainer.setRequestHandled(false);

        Assert.state(this.returnValueHandlers != null, "No return value handlers");

        try {

           this.returnValueHandlers.handleReturnValue(

                   returnValue, getReturnValueType(returnValue), mavContainer, webRequest);

        }

        catch (Exception ex) {

           if (logger.isTraceEnabled()) {

               logger.trace(formatErrorForReturnValue(returnValue), ex);

           }

           throw ex;

        }

    }

在代码中可以看到,ServletInvocableHandlerMethod类的invokeAndHandle方法中,先调用了父类的invokeForRequest方法。在此基础上,1.增加了对ResponseStatus状态的设置。2.对返回值进行了处理。

ModelAndView

ModelAndView类中包含了Model和View两个重要的属性。ModelAndView主要用于后台与前端页面交互。它可以保存数据,并重定向或转发到指定页面,然后使用数据来渲染页面。

Model是一个ModelMap类型,而ModelMap继承了LinkedHashMap的这个子类。Model 对象负责在控制器(Controller)和视图(View)之间传递数据。Model属性中的数据会复制到 Servlet Response的属性中。Model相当于一个JOPO的对象。

View是Spring MVC中的视图。视图的作用是渲染数据,将模型model中的数据展示给用户。视图可以用于重定向或转发到指定页面。

HttpRequestHandlerAdapter

HttpRequestHandlerAdapter是专门的http请求处理器的适配器。这里的http请求处理器Handler,是一个实现了org.springframework.web. HttpRequestHandler接口的实例。

我们自定义的http请求处理器,需要实现HttpRequestHandler接口的handleRequest方法。在这个方法实现自己业务逻辑。

handleRequest方法的中提供了HttpServletRequest和HttpServletResponse的两个参数,来供我们使用。

SimpleControllerHandlerAdapter

SimpleControllerHandlerAdapter是控制器Controller处理器的适配器。这里的控制器处理器的Handler,是一个实现了org.springframework.web.servlet.mvc.Controller接口的实例。

Spring MVC中提供了org.springframework.web.servlet.mvc.AbstractController的这个抽象类,AbstractController抽象类实现了Controller接口。

在AbstractController中实现Controller接口的handleRequest方法,并提供了handleRequestInternal的这个模板方法留给子类自行扩展。

我们一般会直接继承AbstractController抽象类,并重写handleRequestInternal方法。在handleRequestInternal方法中,我们需要构造一个ModelAndView对象返回即可。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK