SpringMVC原理

流程概括

请添加图片描述

以使用了 @RequestMapping 注解的Controller为例,流程概括:

  1. spring项目启动,扫描到@Controller注解,将其标记的类解析进行bean的初始化,初始化过程中会将Controller类、方法、及其中配置的请求等信息加载到处理器映射器HandlerMapping中,并将请求URL和对应的处理器Controller映射关系保存到MappingRegistry里。

  2. 当http请求来临后,进入前端控制器DispatcherServlet进行处理,前端控制器会调用处理映射器HandlerMapping处理映射器根据请求信息,去MappingRegistry映射关系中找到对应处理该请求的处理器Controller,并将Controller处理信息封装到执行链 HandlerExecutionChain中返回给前端控制器。

  3. 前端控制器DispatcherServlet会根据返回的执行链 HandlerExecutionChain,获取对应的处理适配器HandlerAdapter,取得的处理器适配器会通过反射执行执行链中的处理器Controller方法。 如果有配置拦截器Interceptor,那么适配器会在执行Controller的请求方法之前和之后,分别执行拦截器的前置和后置方法。

  4. 在**处理适配器HandlerAdapter执行处理器Controller的处理方法前,会通过参数解析器HandlerMethodArgumentResolver解析请求中携带的参数,而参数解析器会使用WebDataBinder或消息转换器MessageConverter把请求参数中的数据转换为controller方法参数:

    1. 如果是@RequestParam,会使用WebDataBinder把请求参数中的数据,绑定转换为controller方法参数,并进行Validation校验。
    2. 如果是@RequestBody,会使用消息转换器MessageConverter把请求参数中的数据,转换为controller方法参数,并使用WebDataBinder进行Validation校验。

    然后适配器会通过反射方式执行Controller对应的请求处理方法。
    注意: WebDataBinderMessageConverter 都可以将http中的参数转换为controller方法的java参数,在不同的注解中有不同的应用。

  5. 适配器执行完处理器Controller的方法后,会使用返回值处理器HandlerMethodReturnValueHandler,对Controller方法处理后生成的返回值进行处理,返回值处理器会调用消息转换器MessageConverter,并根据请求头中的accept,将返回值类型转换为jsonxml等请求希望接收到的返回类型。最后返回值处理器会将转换格式后的结果数据设置到响应体中并返回。

  6. 关于视图解析视图模型

    如果处理器Controller返回类型不是jsonxml、没有使用@ResponseBody@RestController注解,而是指定返回ModelAndView对象字符串页面等,则会使用处理视图模型返回值处理器进行处理(例如ViewNameMethodReturnValueHandler,根据实际的场景有所不同),并将返回值封装成ModelAndView对象,最后由视图解析器ViewResolver解析为视图View返回前端展现(比如代码中return "login",直接返回静态资源login.html登录页面)。

补充:关于消息转换器MessageConverter,并不是只在处理返回值进行响应时会用。比如在处理请求时,如果用了@RequestBody注解,那么RequestResponseBodyMethodProcessor就会用消息转换器处理请求体参数。除此之外,它还可以在许多其他场景下发挥作用,如消息转发和异步支持等等。

流程解析

1.初始化映射器

示例代码

一个Controller

image-20231014162910160

源码入口

AbstractHandlerMethodMapping 是对应 @RequestMapping 注解的处理器映射器RequestMappingHandlerMapping的父类,

在Spring项目启动后,因为它实现了 InitializingBean,在进行bean初始化时,断点会进入 AbstractHandlerMethodMapping 类中的 initHandlerMethods() 方法,进行处理器映射器的初始化(此处具体原理参考bean的生命周期初始化阶段)。

进入初始化方法 :AbstractHandlerMethodMapping类 --》initHandlerMethods方法

image-20231014160102711

下面看它的初始化逻辑:

执行初始化,保存请求映射关系

进入上图红框中的 processCandidateBean 方法。

其中的isHandler会判断bean是否被@Controller@RequestMapping注解修饰,如果是,就会进入 detectHandlerMethods 方法中。这也是处理器映射器RequestMappingHandlerMapping对应处理 @RequestMapping 注解的原因

image-20231012154732937

image-20231014163431588

如果被@Controller或@RequestMapping注解修饰,就会进入 detectHandlerMethods 方法中,把Controller作为参数传入。

然后保存@RequestMapping注解配置的urlController映射关系,后续spring收到请求时,就能根据此映射关系来寻找相应的Controller进行处理了

