SpringBoot 异步线程间传递上下文方式

目录
  • 异步线程间传递上下文
    • 需求
    • 实现
      • 启用异步功能
      • 配置异步
      • 配置任务装饰器
  • 启用多线程安全上下文无法在线程间共享问题
    • 问题
    • 解决方案
    • 原理
    • 结果

异步线程间传递上下文

需求

SpringBoot项目中,经常使用@Async来开启一个子线程来完成异步操作。主线程中的用户信息需要传递给子线程

实现

启用异步功能

在启动类里加上@EnableAsync注解

@EnableAsync
@SpringBootApplication
public class Application {}

配置异步

新建一个配置类,实现AsyncConfigurer接口,并重写getAsyncExecutor方法

@Configuration
public class AsyncConfig implements AsyncConfigurer {
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(50);
        executor.setThreadNamePrefix("async-pool-");
        // 这一步是关键,异步Task装饰器
        executor.setTaskDecorator(new MyContextDecorator());
        executor.initialize();
        return executor;
    }
}

配置任务装饰器

新建一个异步任务装饰器,实现TaskDecorator接口,并重写decorate方法

public class MyContextDecorator implements TaskDecorator {
    @Override
    @Nonnull
    public Runnable decorate(@Nonnull Runnable runnable) {
  // 获取主线程中的请求信息(我们的用户信息也放在里面)
       RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
        return () -> {
            try {
               // 将主线程的请求信息,设置到子线程中
               RequestContextHolder.setRequestAttributes(attributes);
              // 执行子线程,这一步不要忘了
                runnable.run();
            } finally {
             // 线程结束,清空这些信息,否则可能造成内存泄漏
                RequestContextHolder.resetRequestAttributes();
            }
        };
    }

补充下:RequestContextHolder内部是基于ThreadLocal实现的,因此在使用set get时,都是和当前线程绑定的。当然,使用者的用户信息不一定放在了RequestContextHolder里面,读者可以自行扩展。

到此,通过@Async开启的子线程,就可以正常拿到父线程中的Request信息了。

启用多线程安全上下文无法在线程间共享问题

问题

项目中多线程添加数据,mybatisplus元数据填充功能,填充创建人时,数据是来自 spring security SecurityContextHolder.getContext.getAuthentication,同步操作时,能正常获取,而异步执行时空指针异常。

解决方案

配置安全上下文全局策略SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL)

原理

Spring Security 安全上下文默认策略为MODE_THREADLOCAL,ThreadLocal机制来保存每个使用者的安全上下文。

这意味着,只要针对某个使用者的逻辑执行都是在同一个线程中进行,即使不在各个方法之间以参数的形式传递其安全上下文,各个方法也能通过SecurityContextHolder工具获取到该安全上下文。

只要在处理完当前使用者的请求之后注意清除ThreadLocal中的安全上下文,这种使用ThreadLocal的方式是很安全的。

  • MODE_GLOBAL: JVM中所有的线程使用同一个安全上下文
  • MODE_INHERITABLETHREADLOCAL:有些应用会有自己的线程创建,并且希望这些新建线程也能使用创建者的安全上下文。这种效果,可以通过将SecurityContextHolder配置成MODE_INHERITABLETHREADLOCAL策略达到。

结果

在配置文件中添加:

@PostConstruct
public void init(){
    SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
}
 

@PostConstruct注解好多人以为是Spring提供的。其实是Java自己的注解。

Java中该注解的说明:@PostConstruct该注解被用来修饰一个非静态的void()方法。被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。PostConstruct在构造函数之后执行,init()方法之前执行。

