首页 > 其他分享 >Spring MVC组件之HandlerAdapter

Spring MVC组件之HandlerAdapter

时间:2022-08-29 23:22:38浏览次数:60  
标签:Spring Object request HandlerAdapter bean MVC RequestMappingHandlerAdapter 方法

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类图

 

 

从以上类图中可以看出, 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

类图

 

 

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

HandlerMethod

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

HandlerMethod类中的属性

名称

类型

描述

bean

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对象返回即可。

标签:Spring,Object,request,HandlerAdapter,bean,MVC,RequestMappingHandlerAdapter,方法
From: https://www.cnblogs.com/YaoxTao/p/16637783.html

相关文章

  • Spring的自动化装配
      在Spring中,对象无需自己查找和创建与其所关联的其他对象。相反,容易负责把需要相互协作的对象引用赋予各个对象。例如,一个订单管理的组件需要信用卡认证组件,但它不需要......
  • SpringBoot整合EMQ
    1.引入依赖<dependency><groupId>org.eclipse.paho</groupId><artifactId>org.eclipse.paho.client.mqttv3</artifactId><version>1.2.5</version></depe......
  • Spring Boot集成Mybatis
    集成Mybatis1、导入依赖<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><......
  • springboot1
    1.5.5常用的监控终端在百度搜索“springbootactuator”即可找到如下表格。第2章SpringBoot重要用法2.1自定义异常页面对于404、405、500等异常状态,服务器......
  • Spring Cache
    SpringCache是一个框架,实现了基于注解的缓存功能,只需要简单的加一个注解,就能实现缓存功能。SpringCache提供了一层抽象,底层可以切换不同的cache实现,具体就是通过CacheMan......
  • Spirng MVC
    dispatcherServlet:前置控制器,在spring项目启动时生成,初始化其中的handlermapper,handlderadapter,viewresponse。作为http请求的入口,dispatcherServlet会先通过handl......
  • SpringBoot+@Async
    1.使用@Async注解使用@Async注解就能简单的将原来的同步函数变为异步(注:@Async所修饰的函数不要定义为static类型,这样异步调用不会生效)@ComponentpublicclassTask{......
  • springboot定时同步数据,从sqlserver到mysql
    定时同步数据,从sqlserver到mysql 注意事项:一.primary:master #设置默认的数据源或者数据源组,默认值即为master二.@Scheduled()和 @DS("slave_1")注解 步骤:1.......
  • spring-boot-starter-data-jpa + SQLite简单例子(含全部代码)
    简介1.sqlite:SQLite是比Access更优秀的文件型数据库,支持复杂的SQL语句,支持索引、触发器,速度很快,开源等。2.jpa:SpringDataJPA是Spring基于ORM框架、JPA规范的......
  • 六、Spring Cloud Alibaba项目,Feign
    一、Feign 什么是Feign?Feign是Netflix开发的声明式、模板化的HTTP客户端,实现接口的调用。SpringCloudopenfeign 对Feign进行了增强,使其支持SpringMVC注解,另外还......