SpringBoot2核心技术-核心功能【05、Web开发】

news/2024/5/21 1:15:57/文章来源:https://blog.csdn.net/qq_33491651/article/details/127658687

f9676610780b25a2bbe4477df668b119.png

目录

1、SpringMVC自动配置概览 

2、简单功能分析

2.1、静态资源访问

1、静态资源目录

2、静态资源访问前缀

2.2、欢迎页支持

2.3、自定义 Favicon

2.4、静态资源配置原理

3、请求参数处理

0、请求映射

1、rest使用与原理

2、请求映射原理

1、普通参数与基本注解

1.1、注解:

1.2、Servlet API:

1.3、复杂参数:

1.4、自定义对象参数:

2、POJO封装过程

3、参数处理原理

3.1、HandlerAdapter(处理器映射器)

3.2、执行目标方法

 3.3、参数解析器-HandlerMethodArgumentResolver

3.4、返回值处理器

3.5、如何确定目标方法每一个参数的值

3.5.1、循环所有参数解析器,看哪个支持解析这个参数

3.5.2、解析这个参数的值

3.5.3、解析自定义类型参数-封装POJO

3.6、目标方法执行完成

4、数据响应与内容协商

1、响应JSON

原理解析

1.2、SpringMVC到底支持哪些返回值

1.3、HTTPMessageConverter原理

2、内容协商


1、SpringMVC自动配置概览 

Spring Boot provides auto-configuration for Spring MVC that works well with most applications.(大多场景我们都无需自定义配置)

The auto-configuration adds the following features on top of Spring’s defaults:

  • Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.
    • 内容协商视图解析器和BeanName视图解析器
  • Support for serving static resources, including support for WebJars (covered later in this document)).
    • 静态资源(包括webjars)
  • Automatic registration of Converter, GenericConverter, and Formatter beans.
    • 自动注册 Converter,GenericConverter,Formatter
  • Support for HttpMessageConverters (covered later in this document).
    • 支持 HttpMessageConverters (后来我们配合内容协商理解原理)
  • Automatic registration of MessageCodesResolver (covered later in this document).
    • 自动注册 MessageCodesResolver (国际化用)
  • Static index.html support.
    • 静态index.html 页支持
  • Custom Favicon support (covered later in this document).
    • 自定义 Favicon
  • Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document).
    • 自动使用 ConfigurableWebBindingInitializer ,(DataBinder负责将请求数据绑定到JavaBean上)

