网站建设 010,做外贸的女生现状,做网站用的大图,新乡集团网站建设目录
1、SpringMVC自动配置概览
2、简单功能分析
2.1、静态资源访问
1、静态资源目录
2、静态资源访问前缀
2.2、欢迎页支持
2.3、自定义 Favicon
2.4、静态资源配置原理
3、请求参数处理
0、请求映射
1、rest使用与原理
2、请求映射原理
1、普通参数与基本注解 …
目录
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)). 静态资源包括webjarsAutomatic registration of Converter, GenericConverter, and Formatter beans. 自动注册 ConverterGenericConverterFormatter 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). 自定义 FaviconAutomatic 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. 使用 EnableWebMvcConfigurationDelegatingWebMvcConfiguration 全面接管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进行了绑定。 WebMvcPropertiesspring.mvc、ResourcePropertiesspring.resources1、配置类只有一个有参构造器
有参构造器的所有参数的值都会从容器中确定。
//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, ObjectProviderHttpMessageConverters messageConvertersProvider,ObjectProviderResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizerProvider,ObjectProviderDispatcherServletPath dispatcherServletPath,ObjectProviderServletRegistrationBean? 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, OptionalResource 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使用与原理
xxxMappingRest风格支持使用HTTP请求方式动词来表示对资源的操作 以前/getUser 获取用户 /deleteUser 删除用户 /editUser 修改用户 /saveUser 保存用户现在 /user GET-获取用户 DELETE-删除用户 PUT-修改用户 POST-保存用户核心FilterHiddenHttpMethodFilter 表单methodpost隐藏域 _methodput
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-张三;}
}源码 BeanConditionalOnMissingBean({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的时候
表单提交带上 _methodPUT请求过来被HiddenHttpMethodFilter 拦截 请求是否正常并且是POST请求 获取到_method 的值并转成大写英文。兼容以下请求PUT、DELETE、PATCH原生requestpost包装模式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);// 找到当前请求使用哪个HandlerController的方法处理mappedHandler getHandler(processedRequest);//HandlerMapping处理器映射。/xxx-xxxx RequestMappingHandlerMapping保存了所有RequestMapping 和handler的映射规则。 所有的请求映射都在HandlerMapping处理器映射器中。
SpringBoot自动配置欢迎页的 WelcomePageHandlerMapping 。访问 /能访问到index.htmlSpringBoot自动配置了默认 的 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获取请求头ModelAttributeRequestParam获取请求参数MatrixVariable矩阵变量CookieValue获取cookie值RequestBody获取请求体
1.2、Servlet API
WebRequest、ServletRequest、MultipartRequest、HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneIdServletRequestMethodArgumentResolver 方法参数解析器来解析以上部的
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、复杂参数
Map、Modelmap、model里面的数据会被放在request的请求域中相当于调用 request.setAttributeErrors/BindingResultRedirectAttributes 重定向携带数据ServletResponseresponseSessionStatusUriComponentsBuilderServletUriComponentsBuilderMapString,Object map, Model model, HttpServletRequest request 都是可以给request域中放数据
等价于 request.getAttribute();
无论是Map、Model类型的参数底层都是调用 mavContainer.getMode() 返回一个 BindingAwareModelMap而它是Model也是Map。 1.4、自定义对象参数
可以自动类型转换与格式化可以级联封装。
/*** 姓名 input nameuserName/ br/* 年龄 input nameage/ br/* 生日 input namebirth/ br/* 宠物姓名input namepet.name/br/* 宠物年龄input namepet.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封装过程
自定义类型的参数解析器为 ServletModelAttributeMethodProcessor3、参数处理原理
HandlerMapping处理器映射器中找到能处理请求的HandlerController.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转换器将请求数据转换成指定的数据类型。 核心源码
OverrideNullablepublic 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 modelMapString, Object bindingResultModel bindingResult.getModel();mavContainer.removeAttributes(bindingResultModel);mavContainer.addAllAttributes(bindingResultModel);return attribute;}
GenericConversionService在设置每一个值的时候遍历它里面所有的所有converter124个直到找到可以将当前参数的数据类型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(MapString, 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(MapString, 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.jarResponseBody
引入web场景dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency而web场景自动引入了json场景dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-json/artifactIdversion2.3.4.RELEASE/versionscopecompile/scope/dependency主要引入的依赖为 测试代码与结果
Controller
public class ResponseTestController {ResponseBodyGetMapping(/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、返回值处理器判断是否支持这种类型返回值 supportsReturnType2、若支持该返回值处理器调用 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依赖 dependencygroupIdcom.fasterxml.jackson.dataformat/groupIdartifactIdjackson-dataformat-xml/artifactId
/dependency
2、postman分别测试返回json和xml
只需要改变请求头中Accept字段。
Http协议中规定的告诉服务器本客户端可以接收的数据类型。
发现服务代码没改动情况下具备同时返回xml跟json两种格式的能力。 3、开启浏览器参数方式内容协商功能 4、内容协商原理
1、判断当前响应头中是否已经有确定的媒体类型。MediaType2、获取客户端PostMan、浏览器支持接收的内容类型。获取客户端Accept请求头字段【application/xml】