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; } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术