If you want to keep those Spring Boot MVC customizations and make more MVC customizations (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc.

不用@EnableWebMvc注解。使用 @Configuration + WebMvcConfigurer 自定义规则

If you want to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver, and still keep the Spring Boot MVC customizations, you can declare a bean of type WebMvcRegistrations and use it to provide custom instances of those components.

声明 WebMvcRegistrations 改变默认底层组件

If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc, or alternatively add your own @Configuration-annotated DelegatingWebMvcConfiguration as described in the Javadoc of @EnableWebMvc.

使用 @EnableWebMvc+@Configuration+DelegatingWebMvcConfiguration 全面接管SpringMVC

2、简单功能分析

2.1、静态资源访问

1、静态资源目录

只要静态资源放在类路径下: called /static (or /public or /resources or /META-INF/resources

访问:当前项目根目录/ + 静态资源名

原理:静态映射/**

接收到请求,先到dispatcherServlet转发找对应的Controller,若找不到对应的Controller就交给静态资源处理器。若静态资源处理器还找不到,就返回404响应页面。

改变默认的静态资源路径:

spring:mvc:static-path-pattern: /res/**resources:static-locations: [classpath:/haha/]

2、静态资源访问前缀

为了方便让拦截器直接放行静态资源的访问,可以给静态资源的访问配置一个统一的前缀。

默认是无前缀的。

spring:mvc:static-path-pattern: /res/**

当前项目 + static-path-pattern + 静态资源名 = 静态资源文件夹下找

示例:

原本请求路径:http://localhost:8080/res/xq.png

加了访问前缀后:http://localhost:8080/res/xq.png

2.2、欢迎页支持

可以有两种方式:静态资源、动态请求处理。

  • 静态资源路径下:index.html
    • 可以配置静态资源路径
    • 但是不可以配置静态资源的访问前缀,会导致index.html不能被默认访问
spring:mvc:static-path-pattern: /res/** # 配置之后导致welcome page 功能失效resources:static-locations: [ classpath:/haha/ ]
  • 编写controller处理 /index 请求

2.3、自定义 Favicon

访问图标。favicon.ico 放在静态资源目录下即可。

2.4、静态资源配置原理

  • SpringBoot启动默认加载 xxxAutoConfiguration 类(自动配置类)
  • SpringMVC的自动配置类 WebMvcAutoConfiguration。查看是否生效。
@Configuration(proxyBeanMethods = false
)
@ConditionalOnWebApplication(type = Type.SERVLET
)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {..
  • 查看自动配置类 给Spring容器中注册了哪些东西
@Configuration(proxyBeanMethods = false)
@Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})
@EnableConfigurationProperties({WebMvcProperties.class, ResourceProperties.class})
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
  • 配置文件的相关属性和xxx进行了绑定。
    • WebMvcProperties==spring.mvc
    • ResourceProperties==spring.resources

1、配置类只有一个有参构造器

有参构造器的所有参数的值都会从容器中确定。

//ResourceProperties resourceProperties;获取和spring.resources绑定的所有的值的对象
//WebMvcProperties mvcProperties 获取和spring.mvc绑定的所有的值的对象
//ListableBeanFactory beanFactory Spring的beanFactory
//HttpMessageConverters 找到所有的HttpMessageConverters
//ResourceHandlerRegistrationCustomizer 找到 资源处理器的自定义器。=========
//DispatcherServletPath  
//ServletRegistrationBean   给应用注册Servlet、Filter....public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties, WebMvcProperties mvcProperties,ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider,ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider,ObjectProvider<DispatcherServletPath> dispatcherServletPath,ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) {this.resourceProperties = resourceProperties;this.mvcProperties = mvcProperties;this.beanFactory = beanFactory;this.messageConvertersProvider = messageConvertersProvider;this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider.getIfAvailable();this.dispatcherServletPath = dispatcherServletPath;this.servletRegistrations = servletRegistrations;}

2、资源处理的默认规则

public void addResourceHandlers(ResourceHandlerRegistry registry) {if (!this.resourceProperties.isAddMappings()) {logger.debug("Default resource handling disabled");} else {Duration cachePeriod = this.resourceProperties.getCache().getPeriod();CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();if (!registry.hasMappingForPattern("/webjars/**")) {this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{"/webjars/**"}).addResourceLocations(new String[]{"classpath:/META-INF/resources/webjars/"}).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));}String staticPathPattern = this.mvcProperties.getStaticPathPattern();if (!registry.hasMappingForPattern(staticPathPattern)) {this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{staticPathPattern}).addResourceLocations(WebMvcAutoConfiguration.getResourceLocations(this.resourceProperties.getStaticLocations())).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));}}
}

可以发现使用配置可以禁用静态资源规则。

spring:
#  mvc:
#    static-path-pattern: /res/**resources:add-mappings: false   禁用所有静态资源规则
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties {private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/","classpath:/resources/", "classpath:/static/", "classpath:/public/" };/*** Locations of static resources. Defaults to classpath:[/META-INF/resources/,* /resources/, /static/, /public/].*/private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;

3、欢迎页的处理规则

    HandlerMapping:处理器映射器。保存了每一个Handler能处理哪些请求。    

@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext
applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext), applicationContext, this.getWelcomePage(), this.mvcProperties.getStaticPathPattern());welcomePageHandlerMapping.setInterceptors(this.getInterceptors(mvcConversionService, mvcResourceUrlProvider));welcomePageHandlerMapping.setCorsConfigurations(this.getCorsConfigurations());return welcomePageHandlerMapping;
}
WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders,ApplicationContext applicationContext, Optional<Resource> welcomePage, String staticPathPattern) {if (welcomePage.isPresent() && "/**".equals(staticPathPattern)) {//要用欢迎页功能,必须是/**logger.info("Adding welcome page: " + welcomePage.get());setRootViewName("forward:index.html");}else if (welcomeTemplateExists(templateAvailabilityProviders, applicationContext)) {// 调用Controller  /indexlogger.info("Adding welcome page template: index");setRootViewName("index");}
}

4、favicon

浏览器会发送 /favicon 来获取图标,整个session期间不再获取。

这就解释了,在配置了静态资源前缀下,浏览器获取不到的ico的原因。

3、请求参数处理

0、请求映射

1、rest使用与原理

  • @xxxMapping;
  • Rest风格支持(使用HTTP请求方式动词来表示对资源的操作
    • 以前:/getUser   获取用户  /deleteUser 删除用户   /editUser  修改用户   /saveUser 保存用户
    • 现在: /user GET-获取用户 DELETE-删除用户 PUT-修改用户 POST-保存用户
  • 核心Filter;HiddenHttpMethodFilter
    • 表单method=post,隐藏域 _method=put

@RestController
public class HelloController {@RequestMapping("/bug.jpg")public String hello() {return "hello";}@RequestMapping(value = "/user",method = RequestMethod.GET)public String getUser(){return "GET-张三";}@RequestMapping(value = "/user",method = RequestMethod.POST)public String saveUser(){return "POST-张三";}@RequestMapping(value = "/user",method = RequestMethod.PUT)public String putUser(){return "PUT-张三";}@RequestMapping(value = "/user",method = RequestMethod.DELETE)public String deleteUser(){return "DELETE-张三";}
}

源码:

    @Bean@ConditionalOnMissingBean({FormContentFilter.class})@ConditionalOnProperty(prefix = "spring.mvc.formcontent.filter",name = {"enabled"},matchIfMissing = true)public OrderedFormContentFilter formContentFilter() {return new OrderedFormContentFilter();}
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {HttpServletRequest requestToUse = request;if ("POST".equals(request.getMethod()) && request.getAttribute("javax.servlet.error.exception") == null) {String paramValue = request.getParameter(this.methodParam);if (StringUtils.hasLength(paramValue)) {String method = paramValue.toUpperCase(Locale.ENGLISH);if (ALLOWED_METHODS.contains(method)) {requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);}}}filterChain.doFilter((ServletRequest)requestToUse, response);}

Rest原理(表单提交要使用Rest的时候)

  • 表单提交带上 _method=PUT
  • 请求过来被HiddenHttpMethodFilter 拦截
    • 请求是否正常,并且是POST请求
      • 获取到_method 的值,并转成大写英文。
      • 兼容以下请求:PUT、DELETE、PATCH
      • 原生request(post),包装模式requesWrapper重写了getMethod方法,返回的是传入的值。
      • 过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的。
spring:mvc:hiddenmethod:filter:enabled: true

Rest使用客户端工具

  • 如PostMan直接发送Put、delete等方式请求,无需Filter。

2、请求映射原理

SpringMVC功能分析都从 org.springframework.web.servlet.DispatcherServlet  》 doDispatch

中开始。

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {ModelAndView mv = null;Exception dispatchException = null;try {processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// 找到当前请求使用哪个Handler(Controller的方法)处理mappedHandler = getHandler(processedRequest);//HandlerMapping:处理器映射。/xxx->>xxxx

RequestMappingHandlerMapping:保存了所有@RequestMapping 和handler的映射规则。

所有的请求映射都在HandlerMapping(处理器映射器)中。

  • SpringBoot自动配置欢迎页的 WelcomePageHandlerMapping 。访问 /能访问到index.html;
  • SpringBoot自动配置了默认 的 RequestMappingHandlerMapping。
  • 请求进来,挨个尝试所有的HandlerMapping看是否有请求信息。
    • 如果有就找到这个请求对应的handler
    • 如果没有就是下一个 HandlerMapping
  • 我们如果需要一些自定义的映射处理,我们也可以自己给容器中放HandlerMapping。

	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {if (this.handlerMappings != null) {for (HandlerMapping mapping : this.handlerMappings) {HandlerExecutionChain handler = mapping.getHandler(request);if (handler != null) {return handler;}}}return null;}

1、普通参数与基本注解

1.1、注解:

  • @PathVariable(路径变量)
  • @RequestHeader(获取请求头)
  • @ModelAttribute
  • @RequestParam(获取请求参数)
  • @MatrixVariable(矩阵变量)
  • @CookieValue(获取cookie值)
  • @RequestBody(获取请求体)

1.2、Servlet API:

  • WebRequest、
  • ServletRequest、
  • MultipartRequest、
  • HttpSession、
  • javax.servlet.http.PushBuilder、
  • Principal、
  • InputStream、
  • Reader、
  • HttpMethod、
  • Locale、
  • TimeZone、
  • ZoneId

 ServletRequestMethodArgumentResolver (方法参数解析器)来解析以上部的

@Overridepublic boolean supportsParameter(MethodParameter parameter) {Class<?> paramType = parameter.getParameterType();return (WebRequest.class.isAssignableFrom(paramType) ||ServletRequest.class.isAssignableFrom(paramType) ||MultipartRequest.class.isAssignableFrom(paramType) ||HttpSession.class.isAssignableFrom(paramType) ||(pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) ||Principal.class.isAssignableFrom(paramType) ||InputStream.class.isAssignableFrom(paramType) ||Reader.class.isAssignableFrom(paramType) ||HttpMethod.class == paramType ||Locale.class == paramType ||TimeZone.class == paramType ||ZoneId.class == paramType);}

1.3、复杂参数:

  • MapModel(map、model里面的数据会被放在request的请求域中,相当于调用 request.setAttribute)
  • Errors/BindingResult
  • RedirectAttributes( 重定向携带数据)
  • ServletResponse(response)
  • SessionStatus
  • UriComponentsBuilder
  • ServletUriComponentsBuilder

Map<String,Object> map,  Model model, HttpServletRequest request 都是可以给request域中放数据,
等价于 request.getAttribute();

无论是Map、Model类型的参数,底层都是调用 mavContainer.getMode() 返回一个 BindingAwareModelMap,而它是Model,也是Map。

 

1.4、自定义对象参数:

可以自动类型转换与格式化,可以级联封装。

/***     姓名: <input name="userName"/> <br/>*     年龄: <input name="age"/> <br/>*     生日: <input name="birth"/> <br/>*     宠物姓名:<input name="pet.name"/><br/>*     宠物年龄:<input name="pet.age"/>*/
@Data
public class Person {private String userName;private Integer age;private Date birth;private Pet pet;}
==================================================
@Data
public class Pet {private String name;private String age;}
@PostMapping("/saveuser")
public Person saveUser(Person person) {return person;
}

2、POJO封装过程

  • 自定义类型的参数解析器为:
    • ServletModelAttributeMethodProcessor

3、参数处理原理

  • HandlerMapping(处理器映射器)中找到能处理请求的Handler(Controller.method()),并返回处理器执行链。
  • 为当前Handler找到一个HandlerAdapter(处理器适配器);一般被@RequestMapping 标记的方法找到的都是 RequestMappingHandlerAdapter
  • 适配器执行目标方法,并确定方法参数的每一个值。

3.1、HandlerAdapter(处理器映射器)

0 - 支持方法上标注@RequestMapping

1 - 支持函数式编程的

xxxxxx

3.2、执行目标方法

// Actually invoke the handler.
//DispatcherServlet -- doDispatch
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

mav = invokeHandlerMethod(request, response, handlerMethod); //执行目标方法//ServletInvocableHandlerMethod
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
//获取方法的参数值
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);

 3.3、参数解析器-HandlerMethodArgumentResolver

确定将要执行的目标方法的每一个参数的值是什么;

SpringMVC目标方法能写多少种参数类型。取决于参数解析器。

  • 支持就调用 resolveArgument
  • 当前解析器是否支持解析这种参数

3.4、返回值处理器

3.5、如何确定目标方法每一个参数的值

============InvocableHandlerMethod==========================
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {MethodParameter[] parameters = getMethodParameters();if (ObjectUtils.isEmpty(parameters)) {return EMPTY_ARGS;}Object[] args = new Object[parameters.length];for (int i = 0; i < parameters.length; i++) {MethodParameter parameter = parameters[i];parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);args[i] = findProvidedArgument(parameter, providedArgs);if (args[i] != null) {continue;}if (!this.resolvers.supportsParameter(parameter)) {throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));}try {args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);}catch (Exception ex) {// Leave stack trace for later, exception may actually be resolved and handled...if (logger.isDebugEnabled()) {String exMsg = ex.getMessage();if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {logger.debug(formatArgumentError(parameter, exMsg));}}throw ex;}}return args;}

3.5.1、循环所有参数解析器,看哪个支持解析这个参数

	@Nullableprivate HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);if (result == null) {for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {if (resolver.supportsParameter(parameter)) {result = resolver;this.argumentResolverCache.put(parameter, result);break;}}}return result;}

