springboot springmvc 拦截线程池线程执行业务逻辑

springboot:applicationTaskExecutor、taskExecutor、taskScheduler、executor是springboot内置包含的线程池,userInfoHolderExecutorProxy(代理ThreadPoolTaskExecutor)是我们业务线程池,我们想所有的线程池代理到ThreadPoolTaskExecutor,线程池异步执行前添加我们的登录用户信息(存储于HttpServletRequest属性中)。

import org.springblade.common.executor.UserInfoHolderExecutorProxy;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.task.TaskExecutorBuilder;
import org.springframework.context.annotation.*;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

@Configuration
public class TaskExecutionConfig {



	@Primary
	@Bean(
		name = {"applicationTaskExecutor", "taskExecutor", "taskScheduler","executor"}
	)
	public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {
		return  builder.build();
	}

	@Bean("userInfoHolderExecutorProxy")
	public UserInfoHolderExecutorProxy userInfoHolderExecutorProxy(ThreadPoolTaskExecutor threadPoolTaskExecutor) {
		return new UserInfoHolderExecutorProxy(threadPoolTaskExecutor);
	}
}


import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.util.concurrent.Executor;

/**
 * 使用多线程并行查询时,非主线程 尝试获取 用户上下文 (即httpServletRequest)时
 * 用户上下文为空,会导致 使用多线程查询的服务 无法使用多租户功能
 * 所以这个proxy在提交任务到线程池之前先保存线程的上下文,
 * 这样非主线程也能拿到主线程的用户上下文,从而使用多租户
 */

public class UserInfoHolderExecutorProxy implements Executor {

	/**
	 * 被代理的线程池
	 */
	private final Executor executor;

	public UserInfoHolderExecutorProxy(Executor executor) {
		this.executor = executor;
	}

	@Override
	public void execute(Runnable command) {

//		保存主线程的 用户上下文
		RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();

		executor.execute(() -> {

//			为线程池 设置用户上下文
			RequestContextHolder.setRequestAttributes(requestAttributes);
			try {
				command.run();
			} finally {
//				清理线程池线程的上下文
				RequestContextHolder.resetRequestAttributes();
			}
		});


	}
}

使用:

	@Autowired
	@Qualifier("userInfoHolderExecutorProxy")
	private Executor userInfoHolderExecutorProxy;


SpringMVC:
dispatcher-servlet.xml:

	<!--配置定时任务[请注释]-->
<!--    <task:executor id="executor" pool-size="10" />-->
    <!--配置线程池[请注释]-->
<!--    <task:scheduler id="scheduler" pool-size="10" />-->
    <!--配置定时任务-->
    <task:annotation-driven executor="executor" scheduler="scheduler" />

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.scheduling.SchedulingTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

@Configuration
public class ThreadPoolConfig {

    @Primary
    @Bean(name = {"userInfoHolderExecutorProxy", "executor"})
    public UserInfoHolderExecutorProxy threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();

        threadPoolTaskExecutor.setCorePoolSize(16);
        threadPoolTaskExecutor.setMaxPoolSize(400);
        threadPoolTaskExecutor.setQueueCapacity(Integer.MAX_VALUE);
        threadPoolTaskExecutor.setKeepAliveSeconds(120);
        threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
        threadPoolTaskExecutor.initialize();
        return new UserInfoHolderExecutorProxy(threadPoolTaskExecutor);
    }

    @Bean(name = {"schedulingTaskExecutor", "scheduler"})
    public SchedulingTaskExecutor schedulingTaskExecutor() {
        ThreadPoolTaskScheduler schedulingTaskExecutor = new ThreadPoolTaskScheduler();
        schedulingTaskExecutor.setPoolSize(10);
        return schedulingTaskExecutor;
    }
}


import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import wh.online.bim.bean.model.User;
import wh.online.bim.util.SystemContextHolder;

import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.Executor;

/**
 * 使用多线程并行查询时,非主线程 尝试获取 用户上下文 (即httpServletRequest)时
 * 用户上下文为空,会导致 使用多线程查询的服务 无法使用多租户功能
 * 所以这个proxy在提交任务到线程池之前先保存线程的上下文,
 * 这样非主线程也能拿到主线程的用户上下文,从而使用多租户
 */

public class UserInfoHolderExecutorProxy implements Executor {

	/**
	 * 被代理的线程池
	 */
	private final Executor executor;

	public UserInfoHolderExecutorProxy(Executor executor) {
		this.executor = executor;
	}

	@Override
	public void execute(Runnable command) {


//		保存主线程的 用户上下文
		RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
		User currentUser = SystemContextHolder.getCurrentUser();
		HttpServletRequest request = SystemContextHolder.getCurrentRequest();
		executor.execute(() -> {

//			为线程池 设置用户上下文
			RequestContextHolder.setRequestAttributes(requestAttributes);
			SystemContextHolder.add(currentUser);
			SystemContextHolder.add(request);
			try {
				command.run();
			} finally {
//				清理线程池线程的上下文
                RequestContextHolder.resetRequestAttributes();
				SystemContextHolder.remove();
			}
		});


	}
}

使用:

	@Autowired
	@Qualifier("userInfoHolderExecutorProxy")
	private Executor userInfoHolderExecutorProxy;

补充快速配置:

import org.springblade.common.threadpool.RequestContextHolderThreadPoolProxy;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.task.TaskExecutorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.task.AsyncListenableTaskExecutor;
import org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor;
import org.springframework.scheduling.concurrent.ConcurrentTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

import static org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME;

@Configuration
public class TreadPoolConfig {

	/**
	 * mybatisPlus的多租户插件依赖于AuthUtil.getTenantId(),又依赖于spring security的 request 线程请求上下文,这里新增的线程池配置能保证线程池中的线程能获取到主线程中的请求上下文。从而使得多租户插件生效
	 * @param builder
	 * @return
	 */
	@Lazy
	@Bean(name = {APPLICATION_TASK_EXECUTOR_BEAN_NAME,
		AsyncAnnotationBeanPostProcessor.DEFAULT_TASK_EXECUTOR_BEAN_NAME,
		"executor","taskScheduler"})
	public AsyncListenableTaskExecutor executor(TaskExecutorBuilder builder) {
		ThreadPoolTaskExecutor executor = builder.build();
		executor.initialize();
		Executor requestContextHolderThreadPoolProxy = new RequestContextHolderThreadPoolProxy(executor);

		return new ConcurrentTaskExecutor(requestContextHolderThreadPoolProxy);
	}




}
import org.jetbrains.annotations.NotNull;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.util.concurrent.Executor;

public class RequestContextHolderThreadPoolProxy implements Executor {

	private final Executor executor;

	public RequestContextHolderThreadPoolProxy(Executor executor) {
		this.executor = executor;
	}

	@Override
	public void execute(@NotNull Runnable command) {

		RequestAttributes mainTreadRequestAttributes = RequestContextHolder.getRequestAttributes();

		executor.execute(() -> {
			RequestContextHolder.setRequestAttributes(mainTreadRequestAttributes);
			try {

				command.run();
			} finally {
				RequestContextHolder.resetRequestAttributes();
			}
		});
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值