进入 detectHandlerMethods 方法:

关键处理步骤:getMappingForMethod,该方法会进入处理器射器RequestMappingHandlerMapping的实现中,返回一个有请求处理信息的RequestMappingInfo

image-20230629092508104

根据RequestMappingInfo获取控制器Controller中的请求处理方法,然后将Controller中@RequestMapping参数里的请求URL处理方法之间的映射关系注册到MappingRegistry中。

注册步骤在下图断点处registerHandlerMethod方法:

image-20231014161423625

可以看到mapping中包含实际业务开发的Controller中的请求路径。

进入registerHandlerMethod方法,一直到AbstractHandlerMethodMappingregister方法中:

mapping信息put到MappingRegistry

image-20231014161840363

image-20230628171257120

后续前端控制器收到请求时,就会通过查找MappingRegistry中的映射信息,获得对应的Controller进行处理了

2.初始化拦截器

自定义的拦截器在实现HandlerInterceptor接口、并通过@Configuration配置后,会在项目启动时自动纳入容器管理,并初始化

有一个WebMvcConfigurationSupport:mvc配置支持类,对springMVC所有配置做初始化,所有的HandlerMapping都会添加拦截器,此处只列举RequestMapping

//WebMvcConfigurationSupport中所有handlerMapping都有这句代码
mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));

image-20230630135557083

进入WebMvcConfigurationSupportgetInterceptors方法,获得拦截器(拦截器随项目启动自动配置,此处直接获取)

image-20230630110149571

自己开发的拦截器:

image-20231013101337967

image-20231013161121414

将上一步getInterceptors方法获得的拦截器返回进行set,给到RequestMappingHandlerMapping映射器中

Handler添加拦截器的

setInterceptors(getInterceptors(conversionService, resourceUrlProvider));

方法,是RequestMappingHandlerMappingAbstractHandlerMapping继承来的,

所以断点执行都是在AbstractHandlerMapping类的代码中,但实际属于RequestMappingHandlerMapping

image-20230630140052710

执行完setInterceptors方法后,RequestMappingHandlerMapping获得拦截器

可以看到,此处的interceptors集合,包含自定义的拦截器IndexInterceptor

image-20230630140344799

执行RequestMappingHandlerMapping的拦截器初始化方法initInterceptors

将上一步得到的拦截器,添加到adaptedInterceptors集合中,用于后续存入执行链。

下图是在遍历添加的过程中,将开发者自定义的拦截器加入集合:

image-20230630110903113

对应上一步,RequestMappingHandlerMapping得到了拦截器。

adaptedInterceptors集合中的拦截器会在后续请求处理时放入执行链

image-20230630140608161

3.mvc请求处理过程

示例代码

@RestController
public class IndexController {
    
	@RequestMapping("/dem")
	public Demo demo( @RequestParam(value = "age") String age){
		System.out.println(age);
		Demo d = new Demo();
		d.setOne("1");
		d.setTwo("2");
		d.setThree("3");
		d.setFour("4");
		return d;
	}
    
}

浏览器请求/dem,进入下面步骤:

请求进入DispatcherServlet前端控制器

image-20231013085155925

DispatcherServletServlet继承而来。

mvc的功能都从 org.springframework.web.servlet.DispatcherServlet --》 doDispatch 方法开始

获取处理器映射器

进入 DispatcherServlet --》 doDispatch --》 getHandler(processedRequest) 方法,

先确定处理请求要使用哪个**HandlerMapping处理器映射器**,再用它来获取执行链

image-20231016132151204

getHandler具体获取步骤:

  1. 首先要进入getHandler方法,获取HandlerMapping处理器映射器。
  2. getHandler方法会挨个遍历所有的HandlerMapping处理器映射器实现类,看哪个有请求信息,就用哪个。

HandlerMapping下有5个实现,分别对应不同的请求配置场景:

image-20230628150357381

  • BeanNameUrlHandlerMapping:xml配置,相当于bean的名字就是controller的访问路径,例如:

    <bean id="/test" class="com.hrms.controller.TestController"/>
    
  • RequestMappingHandlerMapping:只需要写@Controller以及@RequestMapping注解即可,无需配置xml,最为常用

  • WelcomePageHandlerMapping:用于处理欢迎页

  • RouterFunctionMapping:主要用于WebFlux响应式处理,是webflux中新引入的HandlerMapping

  • SimpleUrlHandlerMapping:也是xml的方式,在bean中手动配置controller的访问路径,如:

    <bean name="helloController" class="com.zhengfa.controller.HelloController"/>
        <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
            <property name="urlMap">
                <props>
                    <prop key="/hello.html">
                        helloController
                    </prop>
                </props>
            </property>
        </bean>
    