3.5.2、解析这个参数的值

调用各自 HandlerMethodArgumentResolver 的 resolveArgument 方法即可。

3.5.3、解析自定义类型参数-封装POJO

1、找到参数解析器。

ServletModelAttributeMethodProcessor 判断自定义类型是否为简单类型,不是,则作为解析器处理此参数。

public static boolean isSimpleValueType(Class<?> type) {return (Void.class != type && void.class != type &&(ClassUtils.isPrimitiveOrWrapper(type) ||Enum.class.isAssignableFrom(type) ||CharSequence.class.isAssignableFrom(type) ||Number.class.isAssignableFrom(type) ||Date.class.isAssignableFrom(type) ||Temporal.class.isAssignableFrom(type) ||URI.class == type ||URL.class == type ||Locale.class == type ||Class.class == type));}

 2、WebDataBinder (Web数据绑定器)利用它里面的Converters(转换器)将请求数据转换成指定的数据类型。

核心源码: 

@Override@Nullablepublic final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");String name = ModelFactory.getNameForParameter(parameter);ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);if (ann != null) {mavContainer.setBinding(name, ann.binding());}Object attribute = null;BindingResult bindingResult = null;if (mavContainer.containsAttribute(name)) {attribute = mavContainer.getModel().get(name);}else {// Create attribute instancetry {attribute = createAttribute(name, parameter, binderFactory, webRequest);}catch (BindException ex) {if (isBindExceptionRequired(parameter)) {// No BindingResult parameter -> fail with BindExceptionthrow ex;}// Otherwise, expose null/empty value and associated BindingResultif (parameter.getParameterType() == Optional.class) {attribute = Optional.empty();}bindingResult = ex.getBindingResult();}}if (bindingResult == null) {// Bean property binding and validation;// skipped in case of binding failure on construction.WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);if (binder.getTarget() != null) {if (!mavContainer.isBindingDisabled(name)) {bindRequestParameters(binder, webRequest);}validateIfApplicable(binder, parameter);if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {throw new BindException(binder.getBindingResult());}}// Value type adaptation, also covering java.util.Optionalif (!parameter.getParameterType().isInstance(attribute)) {attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);}bindingResult = binder.getBindingResult();}// Add resolved attribute and BindingResult at the end of the modelMap<String, Object> bindingResultModel = bindingResult.getModel();mavContainer.removeAttributes(bindingResultModel);mavContainer.addAllAttributes(bindingResultModel);return attribute;}

