WebMvc 底层配置说明

WebMvcConfigurer

WebMvcConfigurer 是 SpringBoot 接入 SpringMvc 框架后,提供给用户自定义Web功能的接口,用户可以通过重写里面的方法来修改或增加Mvc中没有配置好的功能。

以下为 WebMvcConfigurer 接口的各接口的作用

 

SpringBoot 在 WebMvc 上提供了三种配置模式,分别是《全自动》、《手自一体》、《全手动》三种模式。

全自动:直接编写控制器逻辑,用户不需要知道MVC都配了什么设置,直接开始写业务即可。

手身一体:保留自动配置的效果同时,允许手动设置部分功能,定义Mvc底层组件

使用 @Configuration  + 配置 WebMvcConfigurer + 配置 WebMvcRegistrations

注意:手自一体不要在 @Configuration 组件中标记 @EnableWebMvc

全手动:禁用自动配置交果,全手动设置

使用 @Configuration  + 配置 WebMvcConfigurer,并标记 @EnableWebMvc

 

为什么在容器中放一个 WebMvcConfigurer 就能生效?

1.WebMvcAutoConfiguration 是一个自动配置类,它里面有一个 EnableWebMvcConfiguration

2.EnableWebMvcConfiguration继承了 DelegatingWebMvcConfiguration,这两个都生效

3.DelegatingWebMvcConfiguration利用 DI 把容器中所有WebMvcConfigurer 都注入进来

4.别人调用 DelegatingWebMvcConfiguration 的方法配置底层规则,而它调用所有 WebMvcConfigurer 的配置底层方法

 

路径匹配

Spring5.3 之后加入了更多的请求路径匹配的实现策略;

以前只支持 AntPathMatcher 策略, 现在提供了 PathPatternParser 策略。并且可以让我们指定到底使用那种策略。

可以通过配置项{spring.mvc.pathmatch.matching-strategy=path_pattern_parser}来更改路径匹配规则

 

Ant 风格的路径模式语法具有以下规则:

  • *:表示任意数量的字符
  • ?:表示任意一个字符。
  • **:表示任意数量的目录。
  • {}:表示一个命名的模式占位符。
  • []:表示字符集合,例如[a-z]表示小写字母。

例如:

  • *.html 匹配任意名称,扩展名为.html的文件。
  • /folder1/*/*.java 匹配在folder1目录下的任意两级目录下的.java文件。
  • /folder2/**/*.jsp 匹配在folder2目录下任意目录深度的.jsp文件。
  • /{type}/{id}.html 匹配任意文件名为{id}.html,在任意命名的{type}目录下的文件。

注意:Ant 风格的路径模式语法中的特殊字符需要转义,如:

  • 要匹配文件路径中的星号,则需要转义为\\*。
  • 要匹配文件路径中的问号,则需要转义为\\?。

 

内容协商

即一套系统适配多端数据格式的返回,当我希望以json格式输出时,系统输出json,当我希望输出xml时,则以xml方式输出

boot默认引入了jackson的json包,所以默认是使用json进行数据返回的。

1.我们可以引入多一个支持xml的包,来实现同时支持xml的数据返回。

<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
</dependency>

2.我们在需要返回的数据中,加入注解 @JacksonXmlRootElement

 

3.默认来说,内容协商会使用 accept 请求头作为识别,当accept 请求头为 application/json 时,则会返回 json 数据。当请求头为 application/xml 时,则会返回 xml 数据。

4.我们可以通过修改配置,开启基于请求参数的内容协商功能。

# 开启基于请求参数的内容协商功能。 默认参数名:format。 默认此功能不开启
spring.mvc.contentnegotiation.favor-parameter=true
# 指定内容协商时使⽤的参数名。默认是 format
spring.mvc.contentnegotiation.parameter-name=type

 

自定义内容协商前置-HttpMassageConverter的工作原理 (了解)

