【SpringBoot 统一事物处理】统一登录拦截,统一异常处理,统一返回数据格式

1.登录拦截实现

对于登录拦截,实现分为两步:
1.创建自定义拦截器,实现HandlerInterceptor接口中的preHandle(执行方法之前的预处理)方法
2.将自定义拦截器加入WebMvcConfigurer的addInterceptors方法中.

1.1自定义拦截器

自定义拦截器也就是我们实现的一个普通类,实现接口HandlerInterceptor中preHandle方法.

注意:一个项目中可配置多个拦截器

代码实现:

package com.example.demo.config;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

//登录拦截
@Component
public class LoginIntercept implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session=request.getSession();
        if (session != null && session.getAttribute("userinfo") != null) {
            // 表示已经登录
            return true;
        }
        response.sendRedirect("/login.html");//未登录,此处跳转登录页面
        return false;
    }
}

1.2 将自定义拦截器加⼊到系统配置,并且设置拦截规则

此处注意使用注解@Configuration不是@Configurable,
将拦截规则加到系统配置中的重要一步:addInterceptors方法,否则拦截规则不生效

代码实现:

package com.example.demo.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class Appfig implements WebMvcConfigurer {

    @Autowired
    private LoginIntercept loginIntercept; //采用注入不用new

    @Override
    public void addInterceptors(InterceptorRegistry registry) {  //这个方法是将配置规则加入到系统配置中去
        registry.addInterceptor(loginIntercept).addPathPatterns("/**"). //addPathPatterns是拦截的路径
                excludePathPatterns("/login")													//excludePathPatterns是不拦截的
                .excludePathPatterns("/reg").
                excludePathPatterns("/login.html").
                excludePathPatterns("/reg.html");
//                excludePathPatterns("/**/*.js").
//                excludePathPatterns("/**/*.css").
//                excludePathPatterns("/**/*.png").
//                excludePathPatterns("/**/*.jpg");

    	}
    }

1.3 底层原码分析(了解即可)

普通执行程序调用关系如下图所示

在这里插入图片描述

加了拦截器之后,调用关系:

在这里插入图片描述

编译可得知:所有的 Controller 执⾏都会通过⼀个调度器 DispatcherServlet 来实现

在这里插入图片描述

查看doDispatch方法:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            try {
                ModelAndView mv = null;
                Object dispatchException = null;

                try {
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
                    mappedHandler = this.getHandler(processedRequest);
                    if (mappedHandler == null) {
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }

                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                    String method = request.getMethod();
                    boolean isGet = HttpMethod.GET.matches(method);
                    if (isGet || HttpMethod.HEAD.matches(method)) {
                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                        if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                            return;
                        }
                    }
									//调用预处理
                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }
									//执行Controller中的业务
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }

                    this.applyDefaultViewName(processedRequest, mv);
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                } catch (Exception var20) {
                    dispatchException = var20;
                } catch (Throwable var21) {
                    dispatchException = new NestedServletException("Handler dispatch failed", var21);
                }

                this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
            } catch (Exception var22) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
            } catch (Throwable var23) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
            }

        } finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            } else if (multipartRequestParsed) {
                this.cleanupMultipart(processedRequest);
            }

        }
    }

从上面原码可知:要执行Controller之前是要调用预处理方法(applyPreHandle)
预处理源码如下:

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex = i++) {
            HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
            if (!interceptor.preHandle(request, response, this.handler)) {
                this.triggerAfterCompletion(request, response, (Exception)null);
                return false;
            }
        }

        return true;
    }

在这里面,applyPreHandle会获取所有的拦截器HandlerInterceptor ,并且执行preHandle方法,这就会检测到我们自己的拦截器,因为实现了HandlerInterceptor 接口.

2.统一异常处理

统⼀异常处理使⽤的是 @ControllerAdvice + @ExceptionHandler 来实现的,@ControllerAdvice 表示控制器通知类,@ExceptionHandler 是异常处理器,两个结合表示当出现异常的时候执⾏某个通知,也就是执⾏某个⽅法事件

代码实现:

package com.example.demo.config;

import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.HashMap;

//异常处理 返回值为hashmap对象
@RestControllerAdvice //等同于 @ResponseBody + @ControllerAdvice
public class MyExceptionAdvice {

    //处理算数异常
    @ExceptionHandler(ArithmeticException.class)
    public HashMap<String, Object> arithmeticExceptionAdvice( ArithmeticException e){
        HashMap<String,Object> result=new HashMap<>();
        result.put("state", -1);
        result.put("data", null);
        result.put("msg", "算术异常:" + e.getMessage());

        return result;
    }


    //统一处理所有异常
    @ExceptionHandler(Exception.class)
    public HashMap<String, Object> exceptionAdvice(Exception e) {
        HashMap<String, Object> result = new HashMap<>();
        result.put("state", -1);
        result.put("data", null);
        result.put("msg", "all异常:" + e.getMessage());
        return result;
    }
    
}

3.统⼀数据返回格式

主要作用:当发生错误,遇到一些不可预知的问题时,后端返回的数据常常是不可控的,读不懂的,所以约定统一返回的数据格式,无论如何,不会返回一些稀奇古怪的内容.

3.1 统⼀数据返回格式实现

统⼀的数据返回格式可以使⽤ @ControllerAdvice + ResponseBodyAdvice 的⽅式实现

package com.example.demo.config;

import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.util.HashMap;

//返回数据统一处理 统一返回(json)格式
@ControllerAdvice
public class MyResponseAdvice implements ResponseBodyAdvice {

    /**
     * 返回一个 boolean 值,true 表示返回数据之前对数据进行重写,也就是会进入 beforeBodyWrite 方法,再返回
     * 如果返回 false 表示对结果不进行任何处理,直接返回
     */

    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;//表示可对数据重写 false 表示对结果不进行任何处理,直接返回
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        HashMap<String ,Object> result=new HashMap<>();
        result.put("state", 1);
        result.put("data", body);  //data数据全部给到body
        result.put("msg", "");
        return result;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值