GenericConversionService:在设置每一个值的时候,遍历它里面所有的所有converter(124个),直到找到可以将当前参数的数据类型(request带来参数的字符串)转换到指定的类型(JavaBean -- Integer)。

3、WebDataBinder 将转换后的数据再次封装到指定的JavaBean中。

3.6、目标方法执行完成

将所有的数据都放在 ModelAndViewContainer;包含要去的页面地址View。还包含Model数据。

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);

InternalResourceView:
@Overrideprotected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {// Expose the model object as request attributes.exposeModelAsRequestAttributes(model, request);// Expose helpers as request attributes, if any.exposeHelpers(request);// Determine the path for the request dispatcher.String dispatcherPath = prepareForRendering(request, response);// Obtain a RequestDispatcher for the target resource (typically a JSP).RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);if (rd == null) {throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +"]: Check that the corresponding file exists within your web application archive!");}// If already included or response already committed, perform include, else forward.if (useInclude(request, response)) {response.setContentType(getContentType());if (logger.isDebugEnabled()) {logger.debug("Including [" + getUrl() + "]");}rd.include(request, response);}else {// Note: The forwarded resource is supposed to determine the content type itself.if (logger.isDebugEnabled()) {logger.debug("Forwarding to [" + getUrl() + "]");}rd.forward(request, response);}}

关键点:

暴露模型作为请求域属性
// Expose the model object as request attributes.exposeModelAsRequestAttributes(model, request);
protected void exposeModelAsRequestAttributes(Map<String, Object> model,HttpServletRequest request) throws Exception {//model中的所有数据遍历挨个放在请求域中model.forEach((name, value) -> {if (value != null) {request.setAttribute(name, value);}else {request.removeAttribute(name);}});}


4、数据响应与内容协商

数据响应分为响应页面和响应数据。

  • 响应页面:如何发送一个请求,跳转到指定页面。一般用于一个单体请求。后续在视图解析器详细说明。
  • 响应数据:通常用来开发前后端分离的项目。前端发送请求给后端,后端响应数据、图片、xls、自定义协议数据等。

1、响应JSON

1.1、jackson.jar+@ResponseBody

引入web场景<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>而web场景自动引入了json场景<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-json</artifactId><version>2.3.4.RELEASE</version><scope>compile</scope></dependency>

 主要引入的依赖为:

测试代码与结果

@Controller
public class ResponseTestController {@ResponseBody@GetMapping("/test/person")public Person getPerson() {Person person = new Person();person.setAge(28);person.setBirth(new Date());person.setUserName("张三");return person;}
}

原理解析

1、返回值解析器

核心代码: this.returnValueHandlers.handleReturnValue

try {this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
	@Overridepublic void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);if (handler == null) {throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());}handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);}
