1. 场景还原
其实最开始, 我是在用 java swing
做一个 endpoint-io-transfer
的应用工具(一个用于从citrix下载文件的工具).
页面里面有几个按钮, 点击按钮执行逻辑功能, 相关代码大概是这样:
private JComponent getToolBar() {
// ..... 无关代码省略 .....
JButton singleScanButton = new JButton("单次扫描");
singleScanButton.addActionListener(e -> {
// ..... 省略按钮逻辑代码, 执行可能耗费500ms以上 .....
});
// ..... 无关代码省略 .....
}
java swing
的按钮执行是同步的, 如果执行逻辑耗费时间较长会导致gui
不响应, 而且如果短时间多次点击的话会导致执行多次, 于是就想着处理一下.
处理方式很简单, 就是用锁和多线程搞一下就行了, 但是会把代码搞得很难看.
按钮有10个左右, 虽然可以使用公共类或静态方法的形式简化, 但是这次我却盯上了中间的那个 lambda 表达式.
我想着 能不能对这个lambda表达式进行处理, 处理后的lambda表达式直接能够满足我的需求呢?
于是就捣鼓了下去.
2. 处理点击事件
一会儿, 我就写好了这样的类
/**
* 将传入的 Consumer<T> then 作为 被代理函数式接口实例 传入, 将该对象的 onceExe 方法作为 代理方法实例 返回
*
* 逻辑: 完成 函数式接口实例 的 异步单线程 代理
*/
public static class OnceExecutorForConsumer<T> {
private final Lock lock = new ReentrantLock();
/**
* 被代理的方法
*/
private final Consumer<T> then;
private Consumer<T> skip;
public OnceExecutorForConsumer(Consumer<T> then) {
this.then = then;
}
/**
* 代理方法
*
* <p>
* 每次调用新建一个线程对参数进行处理, 主线程不阻塞, 分线程竞争锁, 抢到运行 then, 抢不到运行 skip
* </p>
*/
public void onceExe(final T e) {
new Thread(() -> {
if (lock.tryLock()) {
try {
if (then != null) {
then.accept(e);
}
} finally {
lock.unlock();
}
} else {
if (skip != null) {
skip.accept(e);
}
}
}).start();
}
public OnceExecutorForConsumer<T> setSkip(Consumer<T> skip) {
this.skip = skip;
return this;
}
}
原来的按钮部分代码就成了下面的调用方式
private JComponent getToolBar() {
// ..... 无关代码省略 .....
JButton singleScanButton = new JButton("单次扫描");
singleScanButton.addActionListener(new OnceExecutorForConsumer<>((ActionEvent e) -> {
// ..... 省略按钮逻辑代码, 执行可能耗费500ms以上 .....
}).setSkip(e -> log.debug("多次点击无效"))::onceExe);
// ..... 无关代码省略 .....
}
如此完美完成了既定目标
3. 分析 OnceExecutorForConsumer
实际上, 在写 OnceExecutorForConsumer 的时候, 我就已经发现了, OnceExecutorForConsumer 会是一个很强的代理类.
和静态代理不同, 这种代理方式代理的是函数式接口, 将一个函数式接口作为被代理对象
传入, 传出一个代理的函数式接口, 然后这个函数式接口就可以实现对原有函数式接口对象的代理.
-
众所周知, 静态代理的弊端之一是代理类和被代理类需要实现同一个接口, 代理类, 被代理类 和接口之间耦合度很高, 同一个功能, 接口和被代理类发生变化, 那么代理类也要跟着变化, 代理类简直成适配器了.
然而, 如果对函数式接口对象进行代理的话会怎么样呢?
- 相同结构的函数式接口可以相互转化.
- 函数式接口结构很少, 也就只有 参数和返回值的区别而已.
- 不通的函数式接口实例也可以通过简单的方式适配连接在一起.
可见, 函数式接口实例的代理不会有
代理类和代理对象的接口问题, 导致代理类泛滥的问题
, 这就使得这种代理模式相当灵活. -
在 java 中, 万物皆对象, 对象中的方法也看作是一个对象, 这个对象可以转换为函数式接口实例.
也就是说, 这种代理功能极其强大, 它能够直接和间接代理所有方法. 一类在手, 简直天下我有啊有木有.
-
这种代理方式主打成为针对方法的工具类, 相对于普通的
方法操控方法
来说, 它有一个闭包的优势, 闭包背后有一个完整的类, 这可以赋予他强大的功能.
起名:闭包代理
我们看下这种代理方式, 它是构造类的过程中将函数方法变成它的一个成员变量, 之后返回创建的类的另一个方法作为代理方法, 之后整个对象仅仅往外暴漏了一个方法, 也就是说因为这个方法被外界引用, 导致这个对象能够存活下去.
大家想到了什么, 这就是Js中的闭包逻辑啊!
虽然java中也有闭包, 但那个闭包我直接忽略了!
那么到这里, 实际上称呼也就定了, 闭包加代理, 那就闭包代理
啊!
其实本来我想起名为函数代理或函数式接口代理来着, 但是吧, 前一个感觉像数学, 后一个名字太长.
从此, java里面除了静态代理 & 动态代理之外, 至少在我的字典里面就可以新增一种代理方式了: 闭包代理
这篇文章关于闭包代理仅仅写了个开头,
如果你对后续内容感兴趣,