SpringBoot统一功能处理(AOP思想实现)(统一用户登录权限验证 / 异常处理 / 数据格式返回)

主要是三个处理:

1、统一用户登录权限验证;

2、统一异常处理;

3、统一数据格式返回。

目录

一、用户登录权限校验

[🍅 1、使用拦截器](#🍅 1、使用拦截器)

[🎈 1.1自定义拦截器](#🎈 1.1自定义拦截器)

[🎈 1.2 设置自定义拦截器](#🎈 1.2 设置自定义拦截器)

🎈创建controller类,并且运行项目

[🍅 2、拦截器原理](#🍅 2、拦截器原理)

二、统一异常处理

三、统一数据返回

[🍅 为什么需要统一数据返回格式](#🍅 为什么需要统一数据返回格式)

[🍅 统一数据返回格式](#🍅 统一数据返回格式)

🎈定义同已返回类型

[🎈 同以数据处理](#🎈 同以数据处理)

🎈业务类


一、用户登录权限校验

🍅 1、使用拦截器

可以对一部分方法进行拦截,而另一部分不拦截。

🎈 1.1自定义拦截器

java 复制代码
/*
* 全局变量
* */
public class AppVar {
//    Session key
    public static final String SESSION_KEY = "SESSION KEY";
}

/*
* 自定义拦截器
* 返回true -> 表示拦截器验证成功,继续指定后续方法
* 返回false -> 表示验证失败,不会执行后续的方法了
* */
@Component
public class UserInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
//        业务方法
        HttpSession session = request.getSession(false);
        if (session!=null &&
                session.getAttribute(AppVar.SESSION_KEY)!=null){
//            用户已经登录
            return true;
        }
        return false;
    }
}

🎈 1.2 设置自定义拦截器

将自定义拦截器设置当前项目的配置文件中,并且设置拦截规则。

拦截器要注入到Spring中才能运行,他是伴随着Spring的启动而启动的

java 复制代码
@Configuration
public class AppConfig implements WebMvcConfigurer {
    @Autowired
    private UserInterceptor userInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
//        registry.addInterceptor(new UserInterceptor());
        registry.addInterceptor(userInterceptor)
                .addPathPatterns("/**")    //("/**")表示拦截所有的请求
                .excludePathPatterns("/user/reg")//表示过滤拦截,不拦截(/user/reg)
                .excludePathPatterns("/user/login");//表示过滤拦截,不拦截("/user/login")
    }
}

🎈创建controller类,并且运行项目

java 复制代码
@RestController
@RequestMapping("/user")
public class UserController {
    @RequestMapping("/getuser")
    public String getUser(){
        System.out.println("do getUser()");
        return "user";
    }

    @RequestMapping("/reg")
    public String reg(){
        System.out.println("do reg()");
        return "reg";
    }

    @RequestMapping("/login")
    public String login(){
        System.out.println("do login()");
        return "login";
    }
}

其中:

  • addPathPatterns:表示要拦截的url,"/**"表示拦截任意方法
  • elcludePathPatterns:表示需要排除的URL
  • 以上得拦截规则可以拦截URL,包括静态文件(图片文件、JS、CSS等),一般拦截静态文件的时候,我们可以把这些静态文件分类放在static文件中

🍅 2、拦截器原理

在使用拦截器之前:

使用拦截器之后:会在调用Controller之前进行相应的业务处理

实现原理源码分析:

所有的controller指定都会通过一个调度器DispatcherServlet来实现,

所有的请求都会执行DispatcherServlet中的doDispatcher方法,在doDispatcher会执行一系列的事件,该事件是在执行拦截器之前的,如果该事件返回false,后续就不会执行Controller。

以下是doDispatcher中的一部分代码,发现在执行controller之前都会追先执行预处理

java 复制代码
// 调⽤预处理【重点】

 if (!mappedHandler.applyPreHandle(processedRequest, respon
se)) {
 return;
 }
 // 执⾏ Controller 中的业务

 mv = ha.handle(processedRequest, response, mappedHandler.g
etHandler());
 if (asyncManager.isConcurrentHandlingStarted()) {
 return;
}

那么,关于预处理⽅法 applyPreHandle方法:从上面的源码可以看出,着和我们之前定义的拦截器相似,着就是拦截器的实现原理

java 复制代码
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex

                = i++) {
            // 获取项⽬中使⽤的拦截器 HandlerInterceptor

            HandlerInterceptor interceptor = (HandlerInterceptor)this.intercep
            torList.get(i);
            if (!interceptor.preHandle(request, response, this.handler)) {
                this.triggerAfterCompletion(request, response, (Exception)null

                );
                return false;
            }
        }
        return true;
    }

二、统一异常处理

统一异常处理:就是指常规的异常,统一处理。

统一异常处理使用的是@ControllerAdvice + @ExceptionHandler来执行的,@ControllerAdvice表示控制器通知类,@ExceptionHandler是异常处理,两个结合表示当出现异常的时候执行某个通知,也就是执行某个方法事件。

首先认为构造一个空指针异常:

java 复制代码
 @RequestMapping("/reg")
    public String reg(){
        System.out.println("do reg()");
        Object obj = null;
        System.out.println(obj.hashCode());
        System.out.println();
        System.out.println("do reg()");
        return "reg";
    }

报错了: 这种直接给你报错的方式并不直观,所以我们可以进行统一的异常处理,返回直观的数据。

然后我们进行统一异常处理:

首先定义一个统一的返回对象:

java 复制代码
@Data
public class ResultAjax {
    private int code;   //状态码
    private String msg; //状态码的描述信息
    private Object data;//返回数据
}

然后定义异常管理器:

java 复制代码
@RestControllerAdvice
public class ExceptionAdvice {

    @ExceptionHandler(NullPointerException.class)
    public ResultAjax doNullPointerException(NullPointerException e){
        ResultAjax resultAjax = new ResultAjax();
        //错误的信息使用-1描述状态码
        resultAjax.setCode(-1);
        resultAjax.setMsg("空指针异常:"+ e.getMessage());
        resultAjax.setData(null);
        return resultAjax;
    }
}

这时候就会返回状态的描述信息:


也可以直接使用NullPointerException的父类

java 复制代码
@ExceptionHandler(Exception.class)
    public ResultAjax doException(Exception e){
        ResultAjax resultAjax = new ResultAjax();
        resultAjax.setCode(-1);
        resultAjax.setMsg("空指针异常:"+ e.getMessage());
        resultAjax.setData(null);
        return resultAjax;
    }

三、统一数据返回

🍅 为什么需要统一数据返回格式

统一数据返回格式的优点(为什么要统一):

  • 方便程序员更好的接收和解析后端数据接口返回的数据
  • 降低前端程序源和后端程序员的沟通成本,按照找某个格式实现,所有接口都这样返回
  • 有利于项目统一数据的维护和修改
  • 有利于后端技术部分的统一规范的标准制定,不会出现稀奇古怪的返回内容

🍅 统一数据返回格式

🎈定义同已返回类型

java 复制代码
@Data
public class ResultAjax {
    private int code;   //状态码
    private String msg; //状态码的描述信息
    private Object data;//返回数据

    /*
    * 返回成功
    * */
    public static ResultAjax success(Object data){
        ResultAjax resultAjax = new ResultAjax();
        resultAjax.setCode(200);
        resultAjax.setMsg("");
        resultAjax.setData(data);
        return resultAjax;
    }


    /*
    * 返回失败
    * */
    public static ResultAjax fail(int code,String msg){
        ResultAjax resultAjax = new ResultAjax();
        resultAjax.setCode(code);
        resultAjax.setMsg(msg);
        resultAjax.setData(null);
        return resultAjax;
    }

    public static ResultAjax fail(int code,String msg,Object data){
        ResultAjax resultAjax = new ResultAjax();
        resultAjax.setCode(code);
        resultAjax.setMsg(msg);
        resultAjax.setData(null);
        return resultAjax;
    }
}

🎈 同以数据处理

统一数据处理(强制执行):

  1. @ControllerAdvice
  2. 实现ResponseBodyAdvice接口,并且重写它其中的两个方法,supports必须返回true,beforeBodyWrite方法进行重新判断和重写操作
java 复制代码
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
    /*
    * 默认翻会true的时候
    * 才会执行beforeBodyWrite方法
    * */
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType,
                                  MediaType selectedContentType,
                                  Class selectedConverterType,
                                  ServerHttpRequest request,
                                  ServerHttpResponse response) {
        //已经包装好的对象
        if (body instanceof ResultAjax){
            return body;
        }
//        没有包装
        return ResultAjax.success(body);

    }
}