RequestResponseBodyMethodProcessor  	
@Overridepublic void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest)throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {mavContainer.setRequestHandled(true);ServletServerHttpRequest inputMessage = createInputMessage(webRequest);ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);// Try even with null return value. ResponseBodyAdvice could get involved.// 使用消息转换器进行写出操作writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);}

2、返回值解析器原理 

  • 1、返回值处理器判断是否支持这种类型返回值 supportsReturnType;
  • 2、若支持,该返回值处理器调用 handleReturnValue 进行处理。
  • 若标记了@ResponseBody注解,则调用处理器为RequestResponseBodyMethodProcessor
    • 利用 MessageConverters 进行处理 将数据写为json,具体过程:

      • 1、内容协商(浏览器默认会以请求头的方式告诉服务器他能接受什么样的内容类型)

      • 2、服务器最终根据自己自身的能力,决定服务器能生产出什么样内容类型的数据;

      • 3、决定内容类型后,SpringMVC会遍历所有容器底层的 HttpMessageConverter(消息转换器)看谁能处理

        • 1、得到MappingJackson2HttpMessageConverter可以将对象写为json

        • 2、利用MappingJackson2HttpMessageConverter将对象转为json再写出去。

1.2、SpringMVC到底支持哪些返回值

ModelAndView
Model
View
ResponseEntity 
ResponseBodyEmitter
StreamingResponseBody
HttpEntity
HttpHeaders
Callable
DeferredResult
ListenableFuture
CompletionStage
WebAsyncTask
有 @ModelAttribute 且为对象类型的
@ResponseBody 注解 ---> RequestResponseBodyMethodProcessor;