我们先来了解一下 HttpMassageConverter 是如何对对象数据进行转换的。

  • 1.我们的对象输出,来自注解 @ResponseBody (或使用了 @RestController),而 @ResponseBody 由 HttpMessageConverter 处理
  • (请求处理阶段,这里不讨论处理请求的问题,只讨论如何把对象转为返回内容的问题)
  • 2.请求会先进入 DispatcherServlet.class 的 doDispatch() 进行处理
  • 3.doDispatch() 会找到一个 HandlerAdapter 适配器进行处理整个请求
  • 4.RequestMappingHandlerAdapter 来执行,调用 invokeHandlerMethod() 切面方法
  • 5.会准备两个东西,HandlerMethodArgumentResolver (参数解析器,确定⽬标⽅法每个参数值),和 HandlerMethodReturnValueHandler (返回值处理器,确定⽬标⽅法的返回值改怎么处理)
  • 6.invokeAndHandle() 真正执行调用 Controller 中的方法
  • (返回对象处理阶段)
  • 7.得到返回值,开始找到合适的返回值处理器 HandlerMethodReturnValueHandler
  • 8.会通过检查是否支持注解来判断返回值处理器是否支持,最终找到 RequestResponseBodyMethodProcessor 能处理
  • 9.RequestResponseBodyMethodProcessor 调用 writeWithMessageConverters , 利用 MessageConverter 把返回值写出去
  • 10.HttpMessageConverter 会先进⾏内容协商, 遍历所有 MessageConverter 看谁能支持这种内容数据
  • 11.最终因为要 json 所以 MappingJackson2HttpMessageConverter ⽀持写出json
  • 12.jackson⽤ ObjectMapper 把对象写出去

SpringBoot 中默认会提供6种 MessageConverter  该初始化存在 WebMvcAutoConfigurationAdapter -> EnableWebMvcConfiguration -> DelegatingWebMvcConfiguration -> WebMvcConfigurationSupport,如下:

 

 

自定义内容协商-yaml

如果我们希望转为json和xml以外的格式,如yaml或properties的格式,此时我们就需要动到 HttpMessageConverter 组件。通过定制组件来实现一切其它内容协商。

编写增加 WebMvcConfigurer 提供的 configureMessageConverters 底层,修改底层的 MessageConverter

1.增加对yaml的支持,引入yaml支持包

<dependency>
      <groupId>com.fasterxml.jackson.dataformat</groupId>
      <artifactId>jackson-dataformat-yaml</artifactId>
</dependency>

2.增加请求头中的accept识别配置

 

spring.mvc.contentnegotiation.media-types

注意,media-types 是一个 Map<String, MediaType> 类型,所以在配置中应该需要定义一个名称

 

spring.mvc.contentnegotiation.media-types.yaml=application/yaml

3.编写自定义内容转换器,需要实现 HttpMessageConverter 或 继承 AbstractHttpMessageConverter<可转换类型>

 

@Configuration
public class MyMessageConverter extends AbstractHttpMessageConverter<Object> {

    private final ObjectMapper objectMapper;

    public MyMessageConverter(ObjectMapper objectMapper) {
        // 告诉父类,我们这个类是支持什么类型的消息
        super(new MediaType("application","yaml", StandardCharsets.UTF_8));
        // 创建一个 Yaml 消息处理工厂,并设置关闭了某些功能(如本例中的删除分页“---”标签)
        YAMLFactory yamlFactory = new YAMLFactory().disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER);
        this.objectMapper = new ObjectMapper(yamlFactory);
    }

    // 判断该数据类型是否支持使用这个来转换
    @Override
    protected boolean supports(Class<?> clazz) {
        return true;
    }

    // 读入数据时如何转化为对象
    @Override
    protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        return null;
    }

    // 如何把对象转换为输出数据
    @Override
    protected void writeInternal(Object o, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        // 把接收到的 对象 使用 objectMapper 来写出消息转换(objectMapper 在上面已经定义为使用 yamlFactory 处理)
        try(OutputStream os = outputMessage.getBody()) {
            objectMapper.writeValue(os, o);
        }
        // try() 是使用 Java 新特性的自动释放流的try方法

    }
}

思考:是不是可以通过实现功能,来创建自定义加密数据传输功能?

 

4.把自定义编写的消息处理器,加入到SpringBoot的消息处理器队列中去

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters){
        MyMessageConverter myMessageConverter = new MyMessageConverter();
        converters.add(myMessageConverter);
    }

注意:实现WebMvcConfigurer接口

 

addResourceHandler

addResourceHandler 是负责处理程序中静态资源的方法,它定义了静态资源如何处理。

private void addResourceHandler(ResourceHandlerRegistry registry, String pattern, Consumer<ResourceHandlerRegistration> customizer) {
            if (!registry.hasMappingForPattern(pattern)) {
                ResourceHandlerRegistration registration = registry.addResourceHandler(new String[]{pattern});
                customizer.accept(registration);
                // 静态资源的缓存机制,设置是缓存间隔多少秒(默认为0)
                registration.setCachePeriod(this.getSeconds(this.resourceProperties.getCache().getPeriod()));
                // 静态资源的缓存机制,缓存控间,默认无
                registration.setCacheControl(this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl());
                // 静态资源的缓存机制,是否使用lastModified头,默认为false
                registration.setUseLastModified(this.resourceProperties.getCache().isUseLastModified());
                this.customizeResourceHandlerRegistration(registration);
            }
        }

关于 lastModified: 浏览器首次获取资源时,会向服务器索取资源的最后修改时间,当下次再需要资源时,浏览器会先向服务器索取资源的最后修改时间,如果时间一致则不重新获取资源,否则再一次向服务器请求获取最新的静态资源。

 

静态资源映射

静态资源映射规则在 WebMvcAutoConfiguration 中进行了定义:

1. /webjars/** 的所有路径 资源都在 classpath:/META-INF/resources/webjars/

2./**的所有路径 资源都在classpath:/META-INF/resources/、classpath:/resources/、classpath:/static/、classpath:/public/

 

错误处理机制

SpringBoot的错误处理自动配置都在 ErrorMvcAutoConfiguration 中,它的作用主要是以下两大类

  • SpringBoot 会自适应处理错误,响应错误页面和响应JSON错误内容的数据(下面有具体的处理机制)
  • SpringMVC 的错误处理机制依然保留,MVC处理不了的,才会交给Boot进行处理

具体的处理机制如下图:

主要功能实现代码:

// 负责处出页面端的错误页生成
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE) // text/html
	public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
		HttpStatus status = getStatus(request);
		Map<String, Object> model = Collections
			.unmodifiableMap(getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
		response.setStatus(status.value());
                  // 页面端,会通过 resolveErrorView 来创建一个错误页面,这个错误页面由MVC在 resource 文件夹中查找特定的页面,看下面的代码
		ModelAndView modelAndView = resolveErrorView(request, response, status, model);
                  // 如果用户没有提供特定名称的错误页面代码,则mvc自己新建一个新的作为错误页输出
		return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
	}

// 负责生成JSON端的错误信息生成
	@RequestMapping
	public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
		HttpStatus status = getStatus(request);
		if (status == HttpStatus.NO_CONTENT) {
			return new ResponseEntity<>(status);
		}
		Map<String, Object> body = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));
		return new ResponseEntity<>(body, status);
	}

 

 

resolveErrorView 方法代码如下:

	@Override
	public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
		ModelAndView modelAndView = resolve(String.valueOf(status.value()), model);
		if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
			modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
		}
		return modelAndView;
	}

	private ModelAndView resolve(String viewName, Map<String, Object> model) {
                  // MVC 会通过查找 resource 文件夹下的 error 文件夹,并串合查找对应的精确错误码作为页面
                  // 如 resources/error/404.html 这样的文件
                  // 如果 error/404.html 中没有找到,就会找 resources/ 下有没有404.html 文件
                  // 如果上面的都没有,就会找模糊码 如 resources/error/4xx(5xx).html 文件
                  // 如果没有,就会在 /resources 下载 4xx, 5xx .html 文件
                  // 如果以上都没有,就会由Boot 实时生成错误页面(ErrorAutoConfigurer中的 render 方法)。
		String errorViewName = "error/" + viewName;
		TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName,
				this.applicationContext);
		if (provider != null) {
			return new ModelAndView(errorViewName, model);
		}
		return resolveResource(errorViewName, model);
	}

	private ModelAndView resolveResource(String viewName, Map<String, Object> model) {
		for (String location : this.resources.getStaticLocations()) {
			try {
				Resource resource = this.applicationContext.getResource(location);
				resource = resource.createRelative(viewName + ".html");
				if (resource.exists()) {
					return new ModelAndView(new HtmlResourceView(resource), model);
				}
			}
			catch (Exception ex) {
				// Ignore
			}
		}
		return null;
	}

规则:

  • 解析一个错误页
    • 如果发生了500、404、503、403 这些错误
      • 如果有模板引擎,默认在 classpath:/templates/error/精确码.html
      • 如果没有模板引擎,在静态资源文件夹下找 精确码.html
    • 如果匹配不到精确码.html这些精确的错误页,就去找5xx.html, 4xx.html模糊匹配
      • 如果有模板引擎,默认在 classpath:/templates/error/5xx.html
      • 如果没有模板引擎,在静态资源文件夹下找 5xx.html
  • 如果模板引擎路径templates下有 error.html页面,就直接渲染

 

 

使用自定义错误json响应

  • 前后分离使用json通讯,可以使用 @ControllerAdvice + @ExceptionHandler 进行统一异常处理

使用服务端页面渲染

  • 不可预知的一些,HTTP码表示的服务器或客户端错误
    • 给classpath:/templates/error/下面,放常用精确的错误码页面。500.html, 404.html
    • 给classpath:/templates/error/下面,放通用模糊匹配的错误码页面。 5xx.html, 4xx.html
  • 发生业务错误
    • 核心业务,每一种错误,都应该代码控制,跳转到自己定制的错误页。
    • 通用业务, classpath:/templates/error.html页面,显示错误信息。
    • 其中ModelAndView中的Model对象会提供相应的错误数据对象信息。

 

Tomcat服务器启动原理(了解)

  • SpringBoot 默认嵌入Tomcat作为Servlet容器。
  • 自动配置类是ServletWebServerFactoryAutoConfiguration , EmbeddedWebServerFactoryCustomizerAutoConfiguration
  • 自动配置类开始分析功能。`xxxxAutoConfiguration`
  • public class ServletWebServerFactoryAutoConfiguration {}
    • ServletWebServerFactoryAutoConfiguration 自动配置了嵌入式容器场景
    • 绑定了ServerProperties配置类,所有和服务器有关的配置 server
    • ServletWebServerFactoryAutoConfiguration 导入了 嵌入式的三大服务器 Tomcat、Jetty、Undertow
      • 导入 Tomcat、Jetty、Undertow 都有条件注解。系统中有这个类才行(也就是导了包)
      • 默认 Tomcat配置生效。给容器中放 TomcatServletWebServerFactory
      • 都给容器中 ServletWebServerFactory放了一个 web服务器工厂(造web服务器的)
      • web服务器工厂 都有一个功能, getWebServer获取web服务器
      • TomcatServletWebServerFactory 创建了 tomcat。
    • ServletWebServerFactory 什么时候会创建 webServer出来。
    • ServletWebServerApplicationContextioc容器,启动的时候会调用创建web服务器
    • Spring容器刷新(启动)的时候,会预留一个时机,刷新子容器。onRefresh()
    • refresh() 容器刷新 十二大步的刷新子容器会调用 onRefresh();
@Override
protected void onRefresh() {
    super.onRefresh();
    try {
        createWebServer();
    }
    catch (Throwable ex) {
        throw new ApplicationContextException("Unable to start web server", ex);
    }
}

Web场景的Spring容器启动,在onRefresh的时候,会调用创建web服务器的方法。

Web服务器的创建是通过WebServerFactory搞定的。容器中又会根据导了什么包条件注解,启动相关的 服务器配置,默认EmbeddedTomcat 会给容器中放一个 TomcatServletWebServerFactory ,导致项目启动,自动创建出Tomcat。

修改server下的相关配置就可以修改服务器参数

通过给容器中放一个ServletWebServerFactory,来禁用掉SpringBoot默认放的服务器工厂(Spring会检测容器中是否已存在ServletWebServerFactory),实现自定义嵌入任意服务器。

 

@EnableWebMvc 原理前缀,WebMvcAutoConfiguration 的配置原理

在说 @EnableWebMvc 为什么能禁用所有配置的原理之前,我们要分析一下 WebMvcAutoConfiguration 的配置过程,

  1. WebMvcAutoConfiguration web场景的自动配置类
    1. 支持RESTful的filter:HiddenHttpMethodFilter
    2. 支持非POST请求,请求体携带数据:FormContentFilter
    3. 导入 EnableWebMvcConfiguration
      1. RequestMappingHandlerAdapter
      2. WelcomePageHandlerMapping: 欢迎页功能支持(模板引擎目录、静态资源目录放index.html),项目访问/ 就默认展示这个页面.
      3. RequestMappingHandlerMapping:找每个请求由谁处理的映射关系
      4. ExceptionHandlerExceptionResolver:默认的异常解析器
      5. LocaleResolver:国际化解析器
      6. ThemeResolver:主题解析器
      7. FlashMapManager:临时数据共享
      8. FormattingConversionService: 数据格式化 、类型转化
      9. Validator: 数据校验JSR303提供的数据校验功能
      10. WebBindingInitializer:请求参数的封装与绑定
      11. ContentNegotiationManager:内容协商管理器
    4. WebMvcAutoConfigurationAdapter配置生效,它是一个WebMvcConfigurer,定义mvc底层组件
      1. 定义好 WebMvcConfigurer 底层组件默认功能;所有功能详见列表
      2. 视图解析器: InternalResourceViewResolver
      3. 视图解析器: BeanNameViewResolver,视图名(controller方法的返回值字符串)就是组件名
      4. 内容协商解析器: ContentNegotiatingViewResolver
      5. 请求上下文过滤器: RequestContextFilter: 任意位置直接获取当前请求
      6. 静态资源链规则
      7. ProblemDetailsExceptionHandler:错误详情
        1. SpringMVC内部场景异常被它捕获:新特性,后面会讲
      8. 定义了MVC默认的底层行为: WebMvcConfigurer

其中,WebMvcAutoConfiguration 必然会被 SpringBoot 自动配置调用,同时,WebMvcAutoConfiguration 中有一个条件载入 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)

也就是说,如果要导入 WebMvcAutoConfiguration 条件是容器中不存在 WebMvcConfigurationSupport,否则 WebMvcAutoConfiguration 不会进行加载。

 

ProblemDetailsExceptionHandler 新特性

ProblemDetailsExceptionHandler 是SpringBoot 的一个新加入的官方提供的错误处理预置,它实际是一个 @ControllerAdvice

它提供了SpringBoot自身的错误的 ExceptionHandler 处理,它提供了一种新的 Content-Type 数据类型“application/problem+json”

 

@ExceptionHandler({
HttpRequestMethodNotSupportedException.class, //请求方式不支持
HttpMediaTypeNotSupportedException.class,
HttpMediaTypeNotAcceptableException.class,
MissingPathVariableException.class,
MissingServletRequestParameterException.class,
MissingServletRequestPartException.class,
ServletRequestBindingException.class,
MethodArgumentNotValidException.class,
NoHandlerFoundException.class,
AsyncRequestTimeoutException.class,
ErrorResponseException.class,
ConversionNotSupportedException.class,
TypeMismatchException.class,
HttpMessageNotReadableException.class,
HttpMessageNotWritableException.class,
BindException.class
})

不过,ProblemDetailsExceptionHandler 默认是不开启的,因为它在代码中指定了条件载入

 

@Configuration(proxyBeanMethods = false)
	@ConditionalOnBooleanProperty("spring.mvc.problemdetails.enabled")
	static class ProblemDetailsErrorHandlingConfiguration {

		@Bean
		@ConditionalOnMissingBean(ResponseEntityExceptionHandler.class)
		@Order(0)
		ProblemDetailsExceptionHandler problemDetailsExceptionHandler() {
			return new ProblemDetailsExceptionHandler();
		}

	}

也就是说,要开启,需要在配置文件中设置“spring.mvc.problemdetails.enabled=true”才行。

 

 

WebMvcConfigurer 具体功能

提供⽅法 核⼼参数 功能 默认
addFormatters FormatterRegistry 格式化器:⽀持属性 上@NumberFormat 和@DatetimeFormat 的数据类型转换 GenericConversionS ervice
getValidator 数据校验:校验 Controller 上使⽤

@Valid标注的参数合 法性。需要导⼊ starter-validator

 

addInterceptors InterceptorRegistry 拦截器:拦截收到的 所有请求
configureContentNe ContentNegotiation 内容协商:⽀持多种 ⽀持 json
gotiation Configurer 数据格式返回。需要
配合⽀持这种类型的
HttpMessageConver
ter
configureMessageConverters List<HttpMessageConverter<?>> 消息转换器:标注@ResponseBody的返回值会利⽤MessageConverter直接写出去 8 个,⽀持byte,string,multipart,resource,json
addViewControllers ViewControllerRegistry 视图映射:直接将请求路径与物理视图映射。⽤于⽆ java 业务逻辑的直接视图⻚渲染
    <mvc:view-controller>
configureViewResolvers ViewResolverRegistry 视图解析器:逻辑视图转为物理视图 ViewResolverComposite
addResourceHandlers ResourceHandlerRegistry 静态资源处理:静态

资源路径映射、缓存控制

ResourceHandlerRegistry
configureDefaultSer vletHandling DefaultServletHandl erConfigurer 默认 Servlet:可以 覆盖 Tomcat 的 DefaultServlet。让 DispatcherServlet拦 截/
configurePathMatch PathMatchConfigur er 路径匹配:⾃定义 URL 路径匹配。可以

⾃动为所有路径加上 指定前缀,⽐如 /api

 

configureAsyncSupp ort AsyncSupportConfig urer 异步⽀持: TaskExecutionAuto Configuration
addCorsMappings CorsRegistry 跨域:
addArgumentResolv ers List<HandlerMethod ArgumentResolver> 参数解析器: mvc 默认提供
addReturnValueHan dlers List<HandlerMethod ReturnValueHandler

>

返回值解析器: mvc 默认提供
configureHandlerEx ceptionResolvers List<HandlerExcepti onResolver> 异常处理器: 默认 3 个 ExceptionHandlerEx ceptionResolver ResponseStatusExc eptionResolver DefaultHandlerExce ptionResolver
getMessageCodesR esolver 消息码解析器:国际 化使⽤

 

 

 

@EnableWebMvc 的禁用配置原理

我们知道,只要在项目中加入 @EnableWebMvc 后,SpringBoot 就会禁用所有的默认配置项,那么它是如何因这个注解,就会处理掉所有默认配置呢?

从上面的 WebMvcAutoConfiguration 的配置原理可知,WebMvcAutoConfiguration 的引入前提是容器中不存在 WebMvcConfigurationSupport 对象

我们看一下 @EnableWebMvc 注解的代码

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}

从代码可知,@EnableWebMvc 注解的代码中包含了 引入 DelegatingWebMvcConfiguration

从 DelegatingWebMvcConfiguration   类得知

public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {}

DelegatingWebMvcConfiguration 正是 WebMvcConfigurationSupport 的字类,因此,当我们引入了 @EnableWebMvc 时,容器中就存在了 WebMvcConfigurationSupport 对象,自然 WebMvcAutoConfiguration  就不会被加载

 

而正好,DelegatingWebMvcConfiguration 在重写了 WebMvcConfigurationSupport 类方法后,代码中没有做任何的配置动作。

最终使得所有预设配置都没有载入。

 

函数式Web请求(不常用)

相对使用 @Controller + @RequestMapping 的方式来定义Web请求,在SpringMVC 5.2之后,推出了一种新的Web请求方式,它解决了传统请求的耦合性。

即,在Controller类中,定义一个请求,需要在方法上加上 @GetMapping 注解来定义请求 URI 路径,这在一定程度上说明,路由定义和业务方法耦合在一起了,函数式请求就是希望解耦路由定义和业务。

通过路由单独定义,业务方法单独定义,拆分的方式解耦。

举例:假如某个web请求方法使用RESTful提供5种处理方式:

GET /user/1 获取1号用户
GET /users 获取所有用户
POST /user 请求体携带JSON,新增一个用户
PUT /user/1 请求体携带JSON,修改1号用户
DELETE /user/1 删除1号用户

我们可能需要在Controller中定义5个同样的方法。使用5个定义路由的注解,而函数式请求,则是在一个方法中,把5个路由路径在一个类中全给定义了,业务方法再另外在另一个类中定义。

函数式请求提供以下四种核心类

  • RouterFunction : 用于定义路由路径的方法
  • RequestPredicate : 用于定义请求的各项参数,如“接收何种数据类型”、“取出请求头”等
  • ServerRequest : 客户端请求带来的请求体对象
  • ServerResponse : 向客户端写出的响应体对象

实现步骤

1.创建一个容器组件(可以是Conponent也可以是Configuration)

2.实现 routerFunction 方法,并注册为Bean

@Bean
public RouterFunction<ServerResponse> routerFunction ()

从方法中我们得知,我们需要返回一个 RouterFunction<ServerResponse> 类型的对象。

 

3.我们使用 静态类 RouterFunctions 来定义一个router 路由路径

RouterFunctions.route().GET(业务方法)
RouterFunctions.route().GET(路径,业务方法)
RouterFunctions.route().GET(请求参数,业务方法)
RouterFunctions.route().GET(路径,请求参数,业务方法)
注意:还能定义POST等方法

4.创建一个专用于处理业务的类,里面把所有请求需要处理的业务添加上去,它实际上就是配合 业务方法(即HandlerFunction),而 HandlerFunction 实际上是一个函数式接口,所以我们的业务方法需要符合 HandlerFunction 的函数方法格式,因此,我们先参考 HandlerFunction 的函数接口方法格式

public interface HandlerFunction<T extends ServerResponse> {

	/**
	 * Handle the given request.
	 * @param request the request to handle
	 * @return the response
	 */
	T handle(ServerRequest request) throws Exception;
         // 可知,函数式方法,是泛型T,而T需要是ServerResponse的子类
}

 

 

