SpringBoot整合Spring MVC
使用官方提供的spring-boot-starter-web
启动器,默认配好了SpringMVC全套功能,该启动器依赖了spring-boot-starter
根启动器,以及spring-boot-starter-json
JSON序列化和spring-boot-starter-tomcat
web容器
静态资源访问
- 静态资源访问:默认开启;可使用
spring.web.resources.add-mappings
配置项进行修改 - 资源目录:默认静态资源目录存约定的是
/static
,也可以是/public
或/resources
或/META-INF/resources
;可使用spring.web.resources.static-locations
配置项进行更改 - 访问前缀:默认静态资源访问URL无前缀;可使用
spring.mvc.static-path-pattern
配置项进行更改 - 默认首页:默认欢迎页是从静态资源目录中寻找
index.html
,若找不到则会去模板目录中找index
模板 - 网站图标:默认
favicon.ico
图标在静态资源目录的根下 - 静态资源缓存:可使用
spring.web.resources.cache.period
配置静态资源的缓存时间,单位秒
特殊的请求参数
REST风格表单提交
默认关闭,开启后可使用在表单提交中使用一个name
值为_method
,value
值为一个Http请求动词的一个表单项再进行POST提交(一般使用隐藏表单);可使用spring.mvc.hiddenmethod.filter.enabled
配置项进行修改
html代码:
<h1>REST风格测试:</h1>
<form action="/get" method="get">
<input type="submit" value="get">
</form>
<form action="/post" method="post">
<input type="submit" value="post">
</form>
<form action="/put" method="post">
<input type="submit" value="put">
<input type="hidden" name="_method" value="put">
</form>
<form action="/delete" method="post">
<input type="submit" value="delete">
<input type="hidden" name="_method" value="delete">
</form>
控制器代码:
import org.springframework.web.bind.annotation.*;
@RestController
public class MyController {
@GetMapping("/get")
public String get() {
return "get";
}
@PostMapping("/post")
public String post() {
return "post";
}
@PutMapping("/put")
public String put() {
return "put";
}
@DeleteMapping("/delete")
public String delete() {
return "delete";
}
}
矩阵变量
默认关闭,开启后可在请求路径使用分号分隔添加矩阵变量,相当于是路径变量又携带了变量,就像这样:
- 多个矩阵变量再使用分号进行分隔:
/path;name=zhangsan;message=message
- 多个矩阵变量的值使用逗号进行分隔:
/path;message=message1,message2
- 多个路径变量携带矩阵变量直接反斜杠分隔:
/path1;message=message1/path2/path3;message=message2
可自定义配置类,进行下面配置,开启该功能
@Configuration
public class MyConfiguration {
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() { //创建自定义WebMvc配置类对象添加到容器中,以覆盖原有的
@Override
public void configurePathMatch(PathMatchConfigurer configurer) { //重写路径解析方法
UrlPathHelper urlPathHelper = new UrlPathHelper(); //创建URL路径解析器对象
urlPathHelper.setRemoveSemicolonContent(false); //设置为不删除分号
configurer.setUrlPathHelper(urlPathHelper); //将该URL路径解析对象设置到路径匹配器中
}
};
}
}
使用下面代码进行测试
@RestController
public class Main {
@GetMapping("/test1/{path}")
//测试路径:/test1/path;name=zhangsan;message=message
public String test1(@MatrixVariable("name") String name,
@MatrixVariable("message") String message) {
return name + ":" + message;
}
@GetMapping("/test2/{path}")
//测试路径:/test2/path;message=message1,message2
public String test2(@MatrixVariable("message") List<String> message) {
return message.toString();
}
@GetMapping("/test3/{path1}/path2/{path3}")
//测试路径:/test3/path1;message=message1/path2/path3;message=message2
public String test3(@MatrixVariable(pathVar = "path1", value = "message") String message1, //pathVar指定是那个路径变量
@MatrixVariable(pathVar = "path2", value = "message") String message2) {
return message1 + ":" + message2;
}
}
拦截器
同时设置多个拦截器的执行顺序为,请求时按照配置顺序进行执行,响应时按照配置逆序执行,若期间某个拦截器出现了异常,则后续拦截器不会被执行,而是直接逆序执行
afterCompletion()
方法,而不会执行postHandle()
方法
为SpringMVC添加拦截器,向SpringMVC中添加HandlerInterceptor
实例,重写preHandle()
、postHandle()
和afterCompletion()
方法,这三个方法都是默认方法
preHandle()
:前置执行处理,进入控制器之前执行,若返回false则后续不会执行postHandle()
:目标资源已被SpringMVC进行处理,即控制器方法被执行完(return了之后)后执行afterCompletion()
:响应文本已产生,即响应HTML字符串或JSON字符串生成后执行
自定义的拦截器类如下:
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("前置处理");
return true; //返回true后续会接着执行
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("控制器方法执行完毕");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("响应文本已经生成");
}
}
可自定义配置类,进行下面配置:
@Configuration
public class MyConfiguration {
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() { //创建自定义WebMvc配置类对象添加到容器中,以覆盖原有的
@Override
public void addInterceptors(InterceptorRegistry registry) { //重写添加拦截器方法,为SpringMVC添加拦截器
registry.addInterceptor(new MyInterceptor()) //添加自己的拦截器对象,可链式调用
.addPathPatterns("/**") //要拦截的路径,拦截全部请求,静态资源也会被拦截器拦截到
.excludePathPatterns("*.js", "*.css", "*.png"); //不拦截的路径
}
};
}
}
错误处理
默认情况下,SpringBoot提供了/error
请求路径,用于处理所有为捕获的异常,浏览器客户端直接请求则返回HTML错误页,否则返回JSON格式错误数据
自定义错误页
错误页面可自定义,放在静态资源目录或模板文件目录下的error
目录下,文件名如下:模板文件目录下可以是对应的模板引擎后缀名
404.html
或500.html
:精确匹配到404和500错误4xx.html
和5xx.html
:无法精确匹配到404或500错误,会响应概括性的页面4xx或5xx错误
全局异常处理器
在异常处理器类上使用@ControllerAdvice
注解,异常处理器方法上使用@ExceptionHandler
注解指定要处理的异常,之后在控制器中抛出异常时,异常处理器中匹配的方法就会执行
@ControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(value = {Exception.class}) //数组形式,可指定多个异常
@ResponseBody
public String error(Exception e) { //与控制器中的方法用法一致
return "出现异常" + e.getMessage();
}
}
自定义异常
使用@ResponseStatus
来指定该异常Http响应状态码和响应信息,当抛出该异常给/error
时会以定义好的Http状态码进行返回
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "404 Not Found")
public class MyException extends RuntimeException {
public MyException(String message) {
super(message);
}
}
嵌入式容器
若导入多个或没有导入Web容器时,就会抛出异常
服务器常用配置项如下
属性名 | 默认值 | 描述 |
---|---|---|
server.servlet.session.timeout |
30m | 默认的session超时时间 |
server.port |
8080 | 服务器监听端口 |
server.tomcat.accesslog.directory |
logs | 日志文件的目录。可以是绝对路径,也可以相对于Tomcat路径 |
server.tomcat.accesslog.enabled |
false | 启用访问日志 |
导入web开发场景包,SpringBoot就会创建一个web版本是IOC容器ServletWebServApplicationContext
,该容器启动时会使用ServletWebServerFactory
接口的实例来创建Web容器,默认导入的是tomcat,若想使用其他服务器,就需要先将默认导入的tomcat排除,在导入想要导入的服务器即可,具体做法如下
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions> <!--要排除的依赖-->
<exclusion> <!--排除tomcat-->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency> <!--导入jetty服务器-->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
常见问题与解决
自定义格式转化器
为SpringMVC添加自定义格式转化器,向SpringMVC中添加Converter
实例,重写convert()
方法,可用于转化任意类型,可自定义配置类,进行下面配置
@Configuration
public class MyConfiguration {
@Bean
public WebMvcConfigurer webMvcConfigurer() { //并为覆盖所有的配置,想要全面接管SpringMVC的配置就需要在配置类上添加@EnableWebMvc注解
return new WebMvcConfigurer() { //创建自定义WebMvc配置类对象添加到容器中,以覆盖原有的,其实是合并
@Override
public void addFormatters(FormatterRegistry registry) { //重写添加格式转换器方法,为SpringMVC添加格式转换器
registry.addConverter(new Converter<String, Date>() { //向SpringMVC中增加转换器实例,泛型表示将String转化为Date类型
@Override
public Date convert(String string) { //实现格式转化方法
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
try {
return simpleDateFormat.parse(string); //返回转化后的日期对象
} catch (ParseException e) {
return null; //解析错误返回null
}
}
});
}
};
}
}
使用下面代码进行测试
import org.springframework.web.bind.annotation.*;
import java.util.Date;
@RestController
public class Main {
@GetMapping("/test")
//测试路径:/test?date=1970-01-01 00:00:00.000
public String test1(@RequestParam Date date) {
return date.toString();
}
}
文件上传
默认开启,但是限制大小,可通过下面配置项进行更改
属性名 | 默认值 | 描述 |
---|---|---|
spring.servlet.multipart.max-file-size |
1MB |
单个文件的最大值 |
spring.servlet.multipart.max-request-size |
10MB |
上传文件总的最大值 |
控制器代码
@PostMapping("/upload")
@ResponseBody
public String upload(@RequestParam("file") MultipartFile file, HttpServletRequest request) { //若是多文件上传使用List集合接收参数即可
String uploadDir = "upload"; //上传文件目录
if (!file.isEmpty()) { //若有上传
String filename = file.getOriginalFilename(); //获取原来的文件名
String path = request.getServletContext().getRealPath(uploadDir); //获取上传目录的的绝对路径
int idx = filename.lastIndexOf(".");
String exName = filename.substring(idx); //截取文件后缀名
filename = UUID.randomUUID().toString().replace("-", "") + exName; //生成随机UUID拼接后缀名,修改文件名避免上传重名文件被覆盖
try {
file.transferTo(new File(path + File.separator + filename)); //文件转存
String fileRPath = request.getServletContext().getContextPath() + "/" + uploadDir + "/" + filename; //拼接文件符合src的相对路径
return "上传成功,URL地址为:" + fileRPath;
} catch (IOException e) {
e.printStackTrace();
return "上传失败";
}
}
return "未上传";
}
用于上传的测试页面
<form action="${pageContext.request.contextPath}/upload" method="post" enctype="multipart/form-data">
<!-- 必须指定enctype为multipart/form-data -->
<input type="text" name="name">
<input type="file" name="file">
<input type="submit" value="上传">
</form>
模板引擎
freemarker
freemarker模板的具体语法可从官方文档中查看,在pom.xml中引入freemarker的启动器即可
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
freemarker常用配置项如下:
属性名 | 默认值 | 描述 |
---|---|---|
spring.freemarker.suffix |
.ftlh |
模板文件后缀 |
spring.freemarker.template-loader-path |
[classpath:/templates/] |
模板文件所在目录 |
spring.freemarker.content-type |
text/html |
写入HTTP响应的Content-Type值 |
spring.freemarker.charset |
UTF-8 |
模板文件字符编码 |
spring.freemarker.cache |
false |
是否启用模板缓存 |
thymeleaf
thymeleaf模板的具体语法可从官方文档中查看,在最外层的<html>
标签上添加xmlns:th="http://www.thymeleaf.org"
名称空间即可有代码提示,在pom.xml中引入freemarker的启动器即可
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
thymeleaf常用配置项如下:
属性名 | 默认值 | 描述 |
---|---|---|
spring.thymeleaf.suffix |
.html |
模板文件后缀 |
spring.thymeleaf.prefix |
classpath:/templates/ |
模板文件所在目录 |
spring.thymeleaf.encoding |
UTF-8 |
模板文件字符编码 |
spring.thymeleaf.servlet.content-type |
text/html |
写入HTTP响应的Content-Type值 |
spring.thymeleaf.cache |
true |
是否启用模板缓存 |
jsp
SpringBoot官方已经不推荐使用JSP了,确实操作起来也比较麻烦
<dependency> <!--内嵌的tomcat对jsp的支持模块 -->
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
<dependency> <!-- 对jstl的支持 -->
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
在配置文件下需要再进行视图引擎的配置,配置项如下:再手动创建/src/main/webapp
目录,将其设置为源文件目录,再此目录中进行jsp页面的编写
属性名 | 描述 | 设置为 |
---|---|---|
spring.mvc.view.suffix |
模板文件后缀 | /WEB-INF/view (对应的是webapp 目录) |
spring.mvc.view.prefix |
模板文件所在目录 | .jsp |
原生组件注入
所谓原生组件是Servlet、Filter、Listener这些组件,这些组件的定义和原生Web开发一样,使用@WebServlet
、@WebFilter
、@WebListener
注解进行标识
原生组件不会经过SpringMVC的拦截器等专属SpringMVC的组件
不同的是之前是在web.xml
中进行配置的,在SpringBoot中有两种方式可以注册这些组件
- 在SpringBoot启动类中配置
ServletComponentScan
注解指定原生组件的包,进行原生组件的包扫描 - 自定义配置类,向容器中注入
ServletRegistrationBean
、FilterRegistrationBean
、ServletListenerRegistrationBean
的实例
@Configuration
public class MyConfiguration {
@Bean
public ServletRegistrationBean servletBean() {
Servlet servlet = new MyServlet(); //自定义的原生组件对象
return new ServletRegistrationBean(servlet, "/url"); //绑定的url访问路径
}
@Bean
public FilterRegistrationBean filterBean() {
Filter filter = new MyFilter(); //自定义的原生组件对象
//FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(filter, servletBean()); //可绑定servlet
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(filter); //也可单独进行设置
filterRegistrationBean.setUrlPatterns(Arrays.asList("/url")); //绑定的url访问路径
return filterRegistrationBean;
}
@Bean
public ServletListenerRegistrationBean listenerBean() {
ServletContextListener servletContextListener = new MyServletContextListener(); //自定义的原生组件对象
return new ServletListenerRegistrationBean(servletContextListener); //注册监听器
}
}
全局跨域请求
为SpringMVC添加全局跨域请求配置,向SpringMVC中添加跨域请求映射,可自定义配置类,进行下面配置
@Configuration
public class MyConfiguration {
@Bean
public WebMvcConfigurer webMvcConfigurer() { //并为覆盖所有的配置
return new WebMvcConfigurer() { //创建自定义WebMvc配置类对象添加到容器中,以覆盖原有的
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/prefix/**") // 对 /prefix 父路径下的所有路径都可以跨域访问
.allowedOrigins("http://www.baidu.com"); //允许所有域名
}
};
}
}
Comments NOTHING