1.3、HTTPMessageConverter原理

1、MessageConverter 接口规范

HttpMessageConverter: 看是否支持将 此 Class类型的对象,转为MediaType类型的数据。

例子:Person对象转为JSON。或者 JSON转为Person

2、默认的MessageConverter

0 - 只支持Byte类型的

1 - 只支持String

2 - 只支持String

3 - 只支持Resource

4 - 只支持ResourceRegion

5 - 只支持DOMSource.class \ SAXSource.class) \ StAXSource.class \StreamSource.class \Source.class

6 - 只支持MultiValueMap

7 - 直接返回true

8 - 直接返回true

9 - 支持注解方式xml处理的。

最终 MappingJackson2HttpMessageConverter 把对象转为JSON(利用底层的jackson的objectMapper转换的)

2、内容协商

根据客户端接收能力不同,返回不同媒体类型的数据。

假设现在有三种客户端,接收的数据类型不同:

  • 浏览器:能接收所有类型内容
  • 安卓客户端:只能接收json
  • 另一种客户端:只能接收xml

1、引入xml依赖

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

2、postman分别测试返回json和xml

只需要改变请求头中Accept字段。

Http协议中规定的,告诉服务器本客户端可以接收的数据类型。

发现服务代码没改动情况下,具备同时返回xml跟json两种格式的能力。