通常我们会是在Spring框架中使用到@PostConstruct注解 该注解的方法在整个Bean初始化中的执行顺序:

Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • springboot 使用上下文获取bean

    问题 在使用springboot开发项目过程中,有些时候可能出现说会有在spring容器加载前就需要注入bean的类,这个时候如果直接使用@Autowire注解,则会出现控制针异常! 解决办法 如下: 创建一个springContextUtil类 package cn.eangaie.appcloud.util; import org.springframework.context.ApplicationContext; public class SpringContextUtil { priv

  • 详解在SpringBoot应用中获取应用上下文方法

    1.定义上下文工具类: package com.alimama.config; import org.springframework.context.ApplicationContext; /** * 上下文获取工具类 * @author mengfeiyang * */ public class SpringContextUtil { private static ApplicationContext applicationContext; public static void setAppl

  • SpringBoot上下文初始器加载过程详解

    利用 Spring 工厂加载机制,实例化 ApplicationContextInitializer 实现类,并排序对象集合. 关键方法 private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread()

  • SpringBoot 异步线程间传递上下文方式

    目录 异步线程间传递上下文 需求 实现 启用异步功能 配置异步 配置任务装饰器 启用多线程安全上下文无法在线程间共享问题 问题 解决方案 原理 结果 异步线程间传递上下文 需求 SpringBoot项目中,经常使用@Async来开启一个子线程来完成异步操作.主线程中的用户信息需要传递给子线程 实现 启用异步功能 在启动类里加上@EnableAsync注解 @EnableAsync @SpringBootApplication public class Application {} 配置异步 新建

  • Spring Boot异步线程间数据传递的四种方式

    目录 Spring Boot 自定义线程池实现异步开发 1. 手动设置 2. 线程池设置TaskDecorator 3. InheritableThreadLocal 4. TransmittableThreadLocal TransmittableThreadLocal原理 总结 Spring Boot 自定义线程池实现异步开发 Spring Boot 自定义线程池实现异步开发相信看过的都了解,但是在实际开发中需要在父子线程之间传递一些数据,比如用户信息,链路信息等等 比如用户登录信息使用Th

  • SpringBoot父子线程数据传递的五种方案介绍

    目录 方案1.ThreadLocal+TaskDecorator 方案2.RequestContextHolder+TaskDecorator 方案3.MDC+TaskDecorator 方案4.InheritableThreadLocal 方案5.TransmittableThreadLocal 方案对比 简答说一下InheritableThreadLocal 总结 方案1.ThreadLocal+TaskDecorator 用户工具类 UserUtils /** *使用ThreadLocal

  • wxpython多线程防假死与线程间传递消息实例详解

    wxpython中启用线程的方法,将GUI和功能的执行分开. 网上关于python多线程防假死与线程传递消息是几年前的,这里由于wxpython和threading模块已经更新最新,因此给出最新修改代码,能在2017年最新版的python和模块中运行. 原来的publisher()和callafter都无法使用. 修改后的代码. import time import wx from threading import Thread from wx.lib.pubsub import pub cla

  • 在Spring异步调用中传递上下文的方法

    什么是异步调用? 异步调用是相对于同步调用而言的,同步调用是指程序按预定顺序一步步执行,每一步必须等到上一步执行完后才能执行,异步调用则无需等待上一步程序执行完即可执行.异步调用指,在程序在执行时,无需等待执行的返回值即可继续执行后面的代码.在我们的应用服务中,有很多业务逻辑的执行操作不需要同步返回(如发送邮件.冗余数据表等),只需要异步执行即可. 本文将介绍 Spring 应用中,如何实现异步调用.在异步调用的过程中,会出现线程上下文信息的丢失,我们该如何解决线程上下文信息的传递. Sprin

  • c#线程间传递参数详解

    线程操作主要用到Thread类,他是定义在System.Threading.dll下.使用时需要添加这一个引用.该类提供给我们四个重载的构造函数(以下引自msdn). Thread (ParameterizedThreadStart)  初始化 Thread 类的新实例,指定允许对象在线程启动时传递给线程的委托.  Thread (ThreadStart)  初始化 Thread 类的新实例. Thread (ParameterizedThreadStart, Int32)  初始化 Threa

  • SpringBoot整合MQTT并实现异步线程调用的问题

    目录 为什么选择MQTT 使用背景 代码实现 基础代码 异步线程处理实现 为什么选择MQTT MQTT的定义相信很多人都能讲的头头是道,本文章也不讨论什么高大上的东西,旨在用最简单直观的方式让每一位刚接触的同行们可以最快的应用起来 先从使用MQTT需要什么开始分析: 消息服务器 不同应用/设备之间的频繁交互 可能涉及一对多的消息传递 基于SpringBoot通过注解实现对mqtt消息处理的异步调用 使用背景 生产环境下, 由于mqtt 生产者生产的消息逐渐增多, 可能会导致消息堆积. 因此需要消

  • springboot创建线程池的两种方式小结

    目录 springboot创建线程池两种方式 1.使用static代码块创建 2.使用@Configuration @bean注解,程序启动时创建 springboot开启线程池 定义线程池 使用 springboot创建线程池两种方式 1.使用static代码块创建 这样的方式创建的好处是当代码用到线程池的时候才会初始化核心线程数 具体代码如下: public class HttpApiThreadPool { /** 获取当前系统的CPU 数目*/ static int cpuNums =

  • springboot 正确的在异步线程中使用request的示例代码

    目录 起因: 发现有人踩过坑,但是没解决 尝试寻找官方支持 尝试自己解决 还是甩给官方 解决 结论 起因: 有后端同事反馈在异步线程中获取了request中的参数,然后下一个请求是get请求的话,发现会偶尔出现参数丢失的问题. 示例代码: @GetMapping("/getParams") public String getParams(String a, int b) { return "get success"; } @PostMapping("/po

  • Python如何实现线程间通信

    问题 你的程序中有多个线程,你需要在这些线程之间安全地交换信息或数据 解决方案 从一个线程向另一个线程发送数据最安全的方式可能就是使用 queue 库中的队列了.创建一个被多个线程共享的 Queue 对象,这些线程通过使用 put() 和 get() 操作来向队列中添加或者删除元素. 例如: from queue import Queue from threading import Thread # A thread that produces data def producer(out_q):

随机推荐