我们把静态资源放到static目录下
![](https://i-blog.csdnimg.cn/blog_migrate/940e73eaa24a809a005de38d29fd1155.png)
浏览器器访问就能展示图片的信息,如果显示不出来,把target删除掉,重新启动项目
![](https://i-blog.csdnimg.cn/blog_migrate/eb5be28b1ef7e1b0ac994ff5c59b4d43.png)
当我们想要配置一些访问静态资源的规则,可以设置
spring:
mvc:
static-path-pattern: /res/**
默认不需要配置,访问的都是/**的资源
我们直接访问http://localhost:8080/aaa.webp 就会报错
![](https://i-blog.csdnimg.cn/blog_migrate/76e0c3eb6da8d89826df93ae96a17f2a.png)
需要http://localhost:8080/res/aaa.webp 这样访问
![](https://i-blog.csdnimg.cn/blog_migrate/86e28481657f324ba1c854f1727ab17d.png)
我们也可以自定义一个目录tp放入图片
在yml配置web静态资源路径
spring:
mvc:
static-path-pattern: /res/**
web:
resources:
static-locations: classpath:tp
![](https://i-blog.csdnimg.cn/blog_migrate/e296a605e8f6c28e7c06391f1c208db3.png)
![](https://i-blog.csdnimg.cn/blog_migrate/b16c2c08f0b7460dca1db4141d406a10.png)
我们在tp这个目录下配置一个index.html看看能不能访问
![](https://i-blog.csdnimg.cn/blog_migrate/afc859450deb6107221687c8c922e634.png)
发现也可以
![](https://i-blog.csdnimg.cn/blog_migrate/e08a187d3365aaae3fc626c5789fb85d.png)
但是我们想直接访问http://localhost:8080/ 就直接展示index.html就会报错
需要把mvc静态拦截资源注释掉 才能访问
![](https://i-blog.csdnimg.cn/blog_migrate/9d82422f74281cd8dbbdbf7819293192.png)
不用输入index.html,直接访问就能显示了
![](https://i-blog.csdnimg.cn/blog_migrate/bf77402a12afa0ee113e90cbe49ba62f.png)
我们把yml文件的配置注释掉,把资源都放在static目录下
创建一个favicon.ico的图标文件,必须是这个名字
![](https://i-blog.csdnimg.cn/blog_migrate/0c78b9c9026c9b4a9241e09823765522.png)
可以看到图标就有了,如果不显示,清空缓存 关闭浏览器 重新打开
![](https://i-blog.csdnimg.cn/blog_migrate/7bc870190fa1a3a345c6de0c8e20760c.png)
spring mvc的自动配置类就是WebMvcAutoConfiguration
![](https://i-blog.csdnimg.cn/blog_migrate/57ca2f560f44b613ce9fe9c43cee77ed.png)
在@ConditionalOnWebApplication(type = Type.SERVLET)
这里只有基于servlet的web应用程序才能匹配
如果你的项目不是web项目,那么使用不了mvc的功能
我们在看下WebMvcAutoConfigurationAdapter webmvc自动配置适配器
![](https://i-blog.csdnimg.cn/blog_migrate/3f3cd6c74354115cfcff74ea3ff06794.png)
我们点进去WebMvcProperties,webmvc属性和前缀spring.mvc绑定的
就是我们在yml文件输入spring.mvc的时候这些字段会自动显示出来
![](https://i-blog.csdnimg.cn/blog_migrate/56e1ff52f85fd7fcfc5e2bb8d4316fd3.png)
在来看下WebProperties,他是和前缀spring.web绑定的
![](https://i-blog.csdnimg.cn/blog_migrate/7ebf7421da7fed8d3d44f92677fade5c.png)
在看下Resources,这里对应者静态资源的目录
![](https://i-blog.csdnimg.cn/blog_migrate/32aaeaae9d1244893e69e6c6b6e713c5.png)
我们在看下webmvc字段配置适配器的构造方法,所有的参数值都会从容器中确定
ListableBeanFactory beanFactory 就是spring的bean工厂
HttpMessageConverters 就是http消息转换器
ResourceHandlerRegistrationCustomizer 资源处理程序注册自定义器
ServletRegistrationBean 给应用注册servlet ,filter
![](https://i-blog.csdnimg.cn/blog_migrate/9c77067378ebc21a1f6a5c8eca1acc6e.png)
在来看下addResourceHandlers,添加资源处理程序,资源处理的默认规则
![](https://i-blog.csdnimg.cn/blog_migrate/9a7687ffd0d59ffb48b7ffe115d26b8e.png)
我们进入this.resourceProperties.isAddMappings
是否启用默认静态资源处理,如果为true,可以访问静态资源,如果为false,静态资源不能访问
可以看到默认是true
![](https://i-blog.csdnimg.cn/blog_migrate/ae54a7c879282331609361056b9f5e95.png)
我们在yml文件设置为false
spring:
web:
resources:
add-mappings: false
添加资源处理程序这里 直接禁止了
![](https://i-blog.csdnimg.cn/blog_migrate/f5a425b6630a8368c70cce9b88b6efdc.png)
我们在浏览器访问静态资源就是404,默认是true 不需要配置
![](https://i-blog.csdnimg.cn/blog_migrate/7ea2002d92798628a5072a5399065130.png)
还可以在yml文件设置缓存时间
![](https://i-blog.csdnimg.cn/blog_migrate/e92cb1e2432734afbf653a9fe252ef33.png)
spring:
web:
resources:
cache:
period: 100000
![](https://i-blog.csdnimg.cn/blog_migrate/f175498781e6c2901d9e8d0cceebac81.png)
在this.mvcProperties.getStaticPathPattern()这里mv属性的静态资源规则是/**
![](https://i-blog.csdnimg.cn/blog_migrate/90b0ef2db6aa9fb621c0b4b84cec9a13.png)
在Yml文件配置
spring:
mvc:
static-path-pattern: /res/**
这里获取的时候就变成了res/**
![](https://i-blog.csdnimg.cn/blog_migrate/8e62b962d0a0bf5675ddb70f488c5f6d.png)
在this.resourceProperties.getStaticLocations() 静态资源路径 就是读取的下面的路径信息
![](https://i-blog.csdnimg.cn/blog_migrate/8106d3f14b4aaec55cf623cdde15ab27.png)
当我们在Yml文件改了tp这个文件夹路径
spring:
web:
resources:
static-locations: classpath:tp
这里读取的就是tp这个目录
![](https://i-blog.csdnimg.cn/blog_migrate/e87a29b6f22d34daf18cda6f34b4c806.png)
我们来看下欢迎页的处理规则
WelcomePageHandlerMapping 欢迎页处理程序映射
HandlerMapping处理器映射 ,保存了每一个handler能处理那些请求
![](https://i-blog.csdnimg.cn/blog_migrate/61ac159cb0db2c88ff1a16db565f02c7.png)
可以看到如果配置了静态资源规则和静态资源拦截,那么不会走index.html界面
![](https://i-blog.csdnimg.cn/blog_migrate/29fba8324476d61ed67fc9dc1120cbc1.png)
我们把这些yml配置都去掉,可以看到访问到了static目录下的index.html
然后访问localhost:8080 就自动去找index.html界面了
![](https://i-blog.csdnimg.cn/blog_migrate/5022ef2cfcc30bc9244a7c5ba6c774fb.png)
@DeleteMapping("/bb")
public String bb(){
return "bb";
}
@PutMapping("/cc")
public String cc(){
return "cc";
}
当我们在index.html界面直接写PUT和DELETE的表单方式是不行的
<form action="/cc" method="PUT">
<input type="submit" value="修改">
</form>
<form action="/bb" method="DELETE">
<input type="submit" value="删除">
</form>
![](https://i-blog.csdnimg.cn/blog_migrate/96f6a0087d7a34745fa171ec11c1afca.png)
我们需要改成post并且在加一行隐藏的列,name变成_method,value输入DELETE和PUT
<form action="/cc" method="post">
<input type="hidden" name="_method" value="put">
<input type="submit" value="修改">
</form>
<form action="/bb" method="post">
<input type="hidden" name="_method" value="delete">
<input type="submit" value="删除">
</form>
我们来看这里,有序的隐藏http方法过滤器
![](https://i-blog.csdnimg.cn/blog_migrate/dc5296b4cc381a053faf5238dd019689.png)
在yml配置mvc隐藏方法过滤设置为true
spring:
mvc:
hiddenmethod:
filter:
enabled: true
在进入HiddenHttpMethodFilter这里隐藏http方法过滤器
我们在index.html界面点击修改或删除方法,进入这里
看到DEFAULT_METHOD_PARAM = "_method"
默认方法参数就是_method,就是我们在html设置的那个_method
![](https://i-blog.csdnimg.cn/blog_migrate/e6067d69bdc446ca5c3343c9a185edfe.png)
在这里会判断action是不是post请求,并且参数是不是带着_method
并且存在DELETE,PUT,PATCH中
![](https://i-blog.csdnimg.cn/blog_migrate/8e381692a97b22ae7c84e259ee0bc1c1.png)
如果存在进入HttpMethodRequestWrapper把方法换成delete或者put
![](https://i-blog.csdnimg.cn/blog_migrate/f545575e6abdcc4ec1913776aab05c27.png)
![](https://i-blog.csdnimg.cn/blog_migrate/5ed30a87e54c282cb5fcd5969c58ec03.png)
直接访问就不报错了
![](https://i-blog.csdnimg.cn/blog_migrate/7c1b78aa5d62fb75d5af19cc93070985.png)
我们也可以改成自定义的名称,为_aaa
![](https://i-blog.csdnimg.cn/blog_migrate/9750d37ca3fe030ba8621c5dd010dff8.png)
在配置类,我们重写HiddenHttpMethodFilter 隐藏http方法过滤器
在setMethodParam 重新设置方法参数就可以了
@Configuration
public class TestConfig {
@Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
HiddenHttpMethodFilter hiddenHttpMethodFilter=new HiddenHttpMethodFilter();
hiddenHttpMethodFilter.setMethodParam("_aaa");
return hiddenHttpMethodFilter;
}
}
我们来看下请求映射原理
DispatcherServlet是所有请求的开始
进入这个类
![](https://i-blog.csdnimg.cn/blog_migrate/0337de35e9e285923eb98128558e2415.png)
然后进入FrameworkServlet
在这里我们就看到,他有doGet和doPost方法
![](https://i-blog.csdnimg.cn/blog_migrate/994bf1bee2db07d142b0230689aafaaa.png)
最终继承的还是HttpServlet
![](https://i-blog.csdnimg.cn/blog_migrate/2c06742eb922bb1155a5a8c74c472402.png)
我们可以看到doget和dopost都有一个processRequest 处理请求方法 我们点进去
![](https://i-blog.csdnimg.cn/blog_migrate/58df5e0bc5dd1ac05ffc57a4acfbe3bb.png)
他的核心是doService方法我们点进去,然后再进入方法的子类
![](https://i-blog.csdnimg.cn/blog_migrate/5d6639886f7ebb8c9e2f168865f55569.png)
![](https://i-blog.csdnimg.cn/blog_migrate/8d5349ee90089d3f35b1f886953558d4.png)
在访问下走找到doDispatch方法,每个请求都会调用, 点进去
![](https://i-blog.csdnimg.cn/blog_migrate/22b0afa4f74fa97a3ac53028390e26ae.png)
我们随便请求一个接口,然后进入getHandler方法
这里有多个handlerMappings 处理程序映射
![](https://i-blog.csdnimg.cn/blog_migrate/59f71f96c06ce2c5e630a107a12ea88c.png)
比如说这个欢迎页的配置规则是/,那么就进入index.html界面
![](https://i-blog.csdnimg.cn/blog_migrate/3b9a0cdca825db446913280c0bdf7914.png)
![](https://i-blog.csdnimg.cn/blog_migrate/d9e16e500bd0490d58325218bd1ae67c.png)
比如说RequestMappingHandlerMapping就是对应@RequestMapping这个注解和handler的映射规则
我们可以看下,在这个registry这里对应的这些方法,就是我们在控制层写的那些方法
![](https://i-blog.csdnimg.cn/blog_migrate/9aed1c42f2cab60e802eb8f541ac21fa.png)
我们进入getHandler方法
![](https://i-blog.csdnimg.cn/blog_migrate/9398cfdc007e72e908e8aba81c0e7b6e.png)
在进入getHandlerInternal方法
![](https://i-blog.csdnimg.cn/blog_migrate/4e4b76c753f389f3e26a99dd662d5409.png)
在进入lookupHandlerMethod方法
![](https://i-blog.csdnimg.cn/blog_migrate/0201a6ae718426aecb12c83ebee95b20.png)
当我们只有一个路径的时候,这时候匹配上了我们的接口
如果size大于1,那么会报错
![](https://i-blog.csdnimg.cn/blog_migrate/f140ae9db1db3e621fd3c2830f21b116.png)
所有的请求映射都在HandlerMapping中
springboot自动配置欢迎页的HandlerMapping,访问/就能访问到index.html
springboot自动配置了默认的RequestMappingHandlerMapping
请求进来,挨个尝试所有的HandlerMapping看是否有请求信息
如果有就找到这个请求对应的handler
如果没有就是下一个HandlerMapping
我们需要一些自定义的映射出来,我们也可以自己给容器中放HandlerMapping,自定义HandlerMapping
@GetMapping("/a")
public String a(@RequestParam("name")String name,@RequestParam("pwd")String pwd){
return name+pwd;
}
浏览器访问
http://localhost:8080/a?name=aa&pwd=asda
我们进入RequestMappingHandlerAdapter类的this.argumentResolvers != null这里
argumentResolvers参数解析器,可以看到我们请求的参数,在这里出现了
![](https://i-blog.csdnimg.cn/blog_migrate/0fcf20d90517f2fb61dbcf6c43748587.png)
在这里有一堆参数解析器,每一个不同的注解对应的参数,就是在这里解析的
![](https://i-blog.csdnimg.cn/blog_migrate/06e5030e971d735d314946585483f517.png)
然后进入ServletInvocableHandlerMethod的invokeAndHandle方法
在这一行Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
会跳转回我们的控制层
![](https://i-blog.csdnimg.cn/blog_migrate/0792f57c312e8be7f8227b4529510c87.png)
![](https://i-blog.csdnimg.cn/blog_migrate/473bdea0282d4311a1523e5e6929df9a.png)
执行完之后拿到返回结果
![](https://i-blog.csdnimg.cn/blog_migrate/9fc977a8f78e80e0b81a405944b6def2.png)
我们在进入invokeForRequest方法
在进入getMethodArgumentValues方法
所有的参数在下面遍历
![](https://i-blog.csdnimg.cn/blog_migrate/87a43d2fa1c86fa7ba1236265e4e08cd.png)
![](https://i-blog.csdnimg.cn/blog_migrate/1446756e1c01bfb44838eb752c4e4620.png)
我们直接这样写name="actor",获取的数据就是null
@Data
public class Actor {
private String name;
private Integer age;
}
@PostMapping("/bb")
public String bb(Actor actor){
System.out.println("===================="+actor);
return "bb";
}
<form action="/bb" method="post">
<input type="text" name="actor" value="你好,12">
<input type="submit" value="修改">
</form>
所以我们需要自定义转换器
package com.example.client.config;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.example.client.entity.Actor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class TestConfig {
@Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
HiddenHttpMethodFilter hiddenHttpMethodFilter=new HiddenHttpMethodFilter();
hiddenHttpMethodFilter.setMethodParam("_aaa");
return hiddenHttpMethodFilter;
}
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new Converter<String, Actor>() {
@Override
public Actor convert(String source) {
if(StringUtils.isNotEmpty(source)){
Actor actor=new Actor();
String[] split = source.split(",");
actor.setName(split[0]);
actor.setAge(Integer.parseInt(split[1]));
return actor;
}
return null;
}
});
}
};
}
}
当我们配置自定义转换器之后,这个对象就有数据了
![](https://i-blog.csdnimg.cn/blog_migrate/105c24965e6753d8680ed20e1800e3d0.png)
进入ModelAttributeMethodProcessor类 模型属性方法处理器
WebDataBinder web数据绑定这里会看到String已经转换成了实体类
![](https://i-blog.csdnimg.cn/blog_migrate/20d4b2a8bbb44a872a6de6118de3e015.png)
![](https://i-blog.csdnimg.cn/blog_migrate/38fc6c5aabf564660e30c02648793f76.png)
我们在来看下响应处理
在spring-boot-starter-web里面引入了spring-boot-starter-json
![](https://i-blog.csdnimg.cn/blog_migrate/b19413aad3471da74bf8baa144e1f9c5.png)
在spring-boot-starter-json里面引入了jackson
![](https://i-blog.csdnimg.cn/blog_migrate/283974616f2d2d9f09c4f2b50a83d156.png)
所以我们在控制层加入@RestController或者在方法上面@ResponseBody 就返回了json类型的数据
@Controller
public class BlueController {
@ResponseBody
@GetMapping("/bb")
public Actor bb(){
Actor actor=new Actor();
actor.setAge(100);
actor.setName("aaaaaaaa");
return actor;
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/a799a8d4f20e855a85370717e99da74f.png)
我们来分析下源码,进入RequestMappingHandlerAdapter
找到this.returnValueHandlers != null 返回值处理程序
可以看到有这么多的返回值处理程序
![](https://i-blog.csdnimg.cn/blog_migrate/1f7114f9afddb58708b59c9b9952c07d.png)
我们可以看到在这里,他就拿到的返回结果就是对象
![](https://i-blog.csdnimg.cn/blog_migrate/cfabd4dde8b88cb51c2c1842076eb634.png)
然后进入HandlerMethodReturnValueHandlerComposite类的handleReturnValue方法
然后进入selectHandler方法
![](https://i-blog.csdnimg.cn/blog_migrate/a13e5c681145742630dc1c4298b0c5ee.png)
找到合适的处理程序返回,因为我们的方法上面加了ResponseBody注解
所以这里匹配到了RequestResponseBodyMethodProcessor
请求响应主体方法处理器
![](https://i-blog.csdnimg.cn/blog_migrate/fb9575d1e5661d473f30801853b0dc61.png)
![](https://i-blog.csdnimg.cn/blog_migrate/ced76c0248666080e63b779f9258f721.png)
我们在进入RequestResponseBodyMethodProcessor类的handleReturnValue方法
![](https://i-blog.csdnimg.cn/blog_migrate/6f801b172015383abb19a268fc4d324a.png)
![](https://i-blog.csdnimg.cn/blog_migrate/dcef069bbfd1673359dd87d626a89d40.png)
我们在进入writeWithMessageConverters方法 使用消息转换器写入
![](https://i-blog.csdnimg.cn/blog_migrate/198032908286f094a54d8015303877fd.png)
我们首先看下内容协商,就是这一部分数据
![](https://i-blog.csdnimg.cn/blog_migrate/5dc807facc2ae5f866490abae411e11f.png)
可以看到这个getAcceptableMediaTypes就是获取内容协商的数据
获取可接受的媒体类型
![](https://i-blog.csdnimg.cn/blog_migrate/fe0dc6a54ada9b019f3ed2b3682b5493.png)
getProducibleMediaTypes 获取可生产媒体类型
![](https://i-blog.csdnimg.cn/blog_migrate/15e25d7699b96f7c6b7f96753d898531.png)
最终过滤在mediaTypesToUse 要使用的媒体类型
![](https://i-blog.csdnimg.cn/blog_migrate/01e593a671e2742f87070dd8805704ea.png)
然后我们找到HttpMessageConverter http消息转换器
![](https://i-blog.csdnimg.cn/blog_migrate/64e47300d21b8807cbfe867de33bbe3f.png)
HttpMessageConverter 有canRead 要读取的媒体类型
和canWrite要写入的媒体类型
就是看我们那个对象能不能转成json,或者json转成对象
![](https://i-blog.csdnimg.cn/blog_migrate/6f906f790f3758493a987b7aa22d2ae4.png)
我们可以看到MappingJackson2HttpMessageConverter
映射json http消息转换器,这里获取到了body数据
在这个转换器中可以把对象转换成json,也可以把json转成对象
![](https://i-blog.csdnimg.cn/blog_migrate/06b5e1f2f0f7f98f4edd00e4b545d202.png)
在这一行写入数据
![](https://i-blog.csdnimg.cn/blog_migrate/c733bbba440bf06906078555b1ee3309.png)
![](https://i-blog.csdnimg.cn/blog_migrate/d14210d5b262212ad57cbca3baa7d6ab.png)
内容协商就是根据客户端接收能力不同,返回不同媒体类型的数据
比如说返回Json和xml
在pom.xml引入
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
我们可以看到在次查询的时候,内容协商优先返回xml的数据
![](https://i-blog.csdnimg.cn/blog_migrate/bbc32a9415c928d3c9b656bd9f9fe966.png)
我们在postman看到却返回的是json,为什么呢?
因为Accept 返回的*
![](https://i-blog.csdnimg.cn/blog_migrate/55eafbadf1f3b3ccf9a83b4e2f6bf255.png)
我们可以手动修改内容协商Accept的数据
当我修改为xml的时候他就变成了xml的数据
![](https://i-blog.csdnimg.cn/blog_migrate/38bb2e4e0f817a261d801795a9388a40.png)
当我们变成json的时候 返回的就是json的数据
![](https://i-blog.csdnimg.cn/blog_migrate/62f6ca84780965730bd4f4b86c269a05.png)
进入HeaderContentNegotiationStrategy类 请求头内容谈判策略
在这里获取的就是请求头传过来的Accept
得到媒体类型
![](https://i-blog.csdnimg.cn/blog_migrate/f76109d947a575a0c7505ec6594401f4.png)
循环遍历所有的MessageConverter,看谁支持操作这个对象
找到支持操作对象的converter,把converter支持的媒体类型统计出来
客户端需要application/xml,服务端就支持json/xml的
在这个for循环里面匹配xml需要的,最终返回
![](https://i-blog.csdnimg.cn/blog_migrate/0cda381ea72b9e6c9a38d9401beba838.png)
我们可以在yml文件开启内容协商,在format后面输入不同的类型,就返回不同的类型
spring:
mvc:
contentnegotiation:
favor-parameter: true
http://localhost:8080/bb?format=xml
![](https://i-blog.csdnimg.cn/blog_migrate/77864b8c0f31f4c7c1cfb1c85efaea13.png)
http://localhost:8080/bb?format=json
![](https://i-blog.csdnimg.cn/blog_migrate/3357fcb546cacf762136f5781d1944b7.png)
我们进入ContentNegotiationManager 内容协商管理器这里 可以看到strategy 策略中
拿到了json的媒体类型
![](https://i-blog.csdnimg.cn/blog_migrate/6de41735b4f880d62dcaa725a5efec49.png)
![](https://i-blog.csdnimg.cn/blog_migrate/900b858d69ed3a3df4b3c0eeac77d54a.png)
我们可以自定义转换器
package com.example.client.config;
import com.example.client.entity.Actor;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
public class MyConverter implements HttpMessageConverter<Actor> {
@Override
public boolean canRead(Class<?> clazz, MediaType mediaType) {
return false;
}
@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
//如果是这个对象 那么可以写入
return clazz.isAssignableFrom(Actor.class);
}
//服务器要统计所有messageconverter都能写出那些内容类型
@Override
public List<MediaType> getSupportedMediaTypes() {
//自定义abc媒体类型
return MediaType.parseMediaTypes("application/abc");
}
@Override
public Actor read(Class<? extends Actor> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
return null;
}
@Override
public void write(Actor actor, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
//自定义协议数据的写出
String data="123456";
OutputStream outputStream=outputMessage.getBody();
outputStream.write(data.getBytes());
}
}
@Configuration
public class TestConfig {
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
//扩展消息转换器
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
//添加自定义的转换器
converters.add(new MyConverter());
}
};
}
}
输入内容协商application/abc,返回我们自定义转换器的123456
![](https://i-blog.csdnimg.cn/blog_migrate/67e7ce6335af4e5a0decc38fd66261c0.png)
我们也可以在浏览器自定义内容协商的参数和内容
package com.example.client.config;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.example.client.entity.Actor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.convert.converter.Converter;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
import org.springframework.web.accept.ParameterContentNegotiationStrategy;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.*;
@Configuration
public class TestConfig {
@Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
HiddenHttpMethodFilter hiddenHttpMethodFilter=new HiddenHttpMethodFilter();
hiddenHttpMethodFilter.setMethodParam("_aaa");
return hiddenHttpMethodFilter;
}
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
Map<String, MediaType> mediaTypes=new HashMap<>();
mediaTypes.put("json",MediaType.APPLICATION_JSON);
mediaTypes.put("xml",MediaType.APPLICATION_XML);
mediaTypes.put("abc",MediaType.parseMediaType("application/abc"));
//指定支持解析那些参数对应的那些媒体类型
ParameterContentNegotiationStrategy strategy=new ParameterContentNegotiationStrategy(mediaTypes);
//自定义参数类型
strategy.setParameterName("my");
//HeaderContentNegotiationStrategy strategy1=new HeaderContentNegotiationStrategy();
configurer.strategies(Arrays.asList(strategy));
}
//扩展消息转换器
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
//添加自定义的转换器
converters.add(new MyConverter());
}
};
}
}
http://localhost:8080/bb?my=abc
![](https://i-blog.csdnimg.cn/blog_migrate/606f6dbdf04c2c3c99ffdda984c4b560.png)
但是我们在postman 输入xml,却没有返回xml信息
![](https://i-blog.csdnimg.cn/blog_migrate/a6f27541d2f02620ac87df4d96bc2087.png)
需要添加请求头的内容协商策略
![](https://i-blog.csdnimg.cn/blog_migrate/a343e629be73ce3c75dbcc022bc3a46a.png)
可以看到xml出来了
![](https://i-blog.csdnimg.cn/blog_migrate/1e90c70b19e24bbfc7b2ccf97e5ff098.png)
自定义参数my对应的就是这里的ParameterContentNegotiationStrategy类的format
![](https://i-blog.csdnimg.cn/blog_migrate/9e09112f23166bee47b65a6c176b7595.png)