最近在工作中使用到了spring自带的@Async,主要是为了把其中耗时多、响应慢、计算复杂的业务抽出来查询。从代码设计上看Spring自带的比传统线程池提交在代码层次上看起来优雅简洁了不少,无需显示去申明线程池相关代码, 在方法上加注解既可异步返回结果。空闲时间大概看了下原理,其实就是代理模式(cglib or 接口), 刚好最近学习到线程和并发相关的jdk组件,今天就打算自己手动实现这个异步组件。

总体实现思路流程:客户端调用后-》通过代理模式代理-》重写Submit方法并返回Future-》把Future 放到自定义的异步返回包装类-》客户端直接拿到返回的Future 进行阻塞get

流程图:

以下是接口代理实现,至于Cglib代理实现和接口代理原理是一致的,可以自行实现。

1.新建代理接口


public interface IAsyncProxy {

    /**
* 获取代理对象
* 1. 如果是实现了接口,默认使用 dynamic proxy 即可。
* 2. 如果没有实现接口,默认使用 CGLIB 实现代理。
* @return 代理对象
*/
Object proxy(); }

2.新建动态代理实现类

public class DynamicProxy implements InvocationHandler, IAsyncProxy {

    /**
* 被代理的对象
*/
private final Object target; public DynamicProxy(Object target) {
this.target = target;
} /**
*
*
* @param proxy 原始对象
* @param method 方法
* @param args 入参
* @return 结果
* @throws Throwable 异常
*/
@Override
@SuppressWarnings("all")
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return AsyncExecutor.submit(target, method, args);
} @Override
public Object proxy() {
// 我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
InvocationHandler handler = new DynamicProxy(target); return Proxy.newProxyInstance(handler.getClass().getClassLoader(),
target.getClass().getInterfaces(), handler);
}
}

 其中最主要的是代理之后我们要如何交给异步处理,在invoke方法内,我通过线程池去提交任务,细心的可以发现AsyncExecutor在jdk包里是没有的,这个类是我自己定义的。至于原因有以下几个:

1.jdk自带的ExecutorService的submit无法满足现有功能, 所以需要重新实现ExecutorService做扩展,重写submit方法。

2.submit之后需要包装统一的返回结果

3.定义异步接口

/**
* <p> 异步执行结果 </p>
/
public interface IAsyncResult<T> extends Future<T> { /**
* 获取执行的结果
* @return 结果
*/
Object getResult(); }

  

抽象的异步返回类

public abstract class AbstractAsyncResult<T> implements IAsyncResult<T> {

    @Override
public boolean cancel(boolean mayInterruptIfRunning) {
return false;
} @Override
public boolean isCancelled() {
return false;
} @Override
public boolean isDone() {
return false;
} @Override
public T get() throws InterruptedException, ExecutionException {
try {
return this.get(AsyncConstant.DEFAULT_TIME_OUT, TimeUnit.SECONDS);
} catch (TimeoutException e) {
throw new RuntimeException(e);
}
} }

  

返回结果类

/**
* 异步执行结果 */
public class AsyncResult<T> extends AbstractAsyncResult<T> { /**
* future 信息
*/
private Future<T> future; /**
* 结果
*/
private Object value; /**
* 获取执行的结果
* @return 结果
*/
@Override
public Object getResult() {
// 直接返回结果
if(future == null) {
return this.getValue();
} try {
T t = future.get();
// 这里拿到的 AsyncResult 对象
if(null != t) {
return ((AsyncResult)t).getValue();
}
return null;
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
} @Override
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return future.get(timeout, unit);
} public Object getValue() {
return this.value;
} public void setValue(Object value) {
this.value = value;
} public void setFuture(Future<T> future) {
this.future = future;
} }

  

4.定义一个异步接口,继承ExecutorService

/**
* <p> 异步框架执行器 </p>
*/
public interface IAsyncExecutor extends ExecutorService {
}
/**
* 异步执行器
*/
public class AsyncExecutor extends ThreadPoolExecutor implements IAsyncExecutor { //region 私有属性
/**
* 是否初始化
*/
private static volatile boolean isInit = false; /**
* 是否被销毁
*/
private static volatile boolean isDestroy = false; /**
* 线程执行器
*/
private static ExecutorService executorService = null;
//endregion //region 构造器
public AsyncExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
} public AsyncExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
} public AsyncExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
} public AsyncExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
}
//endregion @SuppressWarnings("all")
public static <T> IAsyncResult<T> submit(Object target, Method method, Object[] objects) {
// 初始化的判断
if(!isInit) {
init();
}
//通过线程池提交
Future future = executorService.submit(new Runnable() {
@Override
public void run() {
try {
method.invoke(target, objects);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}); //Future future = executorService.submit(() -> method.invoke(target, objects)); AsyncResult<T> asyncResult = new AsyncResult<>();
asyncResult.setFuture(future);
return asyncResult;
} /**
* 初始化
* 1. 暂时不添加配置相关的信息
* 2. 最后调整状态
*/
private static synchronized void init() {
try {
if(isInit) {
return;
} // 各种属性配置
// 淘汰策略
// 最佳线程数量
executorService = Executors.newFixedThreadPool(10);
updateExecutorStatus(true);
} catch (Exception e) {
throw new AsyncRuntimeException(e);
}
} /**
* 销毁容器
* 1. 销毁的时候进行等待,确保任务的正常执行完成。
* 2. 任务执行的统计信息,后期添加。
*/
private static synchronized void destroy() {
if(isDestroy) {
return;
} executorService = null;
updateExecutorStatus(false);
} /**
* 更新执行器的状态
* @param initStatus 初始化状态
*/
private static void updateExecutorStatus(final boolean initStatus) {
isInit = initStatus;
isDestroy = !isInit;
}
}

  

 

总结:

  以上代码最核心的就是通过动态代理在内部通过线程池异步提交,以及重写jdk的线程池也是为了能够重写内部核心方法

【原】手写spring async异步组件的更多相关文章

  1. 手写Spring MVC

    闲及无聊 又打开了CSDN开始看一看有什么先进的可以学习的相关帖子,这时看到了一位大神写的简历装X必备,手写Spring MVC. 我想这个东西还是有一点意思的 就拜读了一下大佬的博客 通读了一遍相关 ...

  2. 手写Spring事务框架

    Spring事务基于AOP环绕通知和异常通知 编程事务 声明事务 Spring事务底层使用编程事务+AOP进行包装的   = 声明事务 AOP应用场景:  事务 权限 参数验证 什么是AOP技术 AO ...

  3. Spring学习之——手写Spring源码V2.0(实现IOC、D、MVC、AOP)

    前言 在上一篇<Spring学习之——手写Spring源码(V1.0)>中,我实现了一个Mini版本的Spring框架,在这几天,博主又看了不少关于Spring源码解析的视频,受益匪浅,也 ...

  4. 手写spring

    体系结构 Spring 有可能成为所有企业应用程序的一站式服务点,然而,Spring 是模块化的,允许你挑选和选择适用于你的模块,不必要把剩余部分也引入.下面的部分对在 Spring 框架中所有可用的 ...

  5. 手写Spring AOP,快来瞧一瞧看一看撒!

    目录 AOP分析 Advice实现 定义Advice接口 定义前置.后置.环绕和异常增强接口 Pointcut实现 定义PointCut接口 定义正则表达式的实现类:RegExpressionPoin ...

  6. 一个老程序员是如何手写Spring MVC的

    人见人爱的Spring已然不仅仅只是一个框架了.如今,Spring已然成为了一个生态.但深入了解Spring的却寥寥无几.这里,我带大家一起来看看,我是如何手写Spring的.我将结合对Spring十 ...

  7. 【Spring】手写Spring MVC

    Spring MVC原理 Spring的MVC框架主要由DispatcherServlet.处理器映射.处理器(控制器).视图解析器.视图组成. 完整的Spring MVC处理 流程如下: Sprin ...

  8. 我是这样手写 Spring 的(麻雀虽小五脏俱全)

    人见人爱的 Spring 已然不仅仅只是一个框架了.如今,Spring 已然成为了一个生态.但深入了解 Spring 的却寥寥无几.这里,我带大家一起来看看,我是如何手写 Spring 的.我将结合对 ...

  9. 《四 spring源码》利用TransactionManager手写spring的aop

    事务控制分类 编程式事务控制          自己手动控制事务,就叫做编程式事务控制. Jdbc代码: Conn.setAutoCommite(false);  // 设置手动控制事务 Hibern ...

随机推荐

  1. Django之用户认证

    用户认证组件简介 功能:用session记录登录验证状态 前提:必须使用django自带的auth_user表.那这里有的同学就会有疑问了,自己不能创建自己的用户表吗? 当然可以,用户认证组件虽然只针 ...

  2. HDU - 4763 Theme Section (KMP的next数组的应用)

    给定一个字符串,求出一个前缀A,使得字符串的构成可以表示成ABABA的形式(B可以为空串). 输出这个前缀的最大长度. KMP算法Next数组的使用. 枚举中间的每个位置,可以根据Next数组求出这个 ...

  3. linux中的部分宏

    *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...

  4. 《小团团团队》第八次团队作业:Alpha冲刺

    项目 内容 这个作业属于哪个课程 任课教师博客主页链接 这个作业的要求在哪里 实验十二 团队作业8:软件测试与Alpha冲刺 团队名称 小团团团队 作业学习目标 (1)掌握软件测试基础技术; (2)学 ...

  5. day04_01 知识回顾、算术运算符

    ","和"+"的区别 除法运算,整除//,别名"地板除" 取余数 2**10 2的10次方 指数运算 指数运算符优先级要比乘法要高,所以先算 ...

  6. configparser模块——用于生成和修改常见配置文档

    配置文档格式 [DEFAULT] ServerAliveInterval = 45 Compression = yes CompressionLevel = 9 ForwardX11 = yes [b ...

  7. Stringsobits(模拟)

    描述 Consider an ordered set S of strings of N (1 <= N <= 31) bits. Bits, of course, are either ...

  8. mysql处理添加外键时 error 150 问题

    当你试图在mysql中创建一个外键的时候,这个出错会经常发生,这是非常令人沮丧的.像这种不能创建一个.frm 文件的报错好像暗示着操作系统的文件的权限错误或者其它原因,但实际上,这些都不是的,事实上, ...

  9. 用Javascript实现图片的缓慢缩放效果

    <body> <!--页面布局:一张图片两个按钮--> <div style = "width:400px;margin:0 auto"> &l ...

  10. FZU Problem 2200 cleaning dp

    Problem Description N个人围成一圈在讨论大扫除的事情,需要选出K个人.但是每个人与他距离为2的人存在矛盾,所以这K个人中任意两个人的距离不能为2,他们想知道共有多少种方法. Inp ...