🎈业务类

java 复制代码
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/login")
    public ResultAjax login(){
        System.out.println("do login()");
        return ResultAjax.success("login");
    }
    
    @RequestMapping("/getnum")
    public int getNum(){
        return 1;
    }
}

其中login没有定义返回类型,getNum定义了返回类型,返回结果分别如下:

注意:

如果定义的返回值类型是String,那么会报错

java 复制代码
  @RequestMapping("/getstring")
    public String getString(){
        return "qqq";
    }

那么可以对String类型作出单独处理:

java 复制代码
@Autowired
    private ObjectMapper objectMapper;

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType,
                                  MediaType selectedContentType,
                                  Class selectedConverterType,
                                  ServerHttpRequest request,
                                  ServerHttpResponse response) {
        //已经包装好的对象
        if (body instanceof ResultAjax){
            return body;
        }
//        对字符串进行单独处理
        if (body instanceof String){
            ResultAjax resultAjax = ResultAjax.success(body);
            try {
                return objectMapper.writeValueAsString(resultAjax);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
//        没有包装
        return ResultAjax.success(body);
    }

返回结果:

相关推荐
hai4058715 分钟前
Spring Boot中的响应与分层解耦架构
spring boot·后端·架构
陈大爷(有低保)34 分钟前
UDP Socket聊天室(Java)
java·网络协议·udp
kinlon.liu1 小时前
零信任安全架构--持续验证
java·安全·安全架构·mfa·持续验证
哈喽,树先生1 小时前
1.Seata 1.5.2 seata-server搭建
spring·springcloud
王哲晓1 小时前
Linux通过yum安装Docker
java·linux·docker
java6666688881 小时前
如何在Java中实现高效的对象映射:Dozer与MapStruct的比较与优化
java·开发语言
Violet永存1 小时前
源码分析:LinkedList
java·开发语言
执键行天涯1 小时前
【经验帖】JAVA中同方法,两次调用Mybatis,一次更新,一次查询,同一事务,第一次修改对第二次的可见性如何
java·数据库·mybatis
Adolf_19931 小时前
Flask-JWT-Extended登录验证, 不用自定义
后端·python·flask
Jarlen1 小时前
将本地离线Jar包上传到Maven远程私库上,供项目编译使用
java·maven·jar