我的编程空间,编程开发者的网络收藏夹
学习永远不晚

springboot有哪些实现拦截器的方式及异步执行是什么

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

springboot有哪些实现拦截器的方式及异步执行是什么

这篇文章主要讲解了“springboot有哪些实现拦截器的方式及异步执行是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“springboot有哪些实现拦截器的方式及异步执行是什么”吧!

目录
  • springboot 拦截器

  • springboot 入门案例

    • maven 引入

    • 启动类

    • 定义 Controller

  • 拦截器定义

    • 基于 Aspect

  • 基于 HandlerInterceptor

    • 基于 ResponseBodyAdvice

      • 测试

    • 异步执行

      • 定义异步线程池

      • 异步执行的 Controller

      • 思考

      • 测试

    • 反思

      springboot 拦截器

      实际项目中,我们经常需要输出请求参数,响应结果,方法耗时,统一的权限校验等。

      本文首先为大家介绍 HTTP 请求中三种常见的拦截实现,并且比较一下其中的差异。
      (1)基于 Aspect 的拦截器
      (2)基于 HandlerInterceptor 的拦截器
      (3)基于 ResponseBodyAdvice 的拦截器

      推荐阅读:

      统一日志框架: https://github.com/houbb/auto-log

      springboot有哪些实现拦截器的方式及异步执行是什么

      springboot 入门案例

      为了便于大家学习,我们首先从最基本的 springboot 例子讲起。

      maven 引入

      引入必须的 jar 包。

      <parent>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-parent</artifactId>    <version>1.5.9.RELEASE</version></parent><dependencies>    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-web</artifactId>    </dependency>    <dependency>        <groupId>org.aspectj</groupId>        <artifactId>aspectjrt</artifactId>        <version>1.8.10</version>    </dependency>    <dependency>        <groupId>org.aspectj</groupId>        <artifactId>aspectjweaver</artifactId>        <version>1.8.10</version>    </dependency></dependencies><!-- Package as an executable jar --><build>    <plugins>        <plugin>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-maven-plugin</artifactId>        </plugin>    </plugins></build>

      启动类

      实现最简单的启动类。

      @SpringBootApplicationpublic class Application {    public static void main(String[] args) {        SpringApplication.run(Application.class, args);    }}

      定义 Controller

      为了演示方便,我们首先实现一个简单的 controller。

      @RestControllerpublic class IndexController {    @RequestMapping("/index")    public AsyncResp index() {        AsyncResp asyncResp = new AsyncResp();        asyncResp.setResult("ok");        asyncResp.setRespCode("00");        asyncResp.setRespDesc("成功");        System.out.println("IndexController#index:" + asyncResp);        return asyncResp;    }}

      其中 AsyncResp 的定义如下:

      public class AsyncResp {    private String respCode;    private String respDesc;    private String result;    // getter & setter & toString()}

      拦截器定义

      基于 Aspect

      import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.context.annotation.EnableAspectJAutoProxy;import org.springframework.stereotype.Component;import java.util.Arrays;@Aspect@Component@EnableAspectJAutoProxypublic class AspectLogInterceptor {        private static final Logger LOG = LoggerFactory.getLogger(AspectLogInterceptor.class);        @Pointcut("execution(public * com.github.houbb.springboot.learn.aspect.controller..*(..))")    public void pointCut() {        //    }        @Around("pointCut()")    public Object around(ProceedingJoinPoint point) throws Throwable {        try {            //1. 设置 MDC            // 获取当前拦截的方法签名            String signatureShortStr = point.getSignature().toShortString();            //2. 打印入参信息            Object[] args = point.getArgs();            LOG.info("{} 参数: {}", signatureShortStr, Arrays.toString(args));            //3. 打印结果            Object result = point.proceed();            LOG.info("{} 结果: {}", signatureShortStr, result);            return result;        } finally {            // 移除 mdc        }    }}

      这种实现的优点是比较通用,可以结合注解实现更加灵活强大的功能。

      是个人非常喜欢的一种方式。
      主要用途:
      (1)日志的出参/入参
      (2)统一设置 TraceId
      (3)方法的调用耗时统计

      基于 HandlerInterceptor

      import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.stereotype.Component;import org.springframework.web.servlet.HandlerInterceptor;import org.springframework.web.servlet.ModelAndView;import javax.servlet.DispatcherType;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;@Componentpublic class LogHandlerInterceptor implements HandlerInterceptor {    private Logger logger = LoggerFactory.getLogger(LogHandlerInterceptor.class);    @Override    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {        // 统一的权限校验、路由等        logger.info("LogHandlerInterceptor#preHandle 请求地址:{}", request.getRequestURI());        if (request.getDispatcherType().equals(DispatcherType.ASYNC)) {            return true;        }        return true;    }    @Override    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {        logger.info("LogHandlerInterceptor#postHandle 调用");    }    @Override    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {    }}

      然后需要指定对应的 url 和拦截器之间的关系才会生效:

      import com.github.houbb.springboot.learn.aspect.aspect.LogHandlerInterceptor;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.WebMvcConfigurerAdapter;@Configurationpublic class SpringMvcConfig extends WebMvcConfigurerAdapter {    @Autowired    private LogHandlerInterceptor logHandlerInterceptor;    @Override    public void addInterceptors(InterceptorRegistry registry) {        registry.addInterceptor(logHandlerInterceptor)                .addPathPatterns("@ControllerAdvicepublic class MyResponseBodyAdvice implements ResponseBodyAdvice<Object> {        private static final Logger LOG = LoggerFactory.getLogger(MyResponseBodyAdvice.class);    @Override    public boolean supports(MethodParameter methodParameter, Class aClass) {        //这个地方如果返回false, 不会执行 beforeBodyWrite 方法        return true;    }    @Override    public Object beforeBodyWrite(Object resp, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {        String uri = serverHttpRequest.getURI().getPath();        LOG.info("MyResponseBodyAdvice#beforeBodyWrite 请求地址:{}", uri);        ServletServerHttpRequest servletServerHttpRequest = (ServletServerHttpRequest) serverHttpRequest;        HttpServletRequest servletRequest = servletServerHttpRequest.getServletRequest();        // 可以做统一的拦截器处理        // 可以对结果做动态修改等        LOG.info("MyResponseBodyAdvice#beforeBodyWrite 响应结果:{}", resp);        return resp;    }}

      测试

      我们启动应用,页面访问:
      http://localhost:18080/index
      页面响应:
      {"respCode":"00","respDesc":"成功","result":"ok"}

      后端日志:

      c.g.h.s.l.a.a.LogHandlerInterceptor      : LogHandlerInterceptor#preHandle 请求地址:/index
      c.g.h.s.l.a.aspect.AspectLogInterceptor  : IndexController.index() 参数: []
      IndexController#index:AsyncResp{respCode='00', respDesc='成功', result='ok'}
      c.g.h.s.l.a.aspect.AspectLogInterceptor  : IndexController.index() 结果: AsyncResp{respCode='00', respDesc='成功', result='ok'}
      c.g.h.s.l.a.aspect.MyResponseBodyAdvice  : MyResponseBodyAdvice#beforeBodyWrite 请求地址:/index
      c.g.h.s.l.a.aspect.MyResponseBodyAdvice  : MyResponseBodyAdvice#beforeBodyWrite 响应结果:AsyncResp{respCode='00', respDesc='成功', result='ok'}
      c.g.h.s.l.a.a.LogHandlerInterceptor      : LogHandlerInterceptor#postHandle 调用

      这里执行的先后顺序也比较明确,此处不再赘述。

      异步执行

      当然,如果只是上面这些内容,并不是本篇文章的重点。
      接下来,我们一起来看下,如果引入了异步执行会怎么样。

      定义异步线程池

      springboot 中定义异步线程池,非常简单。

      import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.task.AsyncTaskExecutor;import org.springframework.scheduling.annotation.EnableAsync;import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;@Configuration@EnableAsyncpublic class SpringAsyncConfig {    @Bean(name = "asyncPoolTaskExecutor")    public AsyncTaskExecutor taskExecutor() {        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();        executor.setMaxPoolSize(10);        executor.setQueueCapacity(10);        executor.setCorePoolSize(10);        executor.setWaitForTasksToCompleteOnShutdown(true);        return executor;    }}

      异步执行的 Controller

      @RestControllerpublic class MyAsyncController extends BaseAsyncController<String> {    @Override    protected String process(HttpServletRequest request) {        return "ok";    }    @RequestMapping("/async")    public AsyncResp hello(HttpServletRequest request) {        AsyncResp resp = super.execute(request);        System.out.println("Controller#async 结果:" + resp);        return resp;    }}

      其中 BaseAsyncController 的实现如下:

      @RestControllerpublic abstract class BaseAsyncController<T> {    protected abstract T process(HttpServletRequest request);    @Autowired    private AsyncTaskExecutor taskExecutor;    protected AsyncResp execute(HttpServletRequest request) {        // 异步响应结果        AsyncResp resp = new AsyncResp();        try {            taskExecutor.execute(new Runnable() {                @Override                public void run() {                    try {                        T result = process(request);                        resp.setRespCode("00");                        resp.setRespDesc("成功");                        resp.setResult(result.toString());                    } catch (Exception exception) {                        resp.setRespCode("98");                        resp.setRespDesc("任务异常");                    }                }            });        } catch (TaskRejectedException e) {            resp.setRespCode("99");            resp.setRespDesc("任务拒绝");        }        return resp;    }}

      execute 的实现也比较简单:
      (1)主线程创建一个 AsyncResp,用于返回。
      (2)线程池异步执行具体的子类方法,并且设置对应的值。

      思考

      接下来,问大家一个问题。
      如果我们请求 http://localhost:18080/async,那么:
      (1)页面得到的返回值是什么?
      (2)Aspect 日志输出的返回值是?
      (3)ResponseBodyAdvice 日志输出的返回值是什么?
      你可以在这里稍微停一下,记录下你的答案。

      测试

      我们页面请求 http://localhost:18080/async。

      页面响应如下:

      {"respCode":"00","respDesc":"成功","result":"ok"}

      后端的日志:

      c.g.h.s.l.a.a.LogHandlerInterceptor      : LogHandlerInterceptor#preHandle 请求地址:/async
      c.g.h.s.l.a.aspect.AspectLogInterceptor  : MyAsyncController.hello(..) 参数: [org.apache.catalina.connector.RequestFacade@7e931750]
      Controller#async 结果:AsyncResp{respCode='null', respDesc='null', result='null'}
      c.g.h.s.l.a.aspect.AspectLogInterceptor  : MyAsyncController.hello(..) 结果: AsyncResp{respCode='null', respDesc='null', result='null'}
      c.g.h.s.l.a.aspect.MyResponseBodyAdvice  : MyResponseBodyAdvice#beforeBodyWrite 请求地址:/async
      c.g.h.s.l.a.aspect.MyResponseBodyAdvice  : MyResponseBodyAdvice#beforeBodyWrite 响应结果:AsyncResp{respCode='00', respDesc='成功', result='ok'}
      c.g.h.s.l.a.a.LogHandlerInterceptor      : LogHandlerInterceptor#postHandle 调用

      对比一下,可以发现我们上面问题的答案:
      (1)页面得到的返回值是什么?

      {"respCode":"00","respDesc":"成功","result":"ok"}

      可以获取到异步执行完成的结果。
      (2)Aspect 日志输出的返回值是?

      AsyncResp{respCode='null', respDesc='null', result='null'}

      无法获取异步结果。
      (3)ResponseBodyAdvice 日志输出的返回值是什么?

      AsyncResp{respCode='00', respDesc='成功', result='ok'}

      可以获取到异步执行完成的结果。

      反思

      可以发现,spring 对于页面的响应也许和我们想的有些不一样,并不是直接获取同步结果。
      写到这里,发现自己对于 mvc 的理解一直只是停留在表面,没有真正理解整个流程。
      Aspect 的形式在很多框架中都会使用,不过这里会发现无法获取异步的执行结果,存在一定问题。

      感谢各位的阅读,以上就是“springboot有哪些实现拦截器的方式及异步执行是什么”的内容了,经过本文的学习后,相信大家对springboot有哪些实现拦截器的方式及异步执行是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

      免责声明:

      ① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

      ② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

      springboot有哪些实现拦截器的方式及异步执行是什么

      下载Word文档到电脑,方便收藏和打印~

      下载Word文档

      猜你喜欢

      springboot有哪些实现拦截器的方式及异步执行是什么

      这篇文章主要讲解了“springboot有哪些实现拦截器的方式及异步执行是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“springboot有哪些实现拦截器的方式及异步执行是什么”吧!目
      2023-06-20

      编程热搜

      • Python 学习之路 - Python
        一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
        Python 学习之路 - Python
      • chatgpt的中文全称是什么
        chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
        chatgpt的中文全称是什么
      • C/C++中extern函数使用详解
      • C/C++可变参数的使用
        可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
        C/C++可变参数的使用
      • css样式文件该放在哪里
      • php中数组下标必须是连续的吗
      • Python 3 教程
        Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
        Python 3 教程
      • Python pip包管理
        一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
        Python pip包管理
      • ubuntu如何重新编译内核
      • 改善Java代码之慎用java动态编译

      目录