3、开启浏览器参数方式内容协商功能

4、内容协商原理

  • 1、判断当前响应头中是否已经有确定的媒体类型。MediaType
  • 2、获取客户端(PostMan、浏览器)支持接收的内容类型。(获取客户端Accept请求头字段)【application/xml】

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_261026.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vulnhub靶场----1、DC-1

文章目录一、环境搭建二、渗透流程三、思路总结一、环境搭建 靶场下载地址&#xff1a;https://download.vulnhub.com/dc/DC-1.zip Kali地址&#xff1a;192.168.144.148 DC-1靶机地址&#xff1a;192.168.144.149 二、渗透流程 端口扫描&#xff1a;nmap -A -T4 192.168.144.1…

基于 U-Net 网络的遥感图像语义分割 完整代码+论文

一、研究目的U-Net 是一种由全卷积神经网络启发的对称结构网络&#xff0c;在医疗影像分割领域取得了很好的效果。 此次研究尝试使用 U-Net 网络在对多光谱遥感影像数据集上进行训练&#xff0c;尝试使用卷积神经网络自动分割出建筑&#xff0c;希望能够得到一种自动分割遥感影…

基于Java+SpringBoot+Vue+Uniapp前后端分离健身预约系统设计与实现

博主介绍&#xff1a;✌全网粉丝3W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战✌ 博主作品&#xff1a;《微服务实战》专栏是本人的实战经验总结&#xff0c;《Spring家族及…

数字经济讨论题

自2001年以来&#xff0c;Alphabet&#xff08;Google&#xff09;已进行了200多次并购。下面列出了并购年份。选择Alphabet进行的三笔并购讨论这些并购是如何使Alphabet拥有新的或增强的现有业务领域重要的是考虑何时进行所选择的收购。谷歌已经从一家提供互联网搜索引擎的公司…

【VUE3.0_CSS功能】

CSS功能组件css作用域深度选择器&#xff08;标签名空格:deep(标签名)&#xff09;插槽选择器&#xff08;:soltted(标签名)&#xff09;全局选择器&#xff08;:global(类名)&#xff09;动态CSS&#xff08;v-bind&#xff09;useCSSModule拓展知识&#xff1a;deep的写法组件…

拒绝背锅:测试项目中的风险管理一定要知道

测试经理除了要管理产品线的质量保障和日常部门事务工作外&#xff0c;另一项比较重要的就是测试项目全流程的管理。 今天不聊整体的测试项目流程如何开展&#xff0c;而是想聊一聊在同行中比较高频出现的一个字眼&#xff1a;风险管理。 什么是风险管理 引用百度上的解释&a…

亿级高并发电商项目-- 实战篇 --万达商城项目 十二(编写用户服务、发送短信功能、发送注册验证码功能、手机号验证码登录功能、单点登录等模块)

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是小童&#xff0c;Java开发工程师&#xff0c;CSDN博客博主&#xff0c;Java领域新星创作者 &#x1f4d5;系列专栏&#xff1a;前端、Java、Java中间件大全、微信小程序、微信支付、若依框架、Spring全家桶 &#x1f4…

ES6的迭代器与迭代协议Symbol.iterator

前言ES6新增了两个协议&#xff1a;可迭代协议&#xff1a;对象必须具有Symbol.Iterator属性&#xff0c;属性值为一个函数&#xff0c;当这个对象被迭代时&#xff0c;就会调用该函数&#xff0c;返回一个迭代器。迭代器协议&#xff1a;描述了迭代器对象的具体规则。迭代器迭…

数据分析-深度学习 NLP Day2关键词提取案例

