SpringFramework原本使用的是如下图显示的xml配置,这种xml配置曾经让很多程序员头疼,由于一个字符不对而产生一大堆不知道原因的错误,尤其是xmlns种约束文件的引入更是繁杂。
现在spring boot使用 WebMvcConfigurer来配置,其源码如下:
public interface WebMvcConfigurer {
//帮助配置HandlerMappings路径匹配选项,
default void configurePathMatch(PathMatchConfigurer configurer) {
}
//配置内容协商选项
default void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
}
//配置异步请求处理选项.
default void configureAsyncSupport(AsyncSupportConfigurer configurer) {
}
//配置处理程序以通过转发到来委托未处理的请求
default void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
}
//配置自定义转换器
default void addFormatters(FormatterRegistry registry) {
}
//配置拦截器
default void addInterceptors(InterceptorRegistry registry) {
}
//配置静态资源处理器
default void addResourceHandlers(ResourceHandlerRegistry registry) {
}
//配置跨源请求处理
default void addCorsMappings(CorsRegistry registry) {
}
/** 预先配置了响应的简单自动化控制器 */
default void addViewControllers(ViewControllerRegistry registry) {
}
/** 配置视图解析器 */
default void configureViewResolvers(ViewResolverRegistry registry) {
}
/** 添加解析器以支持自定义控制器方法参数类型 */
default void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
}
/** 添加处理程序以支持自定义控制器方法返回值类型。 */
default void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers){
}
// 将HttpMessageConverters配置为用于读取或写入请求或响应的正文。
default void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
}
// 一个钩子,用于在配置后扩展或修改转换器列表。 例如,这对于允许注册默认转换器然后通过此方法
//插入自定义转换器可能很有用。
default void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
}
/** 配置异常解析器 */
default void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
}
default void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
}
//提供自定义的{@link Validator},而不是默认创建的{@link
@Nullable
default Validator getValidator() {
return null;
}
@Nullable
default MessageCodesResolver getMessageCodesResolver() {
return null;
}
}
上述 WebMvcConfigurer表面上是接口,实质上是有default修饰的抽象类,所以实现的类不必要全部实现其方法。
(1)配置页面跳转 addViewControllers
由于spring mvc拦截了所有请求,以前需要专门配置controller来跳转到某个页面,现在只需要配置行代码就行
@Override
public void addViewControllers(ViewControllerRegistry registry) {
WebMvcConfigurer.super.addViewControllers(registry);
registry.addViewController("/welcome").setViewName("welcome");
registry.addViewController("/welcome2").setViewName("welcome2");
}
(2) 配置静态资源文件--addResourceHandlers
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
WebMvcConfigurer.super.addResourceHandlers(registry); registry.addResourceHandler("/teststatic/**").addResourceLocations("classpath:/teststatic/");
registry.addResourceHandler("/teststatic2/**").addResourceLocations("classpath:/teststatic2/");
}
(3)A)配置自定义转换器--addFormatters
首先定义一个类实现一个Formatter接口,这个接口将字符串转化为T类型,这里是转化为Date类型
public class StringToDateFormatter implements Formatter<Date> {
private String datePattern = "yyyy年MM月dd日";
private SimpleDateFormat simpleDateFormat;
@Override
public String print(Date arg0, Locale arg1) {
// TODO Auto-generated method stub
return new SimpleDateFormat().format(arg0);
}
@Override
public Date parse(String arg0, Locale arg1) throws ParseException {
System.out.println("from StringToDateFormatter ");
simpleDateFormat = new SimpleDateFormat(datePattern);
return simpleDateFormat.parse(arg0);
}
}
再举一个将字符串转换为User对象的例子
public class StringToUserFormatter implements Formatter<User>{
@Override
public String print(User arg0, Locale arg1) {
return arg0.toString();
}
@Override
public User parse(String arg0, Locale arg1) throws ParseException {
//name=zhangsan;age=20
User us=new User();
String[] str=arg0.split(";");
System.out.println("formatter "+str.toString());
String[] name=str[0].split(":");
String[] age=str[1].split(":");
us.setName(name[1]);
us.setAge(Integer.parseInt(age[1]));
return us;
}
}
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addFormatter(new StringToDateFormatter());
}
B)配置自定义转换器--Converter,这个转换类实现了T--->T的转换
先通过继承Converter来实现一个自定义转换类
public class StringToDateConverter implements Converter<String, Date> {
private static ThreadLocal<SimpleDateFormat[]> formats = new ThreadLocal<SimpleDateFormat[]>() {
protected SimpleDateFormat[] initialValue() {
return new SimpleDateFormat[]{
new SimpleDateFormat("yyyy-MM"),
new SimpleDateFormat("yyyy-MM-dd"),
new SimpleDateFormat("yyyy-MM-dd HH"),
new SimpleDateFormat("yyyy-MM-dd HH:mm"),
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
};
}
};
@Override
public Date convert(String source) {
if (source == null || source.trim().equals("")) {
return null;
}
System.out.println("from StringToDateConverter ");
Date result = null;
String originalValue = source.trim();
if (source.matches("^\\d{4}-\\d{1,2}$")) {
return parseDate(source, formats.get()[0]);
} else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2}$")) {
return parseDate(source, formats.get()[1]);
} else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}$")) {
return parseDate(source, formats.get()[2]);
} else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}$")) {
return parseDate(source, formats.get()[3]);
} else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}:\\d{1,2}$")) {
return parseDate(source, formats.get()[4]);
} else if (originalValue.matches("^\\d{1,13}$")) {
try {
long timeStamp = Long.parseLong(originalValue);
if (originalValue.length() > 10) {
result = new Date(timeStamp);
} else {
result = new Date(1000L * timeStamp);
}
} catch (Exception e) {
result = null;
e.printStackTrace();
}
} else {
result = null;
}
return result;
}
public Date parseDate(String dateStr, DateFormat dateFormat) {
Date date = null;
try {
date = dateFormat.parse(dateStr);
} catch (Exception e) {
}
return date;
}
}
然后注册上面三个自定义转换器(包含了前面的配置),编写一个WebMvcConfig 类继承WebMvcConfigurer 接口,然后编写如下的代码,其中@Configuration表示这是一个配置类;@Import({WebMvcAutoConfiguration.class})导入了系统提供的一些自动配置,比如对类路径下/static、/public、/resources这些文件夹的定义。WebMvcConfig代码如下
@Configuration
@Import({WebMvcAutoConfiguration.class})
class WebMvcConfig implements WebMvcConfigurer {
//方法1:以@Bean注入自定义转换器
/* @Bean
public StringToDateConverter stringToDateConverter() {
return new StringToDateConverter();
}*/
/*
@Bean
public StringToUserFormatter userToFormatter() {
return new StringToUserFormatter();
}*/
@Override
public void addFormatters(FormatterRegistry registry) {
//方法2:用FormatterRegistry注册自定义转换器
registry.addFormatter(new StringToDateFormatter());
registry.addFormatter(new StringToUserFormatter());
registry.addConverter(new StringToDateConverter());
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// TODO Auto-generated method stub
WebMvcConfigurer.super.addResourceHandlers(registry);
registry.addResourceHandler("/teststatic/**").addResourceLocations("classpath:/teststatic/");
registry.addResourceHandler("/teststatic2/**").addResourceLocations("classpath:/teststatic2/");
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// TODO Auto-generated method stub
WebMvcConfigurer.super.addViewControllers(registry);
registry.addViewController("/welcome").setViewName("welcome");
registry.addViewController("/welcome2").setViewName("welcome2");
}
}
也可以用@Bean注解来注册 ,如:
@Bean
public StringToDateConverter stringToDateConverter() {
return new StringToDateConverter();
}
编写controller
@RestController
public class TestConvertController {
@RequestMapping("/testConvert")
String handler(Date date) {
System.err.println("the followings are the output");
System.out.println("date is:" + date);
Gson gson = new Gson();
return gson.toJson(date);
}
// test
@RequestMapping("/testUser")
String handler(@RequestParam("param") User user) {
String userJson = "{'id':22,'name':'王五','age':26}";
Gson gson = new Gson();
String jsonstr = gson.toJson(user);
User user2 = gson.fromJson(userJson, User.class);
System.err.println("the object output from gson:" + user2.toString());
System.out.println("json string by use gson:" + jsonstr);
System.out.println("user is:" + user.toString());
return jsonstr;
}
}
上述代码中国采用了google的Gson实现json字符串和java对象之间的相互转换(Gson非常好用极力推荐),需要在pom.xml中添加如下依赖:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
测试welcome.html页面
<body>
welcome to this page
<br/>
<a href="/testConvert?date=2020-10-22 08:30:05">测试StringToDateConverter</a><br/>
<a href="/testConvert?date=2020年10月1日">测试StringToDateFormatter</a><br/>
<a href="/testUser?param=name:zhangsan;age:30">测试StringToUserFormatter</a><br/>
</body>
测试结果:
1、浏览器中输入:http://localhost:8080/welcome,有如下结果,说明配置视图控制器起作用。
2、 浏览器中输入:http://localhost:8080/teststatic/test.xml,有下图所示的结果,说明配置静态资源起作用
3、测试StringToDateFormatter和StringToDateConverter
点击第一个链接输出:
点击第二个链接输出为null,说明 Converter优先级高于Formatter
在 TestConvertController 注释掉“registry.addConverter(new StringToDateConverter()); ” 后,再点击第二个链接输出:
测试StringToUserFormatter
点击第三个链接输出: