1. Spring MVC框架统一处理异常
在使用Spring MVC框架时,控制器(Controller)可以不处理异常(如果执行过程中出现异常,则自动抛出),框架提供了统一处理异常的机制。
关于统一处理异常:
-
统一处理异常的代码应该编写在专门的类中,并且,在此类上添加
@ControllerAdvice
/@RestControllerAdvice
注解@ControllerAdvice
/@RestControllerAdvice
注解的类中的特定方法将作用于每一次处理请求的过程中- 其实,统一处理异常的代码可以写在某个控制器中,但是,将只能作用于此控制器中各处理请求的方法,无法作用于其它控制器中处理请求的方法
-
在类中自定义方法来处理异常
- 注解:
@ExceptionHandler
- 访问权限:应该
public
- 返回值类型:设计原则可参考控制器中处理请求的方法
- 方法名称:自定义
- 参数列表:必须包含异常类型的参数,且此参数就是Spring MVC框架调用控制器方法时捕获的异常,另外,可按需添加
HttpServerRequest
、HttpServletResponse
等少量限定类型的参数
- 注解:
例如:
package cn.tedu.csmall.product.handler;
import cn.tedu.csmall.product.ex.ServiceException;
import cn.tedu.csmall.product.web.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler
public JsonResult<Void> handleServiceException(ServiceException e) {
log.debug("处理ServiceException,serviceCode={},message={}",
e.getServiceCode(), e.getMessage());
return JsonResult.fail(e);
}
}
在以上方法中,方法的参数是ServiceException
,则表示此方法就是用于处理ServiceException
及其子孙类异常的,不可以处理其它种类的异常。
在同一个项目中,可以有多个以上处理异常的类,或同一个处理异常的类中可以有多个处理异常的方法,只要这些方法处理的异常不冲突即可!并且,这些方法处理的异常允许存在父子级继承关系,例如某个方法处理ServiceException
,另一个方法处理RuntimeException
,当出现ServiceException
,仍会按照处理ServiceException
的方法进行处理!
强烈建议在每个项目中都添加一个处理Throwable
的方法,避免项目出现500
错误!例如:
package cn.tedu.csmall.product.handler;
import cn.tedu.csmall.product.ex.ServiceException;
import cn.tedu.csmall.product.web.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler
public JsonResult<Void> handleServiceException(ServiceException e) {
log.debug("处理ServiceException,serviceCode={},message={}", e.getServiceCode(), e.getMessage());
return JsonResult.fail(e);
}
@ExceptionHandler
public JsonResult<Void> handleThrowable(Throwable e) {
log.debug("处理Throwable");
e.printStackTrace();
Integer serviceCode = 99999;
String message = "程序运行过程中出现未知错误,请联系系统管理员!";
return JsonResult.fail(serviceCode, message);
}
}
注意:以上处理Throwable
的方法,并不是真正意义的“处理”了异常,在此方法中,应该通过日志输出异常的详情信息,并且,在后续出现相关异常时,在此类中补充针对这些异常的精准处理!
另外,在@ExceptionHandler
中,可以配置异常类型的参数,此参数是异常类型的数组,用于指定需要处理哪些种类的异常,但是,通常并不需要进行此项配置,因为方法的参数就可以直接表示处理哪种异常!此注解参数大多应用于“多种不相关的异常使用同一种处理方式”的情景!
2. 前后端交互
在默认情况下,不允许跨域访问,在前后端分离的模式下,要实现前后端交互,需要在服务器端进行配置,允许跨域访问!
在项目的根包下创建config.WebMvcConfiguration
类,在类上添加@Configuration
注解,并且,实现WebMvcConfiguruer
接口,重写接口中的addCorsMappings()
方法来配置允许跨域访问:
package cn.tedu.csmall.product.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
.allowedHeaders("*")
.allowedMethods("*")
.allowCredentials(true)
.maxAge(3600);
}
}
另外,关于@RequestBody
注解:
-
如果控制器中处理请求的方法的参数添加了此注解,则客户端提交的请求参数必须是JSON格式的,例如:
-
{ name: '华为', pinyin: 'HuaWei' }
-
-
如果控制器中处理请求的方法的参数没有添加此注解,则客户端提交的请求参数必须是FormData格式的
-
name=华为&pinyin=HuaWei
-
在前端技术中,可以使用qs
框架可以轻松的将JavaScript对象转换为FormData格式!
在前端项目中,打开终端,安装qs
:
npm i qs -S
然后,需要在主配置文件main.js
中导入并声明为Vue对象的属性:
import qs from 'qs';
Vue.prototype.qs = qs;
后续,在JavaScript代码中,可以使用qs
的stringify()
将对象转换成FormData格式,例如:
let formData = this.qs.stringify(this.ruleForm);
3. 【框架小结】Spring
3.1. 关于Spring框架
Spring框架主要解决了创建对象、管理对象相关的问题。
Spring框架在创建了各对象之后,会持有这些对象的引用,相当于“把这些对象管理了起来”,所以,Spring框架也经常被称之为“Spring容器”。
3.2. 关于Spring框架创建对象
3.2.1. 关于通过组件扫描来创建组件对象
组件:每个项目中重要的组件部分,即可称之为组件。
在使用Spring框架时,可以通过@Component
/ @Controller
/ @Service
/ @Repository
这4个注解中的任意一个,将某个类标记为“组件”,并且,在Spring框架的作用范围内,这4个注解是完全等效的!之所以存在4个注解,主要是希望通过使用不同的注解,表现出对应的语义!
另外,在配置类上,可以使用@ComponentScan
的注解,开启“组件扫描”,此注解可以配置一个根包(basePackage),例如:
@ComponentScan("cn.tedu.csmall")
则Spring框架会扫描这个根包及其子孙包下所有的类,如果扫描到的类是组件,则Spring框架会自动创建这个类的对象,并把对象保存到Spring容器中!
另外,@Configuration
是比较特殊的组件注解,添加了此注解的类将是”配置类“,Spring在创建此类的对象时,会使用CGLib代理模式进行处理。
还有@RestController
、@ControllerAdvice
、@RestControllerAdvice
也能把类标记为”组件“,但是,这些注解是Spring MVC框架中定义的。
通过组件扫描创建的对象,这些对象在Spring容器中都称之为Spring Bean,每个Spring Bean都有一个Bean Name(Bean的名称),默认情况下,当此对象的类名第1个字母大写,且第2个字母小写时,Bean Name就是将类名首字母改为小写,例如BrandController
类型的对象的Bean Name就是brandController
,如果前2个字母的大小写不符合前序规则,则Bean Name就是类名。如果需要自行指定类名,可以配置@Component
等注解的参数,例如:@Component("beanName")
。
3.2.2. 使用@Bean
方法创建对象
使用Spring框架时,可以在配置类中自定义创建对象的方法,并在方法上添加@Bean
注解,则Spring框架会自动调用此方法,并将此方法返回的对象保存在Spring容器中,例如:
@Configuration
public class BeanConfiguration {
// 假设某Controller并没有通过组件扫描的做法来创建对象
@Bean
public BrandController brandController() {
return new BrandController();
}
}
使用此做法,默认的Bean Name就是方法名称,或者,通过配置@Bean
的参数来指定名称!
3.2.3. 关于以上2种做法的选取
如果是自定义的类型,推荐使用组件扫描的做法,如果不是自定义的类型,只能使用@Bean
方法!
3.3. Spring管理的对象的作用域
Spring管理的对象,默认情况下,是单例的!
如果要修改,可以使用@Scope("prototype")
注解组件类或@Bean
方法。
单例:在任何时刻,此类型的对象最多只有1个!
注意:Spring并没有使用到设计模式中的单例模式,只是管理的对象具有相同的特征。
被Spring管理的单例的对象,默认情况下,是预加载的,相当于单例模式中的”饿汉式“!
如果要修改,可以使用@Lazy
注解组件类或@Bean
方法。
预加载:加载Spring环境时就会创建这些对象!与之相反的概念是单例模式中的”懒汉式“!
单例模式(饿汉式)示例:
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
单例模式(懒汉式)示例:
public class Singleton {
private static volatile Singleton instance;
private static final Object lock = new Object();
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (lock) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
3.4. 关于自动装配
自动装配:当被Spring管理对象的某个属性,或被Spring自动调用的方法的参数需要值时,Spring框架会自动从容器中查找合适的对象,为此属性或参数赋值。
关于”合适的对象“:要么类型匹配,要么名称匹配。
当属性需要被自动装配时,需要在属性上添加自动装配的注解,即添加@Autowired
注解,例如:
@RestController
public class BrandController {
@Autowired
private IBrandService brandService;
}
关于“被Spring自动调用的方法”,通常包括:
- 构造方法
@Bean
方法- 添加了
@Autowired
注解的方法,通常可能是Setter方法
关于@Autowired
的自动装配机制:
- Spring会先从容器中查询匹配类型的Bean的数量
- 0个:取决于
@Autowired
注解的required
属性true
:装配失败,在加载Spring环境时直接抛出异常false
:放弃自动装配,则此属性的值为null
- 1个:直接装配,且成功
- 多个:将尝试按照名称来自动装配
- 存在名称匹配的Bean:成功装配
- 不存在名称匹配的Bean:装配失败,在加载Spring环境时直接抛出异常
- 提示:可以在被装配的值上添加
@Qualifier
注解以指定某个Bean Name
- 0个:取决于
4. Spring Validation
服务器端程序需要对各请求参数进行检查!注意:即使客户端程序已经检查了请求参数,服务器端仍需要再次检查!
Spring Validation是专门用于检查请求参数的格式基本有效性的框架!
在Spring Boot项目中,需要添加依赖项:
<!-- Spring Boot Validation,用于检查请求参数的格式基本有效性 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
关于请求参数的检查,应该在控制器(Controller)中直接执行!
关于检查的做法:
- 在控制器类中处理请求的方法中,对于封装类型的请求参数,添加
@Valid
或@Validated
注解,表示此参数是需要被Spring Validation框架进行检查的 - 在封装类型的各属性上,添加所需的检查注解,例如:
@NotNull
作业
- 完成“添加类别”的前后端交互
- 完成“添加相册”,包括后端与前端的全部内容
- 业务规则:相册名称不允许重复
- 页面文件名:AlbumAddNewView.vue
- 页面路径:/sys-admin/temp/album/add-new
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y3Vyl50Z-1662645780168)(images/image-20220901180109250.png)]