SpringMVC
SpringMVC
ref:
.
什么是MVC
MVC是一种软件架构的思想,将软件按照模型、视图、控制器来划分
M: Model, 模型层,指工程中的avaBean,作用是处理数据
JavaBean分为两类:
一类称为实体类Bean:专存储业务数据的,如Student、 User等
一类称为业务处理Bean:指Service或Dao对象,专用于处理业务逻辑和数据访问。
V: View, 视图层,指工程中的html或jsp等页面,作用是与用户进行交互,展示数据
C: Controller, 控制层,指工程中的servlet,作用是接收请求和响应浏览器
MVC的工作流程:
用户通过视图层发送请求到服务器,在服务器中请求被Controller接收, Controller调用相应的Mode层处理请求,处理完毕将结果返回到Controller, Controller再根据请求处理的结果找到相应的View视图,渲染数据后最终响应给浏览器
什么是SpringMVC
SpringMVC是Spring的一个后续产品,是Spring的一个子项目
SpringMVC是Spring为表述层开发提供的一整套完备的解决方案。在表述层框架历经Strust、WebWork、Strust2等诸多产品的历代更迭之后,目前业界普遍选择了SpringMVC作为Java EE项目表述层开发的首选方案。
注: 三层架构分为表述层(或表示层)、业务逻辑层、数据访问层,表述层表示前台页面和后台servlet
SpringMVC的特点
Spring 家族原生产品,与IOC容器等基础设施无缝对接
基于原生的Servlet, 通过了功能强大的前端控制器DispatcherServlet,对请求和响应进行统一处理
表述层各细分领域需要解决的问题全方位覆盖,提供全面解决方案
代码清新简洁,大幅度提升开发效率
内部组件化程度高,可插拔式组件即插即用,想要什么功能配置相应组件即可
性能卓著,尤其适合现代大型、超大型互联网项目要求
开始
- 创建maven工程
-
打包方式为war
<packaging>war</packaging>
- 引入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.20</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- thymeleaf和spring5 整合包-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.11.RELEASE</version>
</dependency>
- 请求拦截
符合规则的请求都被Spring的DispatcherServlet处理
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
/与/*有区别,/无法处理.jsp,因为tomcat的web.xml配置过jsp的请求拦截,/ *可以拦截所有请求包括jsp
SpringMVC的配置文件默认的位置和名称:
位置: WEB- INF下
名称: <servlet - name>-servlet . xml,当前配置下的配置文件名为SpringMVC- servlet . xml
自定义配置文件位置、服务器启动时初始化DispatcherServlet
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:SpringMVC-servlet.xml</param-value>
</init-param>
<!-- DispatcherServlet load on startup 服务器启动时初始化-->
<load-on-startup>1</load-on-startup>
</servlet>
...
创建配置文件SpringMVC- servlet . xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 开启组件扫描-->
<context:component-scan base-package="com.learn"/>
<!-- thymeleaf视图解析器-->
<bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="order" value="1"/>
<property name="characterEncoding" value="UTF-8"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<!--视图前缀-->
<property name="prefix" value="/templates/"/>
<!--视图后缀-->
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8"/>
</bean>
</property>
</bean>
</property>
</bean>
</beans>
处理静态资源,例如html、js、 css、 jpg,若只设置该标签,则只能访问静态资源,其他请求则无法访问,此时必须设置<mvc:annotation-driven/>
解决问题
<mvc:default-servlet-handler/>
<mvc:annotation-driven>
<mvc:message-converters>
<!--处理响应中文乱码-->
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="defaultCharset" value="UTF-8"/>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
配置tomcat服务器
编写页面
web-app/templates/index.hml
编写HelloController
@Controller
public class HelloController {
@RequestMapping("/")
public String helloIndex(){
// 返回逻辑视图
return "index";
}
}
运行服务器,访问tomcat配置的URL,如http://localhost:8080/
@RequestMapping
概要
-
作用:将请求和处理请求的控制器方法关联起来,建立映射关系
-
SpringMVC接收到指定的请求,就会来找到在映射关系中对应的控制器方法来处理这个请求。
-
注解位置:
- 类: 设置映射请求的请求路径的初始信息
- 法: 设置映射请求请求路径的具体信息
-
参数,@RequestMapping("/test") <=> @RequestMapping(value = "/test")
public @interface RequestMapping{
String name() default "";
@AliasFor("path")
String[] value() default {};
@AliasFor("value")
String[] path() default {};// 当前浏览器所发送请求的请求路径匹配value属性中的任何一个值则当前请求就会被注解所标识的方法进行处理
RequestMethod[] method() default {};// 请求方式 GET POST PUT DELETE HEAD OPTIONS PATCH TRACE,匹配任意一种将被处理,他们也有自己的mapping如@GetMapping
String[] params() default {};
// "username":表示当前所匹配请求的请求参数中必须携带username参数
// '!username": 表示当前所匹配请求的请求参数中一定不能携带username参数
// "username=123":表示当前所匹配请求的请求参数中必须携带username参数且值必须为123
// "username!=456":表示当前所匹配请求的请求参数中可以不携带username,若携带值一定不能是456
String[] headers() default {};
// "Referer":要求请求映射所匹配的请求必须携带Referer请求头信息
// "!Referer":要求请求映射所匹配的请求必须不能携带Referer请求头信息
// "Referer=aaa":要求请求映射所匹配的请求必须携带header请求头信息且Referer=aaa
// "Referer!=bbb":要求请求映射所匹配的请求必须携带header请求头信息且Referer!=bbb
String[] consumes() default {};
String[] produces() default {};
}
@Controller
@RequestMapping("/main")
public class HelloController {
@RequestMapping("/index") // http://localhost:8080/main/index
public String helloIndex(){
return "index";
}
@RequestMapping("/hello") // http://localhost:8080/main/hello
public String toHello(){
return "hello";
}
}
@Controller
public class HelloController {
@RequestMapping("/") // http://localhost:8080/
public String helloIndex(){
return "index";
}
@RequestMapping("/hello") // http://localhost:8080/hello
public String toHello(){
return "hello";
}
}
为什么"/"可以匹配到 http://localhost:8080/
?这与你配置tomcat服务器时设置的URL有关
若当前请求满足@RequestMapping注解的value和method属性,但是不满足headers属性,此时页面显示404错误,即资源未找到
Ant风格
- ?:表示任意的单个字符
- *:表示任意的0个或多个字符
- **:表示任意层数的任意目录 如:
/**/test
可以匹配到/do/test
和/main/do/test
- 注意:在使用
**
时,只能使用/**/xx
的方式
RESTful
SpringMVC路径中的占位符常用于RESTful风格中,当请求路径中将某些数据通过路径的方式传输到服务器中,就可以在相应的@RequestMapping注解的value属性中通过占位符{xxx}表示传输的数据,在通过@PathVariable注解,将占位符所表示的数据赋值给控制器方法的形参
/back/user/{id}
@Controller
@RequestMapping("/back")
public class BackController {
@RequestMapping("/user/{id}")
public String getUser(@PathVariable("id")Integer userId){// 路径拿到参数id的值赋值给userId
return "info";
}
@RequestMapping("/user/{param1}/{param2}")
public String action(@PathVariable("param1")Integer p1,@PathVariable("param2")Integer p2){
return "action";
}
}
@RequestBody
public @interface RequestBody {
boolean required() default true;
}
可以获取请求体信息,使用@RequestBody注解标识控制器方法的形參,当前请求的请求体就会为当前注解所标识的形参赋值
示例
ajax
// ajax
$.ajax({
url: "/test",
method: "POST",
contentType: "application/json;charset=UTF-8",
data: JSON.stringify({// 需要JSON.stringify()......否则400.....
name: "张三",
gender: "123456",
age: 18
}), success: function (res) {
console.log(res)
}
})
axios
axios.post("/test",{
name:"张三",
gender: "男",
age: 18
}).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
@RequestMapping(value = "/test",method = RequestMethod.POST)
public void test(@RequestBody User user, HttpServletResponse response) throws IOException {
System.out.println(user);
response.getWriter().write(user.toString());
}
除此之外还可以使用Map接收...等等...
// ...
public void test(@RequestBody Map<String,String> map){
//...
}
踩坑
415异常:Content type 'application/json;charset=UTF-8' not supported.....
02:51:15.601 [http-nio-8080-exec-7] WARN org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver - Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/json;charset=UTF-8' not supported]
02:51:15.602 [http-nio-8080-exec-7] DEBUG org.springframework.web.servlet.DispatcherServlet - Completed 415 UNSUPPORTED_MEDIA_TYPE
需要jackson依赖..........
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
<version>2.13.4</version>
</dependency>
@RequestHeader
针对请求头
public @interface RequestHeader {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean required() default true;// 必须携带该请求头信息
String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
}
将请求头信息和控制器方法的形参绑定
@RequestMapping("/test")
public String test(@RequestHeader("Referer")String referer){
return "test";
}
@CookieValue
针对cookie
public @interface CookieValue {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean required() default true;
String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
}
将cookie数据和控制器方法的形参绑定
@RequestMapping("/aaa")
public String aaa(@CookieValue("JSESSIONID")String jSessionId){
return "aaa";
}
@ResponseBody
public @interface ResponseBody {
}
使用
@RequestMapping(value = "/test")
@ResponseBody
public User test() {
return new User("zhangsan","男",20);
}
请求:http://localhost:8080/test
响应:{"name":"zhangsan","gender":"男","age":20}
@RestController
@RestController注解是springMVC提供的一个复合注解,标识在控制器的类上,就相当于为类添加了@Controller注解,并且为其中的每个方法添加了@ResponseBody注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
@AliasFor(
annotation = Controller.class
)
String value() default "";
}
获取请求参数
一、通过ServletAPI
@RequestMapping("/hello")
public String toHello(HttpServletRequest request){
String username = request.getParameter("username");
return "hello";
}
二、通过SpringMVC方式
参数名一致
http://xxx/user?username=123
@RequestMapping("/user")
public String getUser(String username){
return "info";
}
参数名不一致
@RequestParam
public @interface RequestParam{
@AliasFor("name")
String value() default "";// 参数名
@AliasFor("value")
String name() default "";
boolean required() default true;// 必须携带该参数 如@RequestParam(value = "id",required = true) 则请求时必须携带id参数
String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";// 如:@RequestParam(value = "id", defaultValue = "123") 给id设置默认值为123
}
http://xxx/user?username=123
@RequestMapping("/user")
public String getUser(@RequestParam("username")String user){
return "info";
}
参数是实体类
@RequestMapping("/user")
public String getUser(User user){
return "info";
}
请求参数乱码
解决获取请求参数的乱码问题,可以使用SpringMVC提供的编码过滤器CharacterEncodingFilter, 但是必须在web.xml中进行注册
在HttpServletRequest获取请求参数之前设置编码,所以,编码过滤在web.xml必须在处理请求之前
示例
http://localhost:8080/hello?name=张三
@RequestMapping("/hello")
public String toHello(String name){
System.out.println(name);// 此时输出:å¼ ä¸ ,乱码
return "hello";
}
解决
web.xml
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
forceEncoding:连同响应编码也设置成UTF-8
域对象共享数据
Request
ServletAPI
使用ServletAPI向request域对象共享数据
@RequestMapping("/hello")
public String toHello(HttpServletRequest request){
request.setAttribute("username", "123");
return "hello";
}
ModelAndView
使用ModelAndView向request域对象共享数据
- ModelAndView有Model和View的功能
- Model:主要用于向请求域共享数据
- View:主要用于设置视图,实现页面跳转
@RequestMapping("/hello")
public ModelAndView toHello(){
ModelAndView mv = new ModelAndView();
// 共享数据
mv.addObject("msg","hello");
// 页面跳转
mv.setViewName("hello");
return mv;
}
<h2 th:text="${msg}"></h2>
Model
使用Model向request域对象共享数据
@RequestMapping("/hello")
public String toHello(Model model){
// 共享数据
model.addAttribute("msg","hello");
return "hello";
}
<h2 th:text="${msg}"></h2>
ModelMap
与Model类似
@RequestMapping("/hello")
public String toHello(ModelMap modelMap){
// 共享数据
modelMap.addAttribute("msg","hello");
return "hello";
}
<h2 th:text="${msg}"></h2>
Map
@RequestMapping("/hello")
public String toHello(Map<String,Object> map){
// 共享数据
map.put("msg","hello");
return "hello";
}
<h2 th:text="${msg}"></h2>
在底层中,这些类型的形参最终都是通过BindingAwareModelMap创建
Session
示例
@RequestMapping("/hello")
public String toHello(HttpSession session){
session.setAttribute("msg", "hello session");
return "hello";
}
<h2 th:text="${session.msg}"></h2>
钝化与活化
勾上框框即可
Application
@RequestMapping("/hello")
public String toHello(HttpSession session){
ServletContext servletContext = session.getServletContext();
// 共享数据
servletContext.setAttribute("msg", "hello application");
return "hello";
}
<h2 th:text="${application.msg}"></h2>
View
SpringMVC中的视图是View接口,视图的作用渲染数据,将模型Model中的数据展示给用户
SpringMVC视图的种类很多,默认有转发视图和重定向视图,当工程引入jstI的依赖,转发视图会自动转换为JstIView,若使用的视图技术为Thymeleaf,在SpringMVC的配置文件中配置 了Thymeleaf的视图解析器,由此视图解析器解析之后所得到的是ThymeleafView
SpringMVC视图
SpringMVC中的视图是View接口,视图的作用渲染数据,将模型Model中的数据展示给用户,SpringMVC视图的种类很多,默认有转发视图和重定向视图,当工程引入jstl的依赖,转发视图会自动转换为]stIView,若使用的视图技术为Thymeleaf,在SpringMVC的配置 文件中配置了Thymeleaf的视图解析器,由此视图解析器解析之后所得到的是ThymeleafView
thymeleafView
浏览器发送请求,若请求地址符合前端控制器的url-pattern, 该请求就会被前端控制器DispatcherServlet处理。前端控制器会读取SpringMVC的核心配置文件,通过扫描组件找到控制器,将请求地址和控制器中@RequestMapping注解的value属性值进行匹配,若匹配成功,该注解所标识的控制器方法就是处理请求的方法。处理请求的方法需要返回-个字符串类型的视图名称,该视图名称会被视图解析器解析,加上前缀和后缀组成视图的路径,通过Thymeleaf对视图进行渲染,最终转发到视图所对应页面
转发并渲染
@RequestMapping("/")
public String helloIndex(){
return "index";
}
InternalResourceView
@RequestMapping("/hello")
public String toHello(){
return "forward:/main/hello";// 转发
}
如果是JSP,需要配置该视图解析器
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"></bean>
RedirectView
重定向
@RequestMapping("/hello")
public String toHello(){
return "redirect:/main/hello";
}
视图控制器
视图控制器:为当前的请求直接设置视图名称实现页面跳转
若设置视图控制器,则只有视图控制器所设置的请求会被处理,其他的请求将全部404
此时必须在配置一个标签: <mvc :annotation -driven />
<mvc:annotation-driven/>
<mvc:view-controller path="/" view-name="index"/>
RESTful
Representational State Transfer,表现层资源状态转移。
<!-- 请求方式过滤,可以使用put和delete-->
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
以post请求发送,并携带_method
参数过去,如果以ajax发送请求,不需要携带_method
参数
<a th:href="@{/user}">获取所有用户信息</a>
<a th:href="@{/user/1}">根据ID获取用户信息</a>
<form th:action="@{/user}" method="post">
<input type="submit" value="添加用户信息"/>
</form>
<form th:action="@{/user}" method="post">
<input name="_method" type="hidden" value="delete"/>
<input type="submit" value="删除用户信息"/>
</form>
<form th:action="@{/user}" method="post">
<input name="_method" type="hidden" value="put"/>
<input type="submit" value="修改用户信息"/>
</form>
// 查询所有
@RequestMapping("/user")
public String getUsers(){
return "success";
}
// 查询单个
@RequestMapping("/user/{id}")
public String getUserById(){
return "success";
}
// 添加
@RequestMapping("/user")
public String addUser(){
return "success";
}
// 删除
@RequestMapping(value = "/user",method = RequestMethod.DELETE)
public String deleteUser(){
return "success";
}
// 修改
@RequestMapping(value = "/user",method = RequestMethod.PUT)
public String updateUser(){
return "success";
}
处理静态资源
<!--处理静态资源,例如html、js、 css、 jpg,若只设置该标签,则只能访问静态资源,其他请求则无法访问,此时必须设置<mvc:default-servlet-handler/>解决问题-->
<mvc:default-servlet-handler/>
文件上传/下载
ResponseEntity用于控制器方法的返回值类型,该控制器方法的返回值就是响应到浏览器的响应报文使用ResponseEntity实现下载文件的功能
前置
<!-- 文件上传解析器 通过id获取bean,必须设置id为multipartResolver-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
下载
@RequestMapping(value = "/test/download")
public ResponseEntity<byte[]> testDownload(HttpSession session) throws IOException {
ServletContext servletContext = session.getServletContext();
// 普通项目是out目录 maven是target目录(war包)
String realPath = servletContext.getRealPath("static" + File.separator + "img");// E:\xxx\learnSpringMvc\target\learnSpringMvc\static\img
realPath = realPath + File.separator + "test.jpg";
// 创建流
FileInputStream inputStream = new FileInputStream(realPath);
// 字节
byte[] bytes = new byte[inputStream.available()];// 字节数
// 流 -->> 字节
inputStream.read(bytes);
MultiValueMap<String,String> headers = new HttpHeaders();// 响应头
// 设置狭隘方式Content-Disposition以附件attachment方式下载
headers.add("Content-Disposition", "attachment;filename=test.jpg");
HttpStatus statusCode = HttpStatus.OK;// 响应状态码
// 响应实体对象
return new ResponseEntity<>(bytes, headers, statusCode);
}
上传
@RequestMapping("/test/upload")
@ResponseBody
public String upload(MultipartFile file, HttpSession session) throws IOException {
if (file == null) {
return "获取目标文件失败";
}
// 原始文件名
String originalFilename = file.getOriginalFilename();
if (originalFilename == null){
return "获取文件名失败";
}
// 截取后缀
System.out.println(originalFilename);
if (!originalFilename.contains(".")){
return "获取文件后缀失败";
}
String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
// 上下文
ServletContext servletContext = session.getServletContext();
// 真实路径
String uploadPath = servletContext.getRealPath("upload");
// 目录是否存在
File f = new File(uploadPath);
if (!f.exists()){
if (!f.mkdir()) {
// 创建目录失败
return "error";
}
}
// 上传文件名
String finalPath = uploadPath + File.separator + UUID.randomUUID() + suffix;
System.out.println("上传文件位置:" + finalPath);
// 上传
file.transferTo(new File(finalPath));
return "success";
}
⚠️ 注意前端input的name要与MultipartFile的变量名一致
拦截器
示例
SpringMVC中的拦截器用于拦截控制器方法的执行
SpringMVC中的拦截器需要实现HandlerInterceptor
SpringMVC的拦截器必须在SpringMVC的配置文件中进行配置:
@Component
public class HelloInterceptor implements HandlerInterceptor {
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("渲染完视图之后执行...");
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("控制器方法之后执行...");
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("控制器方法之前执行...");
return true;// false表示被拦截,不放行
}
}
<mvc:interceptors>
<!-- bean和ref标签所配置的拦截器默认对DispatcherServlet处理的所有的请求进行拦截-->
<!-- <ref bean="helloInterceptor"/>-->
<!-- 精确拦截请求 处理与不处理的路径 /*表示一个层级,并不是所有请求,/**表示所有请求 -->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/bbb"/>
<ref bean="helloInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
拦截顺序
@Component
public class HelloInterceptor implements HandlerInterceptor {
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("HelloInterceptor afterCompletion...");
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("HelloInterceptor postHandle...");
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("HelloInterceptor preHandle...");
return false;
}
}
@Component
public class SecondInterceptor implements HandlerInterceptor {
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("SecondInterceptor afterCompletion...");
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("SecondInterceptor postHandle...");
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("SecondInterceptor preHandle...");
return true;
}
}
helloInterceptor preHandle -->> true
secondInterceptor preHandle -->> true
👇
HelloInterceptor preHandle...
SecondInterceptor preHandle...
SecondInterceptor postHandle...
HelloInterceptor postHandle...
SecondInterceptor afterCompletion...
HelloInterceptor afterCompletion...
helloInterceptor preHandle -->> true
secondInterceptor preHandle -->> false
👇
HelloInterceptor preHandle...
SecondInterceptor preHandle...
HelloInterceptor afterCompletion...
helloInterceptor preHandle -->> false
secondInterceptor preHandle -->> true
👇
HelloInterceptor preHandle...
异常处理解析器
xml方式
控制器出现异常时
public interface HandlerExceptionResolver {
@Nullable
ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}
<!-- 异常映射-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<!-- 遇到NullPointerException异常时,跳转到error视图-->
<prop key="java.lang.NullPointerException">error</prop>
</props>
</property>
<!-- 设置共享在请求域中的异常信息的属性名-->
<property name="exceptionAttribute" value="ex"/>
</bean>
<h2 th:text="@{ex}"></h2>
注解方式
@ControllerAdvice
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<?>[] assignableTypes() default {};
Class<? extends Annotation>[] annotations() default {};
}
@ExceptionHandler
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExceptionHandler {
Class<? extends Throwable>[] value() default {};
}
@ControllerAdvice
public class ExceptionController {
// 哪些异常会被处理
@ExceptionHandler(value = {NullPointerException.class,ClassNotFoundException.class})
public String handlerException(Throwable ex, Model model){
// 共享异常信息
model.addAttribute("ex",ex);
return "error";
}
}
注解配置SpringMVC
在Servlet3.0环境中,容器会在类路径中查找实现javax.servlet.ServletContainerlnitializer接口的类, 如果找到的
话就用它来配置Servlet容器。
Spring提供了这个接口的实现,名为SpringServletContainerlnitializer, 这个类反过来又会查找实现
WebApplicationlnitializer的类并将配置的任务交给它们来完成。Spring3.2引入 了一个便利的
WebApplicationlnitializer基础实现,名为AbstractAnnotationConfigDispatcherServletlnitializer, 当我们的类
扩展了AbstractAnnotationConfigDispatcherServletlnitializer并将其部署到Servlet3.0容器的时候,容器会自动
发现它,并用它来配置Servlet上下文。
// 代替web.xml
public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
// 代替Spring配置文件
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
@Override
// 代替SpringMVC配置文件
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebConfig.class};
}
@Override
// 设置SpringMVC的前端控制器的DispatcherServlet的url-pattern
protected String[] getServletMappings() {
return new String[]{"/"};
}
@Override
// 设置过滤器
protected Filter[] getServletFilters() {
// 编码过滤器
CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
encodingFilter.setEncoding("UTF-8");
encodingFilter.setForceEncoding(true);
// 请求方式过滤器
HiddenHttpMethodFilter httpMethodFilter = new HiddenHttpMethodFilter();
return new Filter[]{httpMethodFilter,encodingFilter};
}
}
@Configuration
public class SpringConfig {
}
@Configuration// 声明配置文件
@ComponentScan("com.learn")//扫描组件
@EnableWebMvc//开启注解驱动
public class WebConfig implements WebMvcConfigurer {
@Override
// 默认servlet处理静态资源
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
@Override
// 配置视图解析器
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
}
@Bean
// 文件上传解析器
public CommonsMultipartResolver multipartResolver(){
return new CommonsMultipartResolver();
}
@Override
// 拦截器
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new HelloInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/aaa");
}
@Override
// 异常解析器
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();
Properties prop = new Properties();
prop.setProperty("java.lang.NullPointerException", "error");
exceptionResolver.setExceptionMappings(prop);
exceptionResolver.setExceptionAttribute("ex");
resolvers.add(exceptionResolver);
}
@Bean
// 配置生成模板解析器
public ITemplateResolver iTemplateResolver(){
WebApplicationContext context = ContextLoader.getCurrentWebApplicationContext();
ServletContextTemplateResolver templateResolver =
new ServletContextTemplateResolver(context.getServletContext());
templateResolver.setPrefix("/templates/");
templateResolver.setSuffix(".html");
templateResolver.setCharacterEncoding("UTF-8");
templateResolver.setTemplateMode(TemplateMode.HTML);
return templateResolver;
}
@Bean
public SpringTemplateEngine springTemplateEngine(ITemplateResolver iTemplateResolver){
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(iTemplateResolver);
return templateEngine;
}
@Bean
// 生成视图解析器并注入模板引擎
public ViewResolver viewResolver(SpringTemplateEngine springTemplateEngine){
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setCharacterEncoding("UTF-8");
viewResolver.setTemplateEngine(springTemplateEngine);
return viewResolver;
}
}