所以我们就在业务方法中创建符合该函数式方法的格式

 

@Component
public class UserBizService {


    public ServerResponse getUsers(ServerRequest serverRequest){
        // 可以获取到路径接收到的 {id} 数据
        String id = serverRequest.pathVariable("id");
        // 处理查询业务
        return ServerResponse.ok().body(id);
    }
    
    public ServerResponse saveUser(ServerRequest serverRequest) throws ServletException, IOException {
        // 通过 serverRequest 获取到接收的数据
        User body = serverRequest.body(User.class);
        // 处理数据写入
        return ServerResponse.ok().body(body);
    }
}

5.在3中创建的路由类中,定义接收到的请求的处理方法,可通过DI注入业务类,并调用相应的处理方法

 

    @Bean
    public RouterFunction<ServerResponse> routerFunction(){
        return RouterFunctions.route()
                // 使用GET方法定义路径,接收所有类型的数据,使用userBizService::getUsers来处理接收到的请求
                .GET("/user/{id}", RequestPredicates.accept(MediaType.ALL),userBizService::getUsers)
                // 使用 POST方法定义路径,接收来自 JSON 类型的数据,使用 userBizService::saveUser 来处理请求
                .POST("/user",RequestPredicates.contentType(MediaType.APPLICATION_JSON),userBizService::saveUser)
                .build();
                // ... 添加其它的方法
        
    }

通过以上的方法实例,我们可以得知,就是把路由和请求规则写到一个类中,并把处理业务的代码写到另一个类中,以这种方式来解耦,但当前的业务依然使用传统的方式来实理请求处理,因为这种方法虽然得到了解耦,但是处理方法貌似变得十分麻烦,不常用。

 

如果您喜欢本站,点击这儿不花一分钱捐赠本站

这些信息可能会帮助到你: 下载帮助 | 报毒说明 | 进站必看

修改版本安卓软件,加群提示为修改者自留,非本站信息,注意鉴别

THE END
分享
二维码
打赏
海报
WebMvc 底层配置说明
WebMvcConfigurer WebMvcConfigurer 是 SpringBoot 接入 SpringMvc 框架后,提供给用户自定义Web功能的接口,用户可以通过重写里面的方法来修改或增加Mvc中没有配置好的功能。 以下为 WebMvcC……
<<上一篇
下一篇>>