赞
踩
开发过程中是不可避免地会出现各种异常情况,例如网络连接异常、数据格式异常、空指针异常等等。异常的出现可能导致程序的运行出现问题,甚至直接导致程序崩溃。因此,在开发过程中,合理处理异常、避免异常产生、以及对异常进行有效的调试是非常重要的。
对于异常的处理,一般分为两种方式:
- 编程式异常处理:是指在代码中显式地编写处理异常的逻辑。它通常涉及对异常类型的检测及其处理,例如使用 try-catch 块来捕获异常,然后在 catch 块中编写特定的处理代码,或者在 finally 块中执行一些清理操作。在编程式异常处理中,开发人员需要显式地进行异常处理,异常处理代码混杂在业务代码中,导致代码可读性较差。
- 声明式异常处理:则是将异常处理的逻辑从具体的业务逻辑中分离出来,通过配置等方式进行统一的管理和处理。在声明式异常处理中,开发人员只需要为方法或类标注相应的注解(如 @Throws 或 @ExceptionHandler),就可以处理特定类型的异常。相较于编程式异常处理,声明式异常处理可以使代码更加简介、易于维护和扩展。
站在宏观角度来看待声明式事务处理:
整个项目从架构这个层面设计的异常处理的统一机制和规范。
一个项目中会包含很多个模块,各个模块需要分工完成。如果张三负责的模块按照 A 方案处理异常,李四负责的模块按照 B 方案处理异常...各个模块处理异常的思路、代码、命名细节都不一样,那么就会让整个项目非常混乱。
使用声明式异常处理,可以统一项目处理异常思路,项目更加清晰了。
1)声明异常处理控制器类
异常处理控制类,统一定义异常处理 handler 方法
- /**
- * projectName: com.atguigu.execptionhandler
- *
- * description: 全局异常处理器,内部可以定义异常处理Handler!
- */
- /**
- * @RestControllerAdvice = @ControllerAdvice + @ResponseBody
- * @ControllerAdvice 代表当前类的异常处理controller!
- */
- @RestControllerAdvice
- public class GlobalExceptionHandler {
-
-
- }
2)声明异常处理 handler 方法
异常处理 handler 方法和普通的 handler 方法参数接收和响应都一致。只不过异常处理 handler 方法要映射异常,发生对应的异常会调用。普通的 handler 方法要使用 @RequestMapping 注解映射路径,发生对应的路径调用。
- /**
- * 异常处理handler
- * @ExceptionHandler(HttpMessageNotReadableException.class)
- * 该注解标记异常处理Handler,并且指定发生异常调用该方法!
- *
- *
- * @param e 获取异常对象!
- * @return 返回handler处理结果!
- */
- @ExceptionHandler(HttpMessageNotReadableException.class)
- public Object handlerJsonDateException(HttpMessageNotReadableException e){
-
- return null;
- }
-
- /**
- * 当发生空指针异常会触发此方法!
- * @param e
- * @return
- */
- @ExceptionHandler(NullPointerException.class)
- public Object handlerNullException(NullPointerException e){
-
- return null;
- }
-
- /**
- * 所有异常都会触发此方法!但是如果有具体的异常处理Handler!
- * 具体异常处理Handler优先级更高!
- * 例如: 发生NullPointerException异常!
- * 会触发handlerNullException方法,不会触发handlerException方法!
- * @param e
- * @return
- */
- @ExceptionHandler(Exception.class)
- public Object handlerException(Exception e){
-
- return null;
- }
3)配置文件扫描控制器类配置
确保异常处理控制类被扫描
- <!-- 扫描controller对应的包,将handler加入到ioc-->
- <context:component-scan base-package="com.atguigu.controller,
- com.atguigu.exceptionhandler" />
拦截器和过滤器解决问题
为了提高乘客效率,在乘客进入站台前统一检票
在程序中,使用拦截器在请求到达具体 handler 方法前统一执行检测
拦截器 VS 过滤器:
选择:
功能需要如果用 SpringMVC 的拦截器能够实现,就不使用过滤器。
1)创建拦截器类
- public class Process01Interceptor implements HandlerInterceptor {
-
- // 在处理请求的目标 handler 方法前执行
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
- System.out.println("request = " + request + ", response = " + response + ", handler = " + handler);
- System.out.println("Process01Interceptor.preHandle");
-
- // 返回true:放行
- // 返回false:不放行
- return true;
- }
-
- // 在目标 handler 方法之后,handler报错不执行!
- @Override
- public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
- System.out.println("request = " + request + ", response = " + response + ", handler = " + handler + ", modelAndView = " + modelAndView);
- System.out.println("Process01Interceptor.postHandle");
- }
-
- // 渲染视图之后执行(最后),一定执行!
- @Override
- public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
- System.out.println("request = " + request + ", response = " + response + ", handler = " + handler + ", ex = " + ex);
- System.out.println("Process01Interceptor.afterCompletion");
- }
- }
单个拦截器执行顺序:
2)拦截器配置
springmvc.xml
- <!-- 配置拦截器-->
- <mvc:interceptors>
- <!-- 默认拦截器,拦截所有请求-->
- <bean class="com.atguigu.interceptor.Process01Interceptor" />
- </mvc:interceptors>
3)配置详解
① 默认拦截全部
- <!-- 具体配置拦截器可以指定拦截的请求地址 -->
- <mvc:interceptor>
- <!-- 精确匹配 -->
- <mvc:mapping path="/common/request/one"/>
- <bean class="com.atguigu.mvc.interceptor.Process03Interceptor"/>
- </mvc:interceptor>
② 精准配置
- <!-- 具体配置拦截器可以指定拦截的请求地址 -->
- <mvc:interceptor>
- <!-- 精确匹配 -->
- <mvc:mapping path="/common/request/one"/>
- <bean class="com.atguigu.mvc.interceptor.Process03Interceptor"/>
- </mvc:interceptor>
-
- <mvc:interceptor>
- <!-- /*匹配路径中的一层 -->
- <mvc:mapping path="/common/request/*"/>
- <bean class="com.atguigu.mvc.interceptor.Process04Interceptor"/>
- </mvc:interceptor>
-
- <mvc:interceptor>
- <!-- /**匹配路径中的多层 -->
- <mvc:mapping path="/common/request/**"/>
- <bean class="com.atguigu.mvc.interceptor.Process05Interceptor"/>
- </mvc:interceptor>
③ 排除配置
- <mvc:interceptor>
- <!-- /**匹配路径中的多层 -->
- <mvc:mapping path="/common/request/**"/>
-
- <!-- 使用 mvc:exclude-mapping 标签配置不拦截的地址 -->
- <mvc:exclude-mapping path="/common/request/two/bbb"/>
-
- <bean class="com.atguigu.mvc.interceptor.Process05Interceptor"/>
- </mvc:interceptor>
④ 多个拦截器执行顺序
一个网站有 5、6个资源,其中有一个为登录资源,两个无需登录即可访问,另外三个需要登录后才能访问,如果不登录就访问那三个资源,需要拦截,并且提示登录后访问。
访问资源的请求地址:
登录资源:/public/resource/login
公共资源1:/public/resource/one
公共资源2:/public/resource/two
私密资源1:/private/resource/one
私密资源2:/private/resource/two
私密资源3:/private/resource/three
案例实现:
1) 声明资源类
① PublicController
- /**
- * projectName: com.atguigu.controller
- * description: 公有资源控制类
- */
- @RestController
- @RequestMapping("public/resource")
- public class PublicController {
- /**
- * 模拟登录,将假用户数据存储到session中!
- */
- @GetMapping("login")
- public Object login(HttpSession session){
- session.setAttribute("user","root");
- return "login success!!";
- }
-
- @GetMapping("one")
- public Object one(){
- return "public one";
- }
-
- @GetMapping("two")
- public Object two(){
- return "public two";
- }
- }
② PrivateController
- @RestController
- @RequestMapping("private/resource")
- public class PrivateController {
-
- @GetMapping("one")
- public Object one(){
- return "private one";
- }
-
- @GetMapping("two")
- public Object two(){
- return "private two";
- }
-
- @GetMapping("three")
- public Object three(){
- return "private two";
- }
-
- }
2)声明拦截器类
- /**
- * projectName: com.atguigu.interceptor
- *
- * description: 登录保护拦截器
- */
- public class LoginProtectInterceptor implements HandlerInterceptor {
-
- /**
- * 登录保护方法
- * @param request current HTTP request
- * @param response current HTTP response
- * @param handler chosen handler to execute, for type and/or instance evaluation
- * @return
- * @throws Exception
- */
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
-
- Object user = request.getSession().getAttribute("user");
-
- if (user == null){
- response.setContentType("text/html;charset=utf-8");
- //没有登录
- response.getWriter().print("请先登录,再访问! <a href='/public/resource/login'>点击此处登录</a>");
- //拦截,不到达目标地址
- return false;
- }
- return true;
- }
- }
3)配置拦截器类
- <!-- 配置拦截器-->
- <mvc:interceptors>
- <mvc:interceptor>
- <mvc:mapping path="/private/**"/>
- <bean class="com.atguigu.interceptor.LoginProtectInterceptor" />
- </mvc:interceptor>
- </mvc:interceptors>
在Web 应用三层架构体系中,表述层负责接收浏览器提交的数据,业务逻辑层负责数据的处理。为了能够让业务逻辑层基于正确的数据进行处理,我们需要在表述层对数据进行检查,将错误的数据隔绝在业务逻辑层之外。
1)校验概述
JSR 303 是 Java 为Bean 数据合法性校验提供的标准框架,它已经包含在 JavaEE 6.0标准中。JSR 303 通过在 Bean 属性上标注类似于 @NoNull、@Max 等标准的注解指定校验规则,并通过标准的验证接口对 Bean 进行验证。
注解 | 规则 |
---|---|
@Null | 标注值必须为 null |
@NotNull | 标注值不可为 null |
@AssertTrue | 标注值必须为 true |
@AssertFalse | 标注值必须为 false |
@Min(value) | 标注值必须大于或等于 value |
@Max(value) | 标注值必须小于或等于 value |
@DecimalMin(value) | 标注值必须大于或等于 value |
@DecimalMax(value) | 标注值必须小于或等于 value |
@Size(max,min) | 标注值大小必须在 max 和 min 限定的范围内 |
@Digits(integer,fratction) | 标注值值必须是一个数字,且必须在可接受的范围内 |
@Past | 标注值只能用于日期型,且必须是过去的日期 |
@Future | 标注值只能用于日期型,且必须是将来的日期 |
@Pattern(value) | 标注值必须符合指定的正则表达式 |
JSR 303 只是一套标准,需要提供其实现才可以使用。Hibernate Validator 是 JSR 303 的一个参考实现,除支持所有标准的校验注解外,它还支持以下的扩展注解: | |
注解 | 规则 |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- |
标注值必须是格式正确的 Email 地址 | |
@Length | 标注值字符串大小必须在指定的范围内 |
@NotEmpty | 标注值字符串不能是空字符串 |
@Range | 标注值必须在指定的范围内 |
Spring 4.0 版本已经拥有自己独立的数据校验框架,同时支持 JSR 303 标准的校验框架。Spring 在进行数据绑定时,可同时调用校验框架完成数据校验工作。在SpringMVC 中,可直接通过注解驱动 mvc:annotation-driven 的方式进行数据校验。Spring 的 LocalValidatorFactoryBean 既实现了 Spring 的 Validator 接口,也实现了 JSR 303 的 Validator 接口。只要在Spring容器中定义了一个LocalValidatorFactoryBean,即可将其注入到需要数据校验的 Bean中。Spring本身并没有提供JSR 303的实现,所以必须将JSR 303的实现者的jar包放到类路径下。 | |
配置 mvc:annotation-driven 后,SpringMVC 会默认装配好一个 LocalValidatorFactoryBean,通过在处理方法的入参上标注 @Validated 注解即可让 SpringMVC 在完成数据绑定后执行数据校验的工作。 |
2)操作演示
导入依赖
- <!-- 校验注解 -->
- <dependency>
- <groupId>jakarta.platform</groupId>
- <artifactId>jakarta.jakartaee-web-api</artifactId>
- <version>9.1.0</version>
- <scope>provided</scope>
- </dependency>
-
- <!-- 校验注解实现-->
- <!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator -->
- <dependency>
- <groupId>org.hibernate.validator</groupId>
- <artifactId>hibernate-validator</artifactId>
- <version>8.0.0.Final</version>
- </dependency>
- <!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator-annotation-processor -->
- <dependency>
- <groupId>org.hibernate.validator</groupId>
- <artifactId>hibernate-validator-annotation-processor</artifactId>
- <version>8.0.0.Final</version>
- </dependency>
应用校验注解
- import jakarta.validation.constraints.Email;
- import jakarta.validation.constraints.Min;
- import org.hibernate.validator.constraints.Length;
-
- /**
- * projectName: com.atguigu.pojo
- */
- public class User {
- //age 1 <= age < = 150
- @Min(10)
- private int age;
-
- //name 3 <= name.length <= 6
- @Length(min = 3,max = 10)
- private String name;
-
- //email 邮箱格式
- @Email
- private String email;
-
- public int getAge() {
- return age;
- }
-
- public void setAge(int age) {
- this.age = age;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public String getEmail() {
- return email;
- }
-
- public void setEmail(String email) {
- this.email = email;
- }
- }
handler 标记和绑定错误收集
- @RestController
- @RequestMapping("user")
- public class UserController {
-
- /**
- * @Validated 代表应用校验注解! 必须添加!
- */
- @PostMapping("save")
- public Object save(@Validated @RequestBody User user,
- //在实体类参数和 BindingResult 之间不能有任何其他参数, BindingResult可以接受错误信息,避免信息抛出!
- BindingResult result){
- //判断是否有信息绑定错误! 有可以自行处理!
- if (result.hasErrors()){
- System.out.println("错误");
- String errorMsg = result.getFieldError().toString();
- return errorMsg;
- }
- //没有,正常处理业务即可
- System.out.println("正常");
- return user;
- }
- }
3)易混总结
@NotNull (包装类型不为null)、@NotEmpty(集合类型长度大于0) 、@NotBlank(字符串,不为null,切不为" "字符串) 都是用于在数据校验中检查字段值是否为空的注解,但是它们的用法和校验规则有所不同。
1)文件上传表单页面
位置:index.xml
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>Title</title>
- </head>
- <body>
- <form action="/save/picture" method="post" enctype="multipart/form-data">
- 昵称:<input type="text" name="nickName" value="龙猫" /><br/>
- 头像:<input type="file" name="headPicture" /><br/>
- 背景:<input type="file" name="backgroundPicture" /><br/>
- <button type="submit">保存</button>
- </form>
- </body>
- </html>
2)springmvc 环境要求
pom.xml 添加依赖
- <!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
- <dependency>
- <groupId>commons-fileupload</groupId>
- <artifactId>commons-fileupload</artifactId>
- <version>1.3.1</version>
- </dependency>
配置文件上传处理器(springmvc配置)
- <!-- 文件上传处理器,可处理 multipart/* 请求并将其转换为 MultipartFile 对象-->
- <bean id="multipartResolver"
- class="org.springframework.web.multipart.support.StandardServletMultipartResolver">
- </bean>
注:CommonsMultipartResolver 的 bean 的 id,必须是:multipartResolver 如果不是这个值,会在上传文件时报错 在 web.xml
文件中添加 Multipart 配置
- <servlet>
- <servlet-name>yourAppServlet</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <multipart-config>
- <!-- 定义文件上传时所需的最大值,单位为字节 -->
- <max-file-size>10485760</max-file-size>
- <!-- 定义单个上传文件的最大值,单位为字节 -->
- <max-request-size>20971520</max-request-size>
- <!-- 定义内存中存储文件的最大值,超过此大小的文件会写入到硬盘中 -->
- <file-size-threshold>5242880</file-size-threshold>
- </multipart-config>
- <load-on-startup>1</load-on-startup>
- </servlet>
3)handler 方法接收数据
- /**
- * 上传的文件使用 MultipartFile 类型接收其相关数据
- * @param nickName
- * @param picture
- * @param backgroundPicture
- * @return
- * @throws IOException
- */
- @PostMapping ("picture")
- public String upload(String nickName, @RequestParam("headPicture") MultipartFile picture, @RequestParam("backgroundPicture")MultipartFile backgroundPicture) throws IOException {
- System.out.println(nickName);
- String inputName = picture.getName();
- System.out.println("文件上传表单项的 name 属性值:" + inputName);
-
- // 获取这个数据通常都是为了获取文件本身的扩展名
- String originalFilename = picture.getOriginalFilename();
- System.out.println("文件在用户本地原始的文件名:" + originalFilename);
-
- String contentType = picture.getContentType();
- System.out.println("文件的内容类型:" + contentType);
-
- boolean empty = picture.isEmpty();
- System.out.println("文件是否为空:" + empty);
-
- long size = picture.getSize();
- System.out.println("文件大小:" + size);
-
- byte[] bytes = picture.getBytes();
- System.out.println("文件二进制数据的字节数组:" + Arrays.asList(bytes));
-
- InputStream inputStream = picture.getInputStream();
- System.out.println("读取文件数据的输入流对象:" + inputStream);
-
- Resource resource = picture.getResource();
- System.out.println("代表当前 MultiPartFile 对象的资源对象" + resource);
- return "home";
- }
- ```
4)MultipartFile 接口
5)文件转存
① 底层机制
② 本地转存
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。