当我们在实际中只使用@RequestMapping注解,那么代码运行起来后,就能在对应@RequestMapping注解的映射器RequestMappingHandlerMapping中获取到请求信息了。下面介绍具体获取逻辑。

获取执行链

有了处理映射器,就需要用它来获得执行链了,接上面getHandler方法,下面进入AbstractHandlerMapping源码中看具体获取逻辑:

进入AbstractHandlerMapping --》 getHandler 方法,再进入黄框getHandlerInternal方法

image-20231016132335062

断点继续执行,会到达AbstractHandlerMethodMapping类中的getHandlerInternal方法中,从lookupHandlerMethod方法中获取到HandlerMethod

image-20230628171508561

lookupHandlerMethod方法内部:

  • 会根据请求信息去MappingRegistry保存的映射关系中匹配对应的请求路径,生成一个HandleMethod,后面用它来封装成执行链HandlerExecutionChain

image-20230628171549150

HandlerMethod中的信息,包含了业务Controller中的内容。也就是要处理当前请求所需要的Controller及其方法等信息

image-20230629144354493

获取到HandlerMethod后,回到AbstractHandlerMapping类中的getHandler方法调用处,

HandlerMethod封装到HandlerExecutionChain执行链的handler属性中,并返回给前端控制器

注意:执行链中有Handler属性,不同的Handler将对应不同的**HandlerAdapter**适配器,下面会介绍到

image-20231016132852912

进入getHandler --》 getHandlerExecutionChain方法,断点执行过程中:

  1. 通过构造方法将HandlerMethod赋予到执行链HandlerExecutionChainHandler属性中
  2. 遍历RequestMappingHandlerMapping的拦截器,将**adaptedInterceptors**的拦截器放入执行链,并返回包含拦截器的执行链

通过构造方法将HandlerMethod赋予到执行链HandlerExecutionChainHandler属性中

image-20230629144617401

遍历RequestMappingHandlerMapping的拦截器,将adaptedInterceptors的拦截器放入执行链(来源在第一步拦截器的初始化),并返回包含拦截器的执行链

image-20231016133409753

如果有自定义的拦截器,就会在这里添加进去

image-20230630114358780

返回执行链HandlerExecutionChain 前端控制器DispatcherServlet

image-20230629155136913

获取适配器

根据执行链来获取适配器,适配器也和处理映射器一样,因请求配置的不同,会得到不同的适配器。

因为示例代码使用的是@RequestMapping 注解,所以会返回 RequestMappingHandlerAdapter 适配器,下面介绍步骤:

还是在 DispatcherServlet --》 doDispatch( )

image-20231016133843885

进入上图getHandlerAdapter方法中,来获取适配器

