Spring (day09)

1. Spring MVC框架统一处理异常

在使用Spring MVC框架时,控制器(Controller)可以不处理异常(如果执行过程中出现异常,则自动抛出),框架提供了统一处理异常的机制。

关于统一处理异常:

  • 统一处理异常的代码应该编写在专门的类中,并且,在此类上添加@ControllerAdvice / @RestControllerAdvice注解

    • @ControllerAdvice / @RestControllerAdvice注解的类中的特定方法将作用于每一次处理请求的过程中
    • 其实,统一处理异常的代码可以写在某个控制器中,但是,将只能作用于此控制器中各处理请求的方法,无法作用于其它控制器中处理请求的方法
  • 在类中自定义方法来处理异常

    • 注解:@ExceptionHandler
    • 访问权限:应该public
    • 返回值类型:设计原则可参考控制器中处理请求的方法
    • 方法名称:自定义
    • 参数列表:必须包含异常类型的参数,且此参数就是Spring MVC框架调用控制器方法时捕获的异常,另外,可按需添加HttpServerRequestHttpServletResponse等少量限定类型的参数

例如:

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代码中,可以使用qsstringify()将对象转换成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

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

作业

  1. 完成“添加类别”的前后端交互
  2. 完成“添加相册”,包括后端与前端的全部内容
    1. 业务规则:相册名称不允许重复
    2. 页面文件名:AlbumAddNewView.vue
    3. 页面路径:/sys-admin/temp/album/add-new

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y3Vyl50Z-1662645780168)(images/image-20220901180109250.png)]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值