训练一个关键词提取算法需要以下几个步骤&#xff1a;1&#xff09;加载已有的文档数据集&#xff1b;2&#xff09;加载停用词表&#xff1b;3&#xff09;对数据集中的文档进行分词&#xff1b;4&#xff09;根据停用词表&#xff0c;过滤干扰词&#xff1b;5&#xff09;根据…

推荐5款免费且无广告的软件,助你提升效率

有时候一些小工具&#xff0c;能给你带来一些意想不到的效果&#xff0c;我们来看看下面这5款工具&#xff0c;你又用过其中几款呢&#xff1f; 1.文件大小查看——Folder Size Folder Size是一个可以查看文件大小的软件&#xff0c;使用它可以一键查看文件夹里的文件大小。你…

容器技术概述

容器化应用程序 软件应用程序通常依赖于运行时环境提供的其他库、配置文件或服务。软件应用程序的传统运行环境是物理主机或虚拟机&#xff0c;应用程序依赖项作为主机的一部分安装。 例如&#xff0c;考虑一个 Python 应用程序&#xff0c;它需要访问实现 TLS 协议的公共共享…

Leaf说明

什么是Leafleaf是叶子的意思我们使用的Leaf是美团公司开源的一个分布式序列号(id)生成系统我们可以在Github网站上下载项目直接使用为什么需要Leaf上面的图片中是一个实际开发中常见的读写分离的数据库部署格式专门进行数据更新(写)的有两个数据库节点它们同时新增数据可能产生…

性能测试学习和性能瓶颈分析路线

很多企业招聘都只写性能测试&#xff0c;会使用LR&#xff0c;jmeter工具。其实会使用jmeter和LR进行性能测试还只是性能测试的第一步&#xff0c;离真正的性能测试工程师还很远&#xff0c;笔者也还在路上 .。 性能测试&#xff0c;都是要求测试系统性能&#xff0c;系统自然…

【nas折腾篇】抉择吧,是入门还是放弃

2018年公司一位女同事问群晖的nas是否值得买。我一脸懵&#xff0c;以前给公司买云服务有采购nas盘&#xff0c;直接mount挂到服务器上当存储&#xff0c;但对于单独的nas服务器没有什么概念。一晃几年过去了&#xff0c;陆续刷到些nas服务的视频&#xff0c;周边朋友用nas的也…

2021-08-29

服务器 主&#xff1a;172.17.0.2 master 备:172.17.0.3 slave1 lvs虚拟IP:172.17.0.100 #nginx下载地址 http://nginx.org/download/ 本地文件路径 1.dockerfile构建nginx FROM centos:7 ADD nginx-1.6.0.tar.gz /usr/local COPY nginx_install.sh /usr/local RUN sh …

k8s种的kubectl命令

一.kubectl基本命令1.1 称述式资源管理的方法kubernetes集群管理集群资源的唯一入口是通过相应的方法调用apiserver的接口kubectl是官方的CLI命令行工具&#xff0c;用于与apiserver进行通信&#xff0c;将用户在命令行输入的命令&#xff0c;组织并转化为apiserver能识别的信息…

【单目标优化算法】海鸥优化算法(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

数组(一)-- LeetCode[26][80] 删除有序数组中的重复元素

1 删除有序数组中的重复项 1.1 题目描述 给你一个 升序排列 的数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次&#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。 由于在某些语言中不能改变数组的长度&#xff0c…

内部知识管理应该怎么做?

许多公司都知道需要有一个面向客户的知识库&#xff0c;以加强客户服务&#xff0c;提供更好的客户体验。 但是很多企业没有意识到的是&#xff0c;拥有一个内部知识库软件对于员工改善沟通和促进知识共享的重要性。 协作是组织成功的关键部分&#xff0c;通过明确的远景和使命…

消灭EMC的三大利器:电容器/电感/磁珠

滤波电容器、共模电感、磁珠在EMC设计电路中是常见的身影&#xff0c;也是消灭电磁干扰的三大利器。 对于这三者在电路中的作用&#xff0c;相信还有很多工程师搞不清楚&#xff0c;文章从设计中详细分析了消灭EMC三大利器的原理。 1 、滤波电容 尽管从滤除高频噪声的角度…