可以看到适配器有多种,分别对应不同的场景:

  • RequestMappingHandlerAdapter:当执行链中的handler属性是HandlerMethod时,返回此适配器
  • HandlerFunctionAdapter:当执行链中的handler属性是HandlerFunction时,返回此适配器
  • SimpleControllerHandlerAdapter:当执行链中的handler属性是Controller(org.springframework.web.servlet.mvc.Controller)时,返回此适配器
  • HttpRequestHandlerAdapter:当执行链中的handler属性是HttpRequestHandler时,返回此适配器
  • SimpleServletHandlerAdapter:当执行链中的``handler属性是Servlet`时,返回此适配器

image-20230629162249602

遍历所有实现,通过supports方法来获取合适的适配器

image-20230629162348112

在遍历到RequestMappingHandlerAdapter适配器时,因为继承关系, 断点会执行AbstractHandlerMethodAdapter 中的supports判断执行链的handler属性是否为HandlerMethod

image-20230629150120331

如果handlerHandlerMethod,那么supportsInternal方法强制转换将不会失败,返回true

image-20230629150138737

根据文章上一步的执行链,当执行链中的handler是HandlerMethod时,会返回 RequestMappingHandlerAdapter 适配器

所以最终返回适配器RequestMappingHandlerAdapter

image-20231016134123109

适配器执行

在获取到合适的适配器以后,会执行handle方法,handle中会利用反射执行controller,并将controller的业务处理结果封装为ModelAndView视图模型 或 其他数据类型json、xml等返回

但是在handle之前和之后,会分别执行2个方法:

mappedHandler.applyPreHandle(processedRequest, response);

mappedHandler.applyPostHandle(processedRequest, response, mv);

其实就是取执行链HandlerExecutionChain中的interceptors,也就是我们开发的拦截器,分别执行其pre和post方法,来做一些自定义的请求拦截处理

下图红框为适配器获取ModelAndView视图模型的方法,黄蓝框对应拦截器的请求前置与后置方法:

image-20230630171924559

有拦截器的话,先执行拦截器Pre前置方法,对应上图蓝框,不具体介绍拦截器方法执行,下面重点说handle的执行:

进入上图红框代码handle方法中,看适配器执行过程:

进入后到达AbstractHandlerMethodAdapter --》handle方法,此处传参时将执行链的handler转为了HandlerMethod,作为后续使用:

image-20230630161519194

进入handleInternal,到达RequestMappingHandlerAdapter --》 handleInternal 中:

image-20230630161725033

继续进入上图蓝色断点代码invokeHandlerMethod中,如下图:

image-20231016135014038

为Controller方法的调用类invocableMethod赋予参数解析器返回值处理器

参数解析器会解析请求参数,返回值处理器会将Controller处理的数据结果进行处理封装,后续添加到response响应中

补充:

注意调用类ServletInvocableHandlerMethod,是根据执行链中的HandlerMethod创建出来的,它包含要调用的Controller相关信息

有了参数解析器与返回值处理器后,下面会执行Controller的方法:

此方法用于执行目标Controller方法,并获取目标方法返回值,其中涉及到参数解析器、返回值处理器、消息转换器

继续执行断点,到达位置:RequestMappingHandlerAdapter --》invokeHandlerMethod 方法,下图红框代码:

image-20231016135201860

进入红框代码内,到达ServletInvocableHandlerMethod --》 invokeAndHandle 方法,

image-20230630162214801

进入上图断点invokeForRequest,来到InvocableHandlerMethod --》invokeForRequest方法,进行请求处理,大致分为两步:

  • 获取参数解析器,并解析请求中的参数;
  • 通过参数执行Controller方法;

image-20231014104826577

下面看参数解析器的获取

获取参数解析器

invokeForRequest方法里,通过上图的getMethodArgumentValues方法获取请求参数,进入其中:

getMethodParameters方法会获取Controller方法参数使用的注解:

演示代码使用的是@RequestParam,所以得到的也是它:

image-20230701104945602

继续向下走,判断是否有支持处理该注解的参数解析器,通过supportsParameter方法判断:

image-20230701113512813

this.resolvers中有很多参数解析器,supportsParameter方法会遍历所有参数解析器,判断有没有能解析Controller方法参数的实现,如果没有能处理的就抛出异常。

supportsParameter方法代码,在参数解析器接口HandlerMethodArgumentResolver中:

image-20231014095603184

进入上面的supportsParameter方法,当循环遍历到解析器实现类RequestParamMethodArgumentResolver时,执行它重写的supportsParameter方法,判断Controller方法参数是否使用@RequestParam注解,下图是它的实现:

image-20231016140838320

因为用了@RequestParam,所以会在RequestParamMethodArgumentResolver的参数解析器实现中判断成功,不会抛出异常。

接上一步supportsParameter方法之后,执行resolveArgument

断点位置:InvocableHandlerMethod --》 this.resolvers.resolveArgument,如下图:

image-20231016141157305

resolveArgument里面会获取具体的参数解析器实例并用这个实例进行请求参数解析,并将解析后的参数返回

代码位置到了HandlerMethodArgumentResolverComposite类中的resolveArgument方法,如下:

image-20231014104148201

getArgumentResolver获取解析器实例时,里面还会使用supportsParameter方法。因为用了@RequestParam,所以会得到RequestParamMethodArgumentResolver的参数解析器实现,如上图黄框,就不重复演示了。

然后进行请求参数的解析工作,执行断点位置代码:

return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);

进行参数解析

断点进入到**RequestParamMethodArgumentResolver解析器**的resolveArgument方法中,走具体的解析逻辑

因为继承关系,此时开发工具的断点实际是在AbstractNamedValueMethodArgumentResolver类的resolveArgument方法上:

然后到达如下断点位置,使用WebDataBinder进行请求参数的转换,将http请求携带的参数,转为Controller的方法参数并接收:

image-20231014132306822

进入上图断点binder.convertIfNecessary中,做两件事:

  1. 获取DataBinder的实现
  2. 进行参数类型转换

image-20231014111208416

WebDataBinder的转换方法有两个实现:

  • DataBinder:数据绑定,它通过使用属性编辑器和数据校验器来实现数据绑定的过程。它可以将外部数据(如请求参数)转换为Java对象的属性,并可以在转换过程中执行数据校验。
  • TypeConverterSupport:类型转换,它负责将一种类型的值转换为另一种类型的值。它提供了类型转换的方法,并可以通过注册自定义的转换器来扩展转换功能。

根据示例代码,我们使用的是基本类型参数String,所以不用数据绑定,而是用类型转换TypeConverterSupport,进入如下代码执行转换:

TypeConverterDelegate类 --》 convertIfNecessary方法

image-20231014134226147

从断点处看到源类型即http请求参数类型为String类型,目标类型即Controller方法类型也是String类型,对应最上面的Controller示例代码

image-20231014111736821

converters具体有上百种,对应java中各种各样的类型转换:

image-20231014114651399

因为请求中的参数是String类型,Controller方法中参数也是String类型,因为是同类型,所以就用了空转换器NoOpConverter,不做转换直接返回。看下图的name:NO_OP

image-20231014111942682

NoOpConverter直接返回解析的参数:

image-20231014112237794

如果我将Controller方法参数改为了Integer,那么他就用字符串转数字的转换器了

image-20231014135838683

image-20231014114445553

最后,回到InvocableHandlerMethod --》invokeForRequest方法的调用位置,完成请求参数的解析,将参数用于doInvoke方法,执行目标Controller

image-20231014135512885

上图参数对应请求url参数:

image-20231014104943630

请求参数解析完,返回到InvocableHandlerMethod --》invokeForRequest,下一步执行目标方法doInvoke

image-20230630162435986

处理器执行

接上图进入doInvoke方法,进入java.lang.reflect包下的Method类的invoke方法(java反射):

image-20231014141559042

上一步doInvoke方法会执行目标Controller的请求处理方法,最后返回Controller的执行结果:

image-20231014141952445

对应了最开始的示例Controller

image-20231014142031938

然后下一步,用返回值处理器处理返回结果

获取返回值处理器

invoke处理完controller的方法后,获取返回值处理器来处理返回值

断点继续执行到达:ServletInvocableHandlerMethod --》 invokeAndHandle --》 this.returnValueHandlers.handleReturnValue,如下图:

image-20231014142331841

进入断点代码handleReturnValue方法内:

执行selectHandler方法,通过遍历获取对应的返回值处理器

源码中与上面的参数解析器套路一致,使用supportsReturnType来获得支持的返回值处理器

image-20231014143049681

示例代码中使用了@RestController注解,它是@Controller + @ResponseBody的组合,所以遍历到返回值处理器的实现类RequestResponseBodyMethodProcessor时,它的supportsReturnType方法判断为true

image-20231014143938756

image-20231014144024973

到这里确定了使用的返回值处理器RequestResponseBodyMethodProcessor

获取消息转换器

得到了返回值处理器后,继续执行断点,到返回值处理方法handleReturnValue,进行返回值的处理

位置:HandlerMethodReturnValueHandlerComposite --》 handleReturnValue

image-20231014144613581

进入handleReturnValue方法中,执行RequestResponseBodyMethodProcessor返回值处理器中的writeWithMessageConverters

image-20231014145133411

再进入writeWithMessageConverters方法中,遍历所有消息转换器,获取能将controller方法返回值转为http请求所需返回类型的转换器:

位置:AbstractMessageConverterMethodProcessor --》writeWithMessageConverters方法内

可以看见10个具体的消息转换器实现

image-20231014145822841

断点向下走,执行canWrite方法,看哪个消息转换器可以将Controller的返回值-自定义java类型Demo,转换为http请求头accept中的application/json

  • 参数valueType:对应示例Controller的返回值自定义Demo
  • 参数selectedMediaType:对应http请求头中Acceptapplication/json

image-20231014150211738

遍历发现,AbstractJackson2HttpMessageConverter类会返回true,证明它可以将自定义java类型转为json格式

image-20231014150824631

下面进行消息转换及响应

消息响应

执行下图的write方法,做消息转换并将数据写到Response

断点位置:AbstractMessageConverterMethodProcessor --》writeWithMessageConverters方法内,下方断点处:

image-20231014151739023

调用到上面获取的消息转换器AbstractJackson2HttpMessageConverter中的writeInternal方法,通过http响应将转换完的json字符串返回浏览器:

image-20231014152407398

浏览器显示:

image-20231014152744063

然后断点会回到前端控制器DispatcherServlet --》 doDispatch

因为返回json格式数据不会使用ModelAndView视图模型,所以代码里的mv为null,后面也不会渲染视图模型

image-20231014152600719

视图解析

因为返回json不涉及视图解析,这里说下返回html页面等类型的视图视图解析流程处理

改下示例代码,返回login字符串,并且项目静态资源中有此页面。

@Controller
public class IndexController {

    @RequestMapping("/login")
    public String login(){
        return "login";
    }
}

image-20231016113404899

浏览器输入"/login"请求,进入DispatcherServlet前端控制器

执行适配器的handle方法,获取视图模型mv

image-20231016094414051

进入适配器RequestMappingHandlerAdapter,调用invokeHandlerMethod执行目标方法

image-20231016094843214

继续走断点到invokeHandlerMethod方法内,创建ModelAndViewContainer实例并作为参数传入invokeAndHandle方法:

ModelAndViewContainer是Spring MVC框架中的一个容器类,用于存储和传递处理器方法的模型Model和视图View

image-20231016095453857

断点继续执行到达:ServletInvocableHandlerMethod --》 invokeAndHandle --》 this.returnValueHandlers.handleReturnValue,获取返回值处理器并处理返回值

image-20231016090858491

selectHandler方法遍历获取支持解析"login"字符串的返回值处理器

image-20231016091016761

循环到ViewNameMethodReturnValueHandler时,通过supportsReturnType方法判断它支持解析"login"字符串

判断逻辑:参数类型为空,或者是CharSequence及其子类。

CharSequence是字符串序列,是String类型的父接口

image-20231016091153899

获得ViewNameMethodReturnValueHandler返回值处理器实例

image-20231016091230769

ViewNameMethodReturnValueHandler对返回值进行处理:

image-20231016091310918

进入ViewNameMethodReturnValueHandler 中的 handleReturnValue方法:

主要是处理mavContainer

mavContainer赋予视图名,即"login",并判断是不是重定向视图,因为字符串中没有"redirect"重定向关键词,所以不是重定向的视图。

image-20231016091646867

回到适配器RequestMappingHandlerAdapter中调用getModelAndView方法获取视图模型:

上一步处理了mavContainer,这里用它去取获取视图模型ModelAndView

image-20231016091927673

getModelAndView方法处理完成返回视图模型ModelAndView

image-20231016092521812

最终回到DispatcherServlet前端控制器,调用processDispatchResult方法处理返回结果

image-20231016093017129

上面介绍消息响应时应为用的是@ResponseBody,返回json数据则mv是空。但是现在这里看到mv视图模型不是空了

image-20231016101354741

processDispatchResult方法中再调用render方法进行视图渲染

image-20231016093134148

render中执行resolveViewName方法获取视图解析器ViewResolver并进行渲染

image-20231016101753006

resolveViewName方法会遍历所有视图解析器的实现类,调用他们的resolveViewName方法来进行处理

image-20231016111758837

不同的解析器对应了不同的场景

image-20231016111731439

这里只有ContentNegotiatingViewResolver可以成功处理,它会用于根据客户端的请求内容类型选择合适的视图进行渲染。

进入视图解析器ContentNegotiatingViewResolverresolveViewName方法,看他如何渲染

  • getMediaTypes():会根据请求属性来获得请求中的所有媒体类型
  • getCandidateViews():根据请求的媒体类型(Media Type)选择合适的视图进行渲染,返回所有可以进行渲染的候选视图
  • getBestView():根据请求的媒体类型与候选视图的媒体类型进行匹配,选择最合适的视图进行渲染

image-20231016102823843

最后返回ThymeleafView视图,调用他的renderFragment方法渲染视图,并在此方法中将视图数据写入response响应中给回前端,最终会将"login"字符串渲染为"login.html",并将项目static文件夹中静态资源的此页面展现在浏览器

image-20231016093403774

最后通过梳理流程发现,mvc流程中的核心组件大都可以由开发者进行自定义扩展或实现,来定制自己的mvc过程处理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值