SpringBoot 统一功能处理

这篇具有很好参考价值的文章主要介绍了SpringBoot 统一功能处理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一. Spring AOP 用户同一登录验证问题

  • 登录、注册页面不拦截,其他页面都拦截
  • 当登录成功写入 session 之后,拦截的页面可正常访问

1.1 自定义拦截器

@Configuration
public class LoginAspect implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("do LoginAspect");
        HttpSession session=request.getSession(false);
        if(session==null||session.getAttribute("")==""){
            response.setContentType("text/html;charset=utf8");
            response.getWriter().write("当前未登录");
            return false;
        }
        return true;
    }
}

1.2 将自定义的拦截器加入到系统配置

@Configuration
public class WebMVC implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginAspect())
                //拦截所有接口
                .addPathPatterns("/**")
                //排除接口
                .excludePathPatterns("/user/login.html")
                .excludePathPatterns("/user/reg.html")
                //排除静态资源中image包下的所有资源
                .excludePathPatterns("/image/**");
    }
}

1.3 拦截器的调用顺序与实现原理

调用顺序:

正常情况下,程序会在调用 Controller 之前进行相应的业务处理(我们在切面中定义的事务),业务通过后,才会调用Controller 层,然后就是Controller -> Serrvice -> Mapper -> 数据库

实现原理:

所有的 Controller 执行都会通过一个调度器 DispatcherServlet 来实现,这一点可以从 Spring Boot 控制台的打印信息看出:

SpringBoot 统一功能处理,spring boot,java,后端,spring,java-ee

而所有方法都会执行 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 ServletException("Handler dispatch failed: " + var21, var21);
                }

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

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

        }
    }

 从上述源码中可以看出在开始执行 Controller 之前,会先调用预处理方法 applyPreHandle, 而 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;
    }

从上述源码中可以看出,在 applyHandle 中会获取所有的拦截器 HandlerInterceptor 并执行拦截器中的 preHandle 方法,此时就和我们定义的拦截器对应上了。

@Configuration
public class LoginAspect implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("do LoginAspect");
        HttpSession session=request.getSession(false);
        if(session==null||session.getAttribute("")==""){
            response.setContentType("text/html;charset=utf8");
            response.getWriter().write("当前未登录");
            return false;
        }
        return true;
    }
}

当我们的拦截器返回false后,applyHandle 也会返回false ,此时 DispatcherServlet 就会直接返回,不会再执行我们 Controller 层的业务代码了。

二. 统一异常处理

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

出现异常统一返回 json 格式报错信息:

定义返回报错信息格式:

@Data
public class ResultAjax {
    private int code;
    private String mes;
    private String data;
}

实现异常通知

@ControllerAdvice
@ResponseBody
public class ExeceptionAdvice {
    @ExceptionHandler(Exception.class)
    public ResultAjax handler(Exception e){
        ResultAjax resultAjax=new ResultAjax();
        resultAjax.setCode(-1);
        resultAjax.setMes(e.getMessage());
        return resultAjax;
    }
}

三. 统一数据返回格式

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

  • 方便前端程序员更好的接收和解析后端数据接口返回的数据.
  • 降低前后端程序员的沟通成本
  • 有利于项目统一数据的维护和修改
  • 有利于后端技术部门的统一规范的标准制定,不会出现稀奇古怪的返回内容

3.2 统一数据返回格式的实现

统一的数据返回格式可以使用 @Controller + ResponseBodyAdvice 的方式实现:

定义返回数据格式:

@Data
public class ResultAjax {
    private int code;
    private String mes;
    private Object data;
    public static ResultAjax succ(String mes){
        ResultAjax resultAjax=new ResultAjax();
        resultAjax.setCode(200);
        resultAjax.setMes(mes);
        return resultAjax;
    }
    public static ResultAjax succ(Object data){
        ResultAjax resultAjax=new ResultAjax();
        resultAjax.setCode(200);
        resultAjax.setMes("");
        resultAjax.setData(data);
        return resultAjax;
    }

    public static ResultAjax succ(String mes,Object data){
        ResultAjax resultAjax=new ResultAjax();
        resultAjax.setCode(200);
        resultAjax.setMes(mes);
        resultAjax.setData(data);
        return resultAjax;
    }

    public static ResultAjax succ(int code,String mes,Object data){
        ResultAjax resultAjax=new ResultAjax();
        resultAjax.setCode(code);
        resultAjax.setMes(mes);
        resultAjax.setData(data);
        return resultAjax;
    }
    public static ResultAjax fail(String mes){
        ResultAjax resultAjax=new ResultAjax();
        resultAjax.setCode(-1);
        resultAjax.setMes(mes);
        return resultAjax;
    }
    public static ResultAjax fail(String mes,Object data){
        ResultAjax resultAjax=new ResultAjax();
        resultAjax.setCode(-1);
        resultAjax.setMes(mes);
        resultAjax.setData(data);
        return resultAjax;
    }
}

对返回数据类型拦截进行统一判断和强制转换:

@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
    @Autowired
    private ObjectMapper mapper;
    /*
    * 返回 ture 才会调用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) {
        ResultAjax resultAjax=new ResultAjax();
        //若返回数据类型就是 json 格式,直接返回
        if(body instanceof ResultAjax){
            return body;
        }
        //针对 String 类型的返回值需要额为处理
        if(body instanceof String){
            try {
                //将ResultAjax 转换为 json 格式字符串
                return mapper.writeValueAsString(resultAjax.succ(body));
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
        return resultAjax.succ(body);
    }
}

为什么需要对String 类型的返回值进行额外处理?

SpringMVC 在进行初始化的时候,默认会注册⼀些⾃带的 HttpMessageConverter,MessageConverter 意思是消息转换器。

1)ByteArrayHttpMessageConverter
2)StringHttpMessageConverter
3)SourceHttpMessageConverter
4)AllEncompassingFormHttpMessageConverter

当我们返回数据的时候,框架会自动选择合适的消息转换器,这个选择转换器的时机是在我们对返回值进行包装之前就选定了。当我们返回的数据类型为String时,框架就会自动选择StringHttpMessageConverter进行处理,但是由于我们对返回类型进行了封装,所以后续调用StringHttpMessageConverter进行包装的时候类型就不是String的了,所以就出现了类型转换错误,因此我们需要将返回的数据转换为 json 格式的字符串,而不是直接返回对象。文章来源地址https://www.toymoban.com/news/detail-820882.html

到了这里,关于SpringBoot 统一功能处理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Spring Boot】拦截器与统一功能处理:统一登录验证、统一异常处理与统一数据返回格式

     Spring AOP是一个基于面向切面编程的框架,用于将横切性关注点(如日志记录、事务管理)与业务逻辑分离,通过代理对象将这些关注点织入到目标对象的方法执行前后、抛出异常或返回结果时等特定位置执行,从而提高程序的可复用性、可维护性和灵活性。但使用原生Sp

    2024年02月16日
    浏览(45)
  • 【Spring】SpringBoot 统一功能处理

    在日常使用 Spring 框架进行开发的时候,对于一些板块来说,可能需要实现一个相同的功能,这个功能可以是验证你的登录信息,也可以是其他的,但是由于各个板块实现这个功能的代码逻辑都是相同的,如果一个板块一个板块进行添加的话,开发效率就会很低,所以 Spring

    2024年01月18日
    浏览(39)
  • 【Spring Boot统一功能处理】统一异常处理,统一的返回格式,@ControllerAdvice简单分析,即将走进SSM项目的大门! ! !

    前言: 大家好,我是 良辰丫 ,在上一篇文章中我们已经学习了一些统一功能处理的相关知识,今天我们继续深入学习这些知识,主要学习统一异常处理,统一的返回格式,@ControllerAdvice简单分析.💌💌💌 🧑个人主页:良辰针不戳 📖所属专栏:javaEE进阶篇之框架学习 🍎励志语句:生

    2024年02月16日
    浏览(39)
  • Spring Boot 统一功能处理(拦截器实现用户登录权限的统一校验、统一异常返回、统一数据格式返回)

    目录 1. 用户登录权限校验 1.1 最初用户登录权限效验 1.2 Spring AOP 用户统⼀登录验证 1.3 Spring 拦截器 (1)创建自定义拦截器 (2)将自定义拦截器添加到系统配置中,并设置拦截的规则 1.4 练习:登录拦截器 (1)实现 UserController 实体类 (2)返回的登录页面:login.html (3)实

    2024年02月12日
    浏览(48)
  • Spring Boot 优雅实现统一数据返回格式+统一异常处理+统一日志处理

            在我们的项目开发中,我们都会对数据返回格式进行统一的处理,这样可以方便前端人员取数据,当然除了正常流程的数据返回格式需要统一以外,我们也需要对异常的情况进行统一的处理,以及项目必备的日志。         在项目开发中返回的是json格式的数据

    2024年01月19日
    浏览(42)
  • Spring Boot 统一数据返回格式 分析 和 处理

    目录 实现统一数据格式  测试   原因分析  解决方案 🎥 个人主页:Dikz12 📕格言:吾愚多不敏,而愿加学 欢迎大家👍点赞✍评论⭐收藏 统⼀的数据返回格式使⽤ @ControllerAdvice 和 ResponseBodyAdvice 的⽅式实现; @ControllerAdvice : 表⽰控制器通知类. 比如:添加类 ResponseAdvic

    2024年04月08日
    浏览(47)
  • Spring Boot实现统一异常处理的技术解析

    引言 在软件开发过程中,异常处理是非常重要的一环。一个好的异常处理机制可以帮助我们更好地定位问题,提高代码的可维护性和稳定性。Spring Boot作为一款轻量级的Java开发框架,提供了一种简单而高效的方式来实现统一异常处理。本文将详细介绍如何使用Spring Boot实现统

    2024年01月21日
    浏览(41)
  • Spring统一功能处理

    获取参数复杂 AOP的规则相对简单 2.1.1. 自定义拦截器 新建interceptor文件夹 2.1.2. 将自定义拦截器加入到系统配置 新建config文件夹 2.1.3. 业务代码 方法1 方法2 2.3.1. 总体思路 2.3.2. 部分源码分析 拦截器是基于AOP实现的 (AOP基于动态代理(JDK|CGLIB)) 参考上一节 拦截器的应用 对于下面

    2024年02月11日
    浏览(30)
  • 【Spring Boot 使用Filter统一处理请求数据转换】

    Spring Boot Filter 使用场景 身份验证和授权 场景描述: 在用户访问应用程序的敏感资源之前,需要验证用户的身份并授权用户访问特定的内容。 实现方式: 使用Filter拦截请求,检查HTTP请求中的身份验证令牌(如JWT),并确定用户是否具有执行操作的权限。 日志记录和审计 场景

    2024年02月21日
    浏览(43)
  • SpringBoot 统一功能的处理

    1.1 最初用户登录验证 从上述代码中可以看出每个方法都相同的登录权限校验 , 这样做的缺点是: 每个方法中都要单独写用户登录验证的方法 , 即使封装成公共方法 , 也一样要在方法中传参判断. 添加控制器越多, 调用用户登录的方法也越多 , 这样后期会增大维护成本. 用户登录

    2024年02月16日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包