最近在工作中使用到了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. 2018 Multi-University Training Contest 10 CSGO(HDU - 6435)(最远曼哈顿距离)

    有 n 种主武器,m 种副武器.每种武器有一个基础分数k种属性值 X[i] . 选出一种主武器 mw 和一种副武器 sw,使得两种武器的分数和 + 每个属性的差值尽量大.(参考下面的式子) 多维的最远 ...

  2. 关于sizeof,对空指针sizeof(*p)可以吗?

    C/C++的sizeof在动态分配内存时经常用到,但之前一直没怎么关注它的具体机制.今天在为一个复杂声明的指针分配内存时,想起来要了解一下sizeof到底是什么? 先抛个问题: 程序运行过程中对空指针 ...

  3. UVa 11149 Power of Matrix 矩阵快速幂

    题意: 给出一个\(n \times n\)的矩阵\(A\),求\(A+A^2+A^3+ \cdots + A^k\). 分析: 这题是有\(k=0\)的情况,我们一开始先特判一下,直接输出单位矩阵\ ...

  4. luogu2473 [SCOI2008]奖励关

    题解参照这里 每个研究完记得乘一个1/n,这是乘了概率. #include <iostream> #include <cstdio> using namespace std; ...

  5. 用qemu+gdb tcp server+CDT调试linux内核启动-起步

    用qemu+gdb tcp server+CDT调试linux内核启动-起步 说明: 环境信息与 用virtualbox+模拟串口+CDT调试linux内核 TCP IP协议栈-起步 提到的一样,并且 ...

  6. webdriver高级应用- 精确比较页面截图图片

    判断两张图是否完全一致,如果存在任何不一致,会认为图片不匹配,代码如下: #encoding=utf-8 from selenium import webdriver import unittest, ...

  7. Selenium WebDriver-actionchain模拟键盘操作

    #encoding=utf-8 import unittest import time import chardet from selenium import webdriver from selen ...

  8. socket编程了解

    Socket 编程 Socket通讯原理描述: 套接字是为特定网络协议(例如TCP/IP,ICMP/IP,UDP/IP等)套件对上的网络应用程序提供者提供当前可移植标准的对象.它们允许程序接受并进行连 ...

  9. HLG2179 组合(dfs水水更健康)

    组合 Time Limit: 1000 MS Memory Limit: 32768 K Total Submit: 57(38 users) Total Accepted: 43(36 users) ...

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

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