guava的正确引入方式

不太经常,但有时我们被迫使用java.lang.reflect.Proxy编写自定义动态代理类 。 这种机制实际上没有任何魔术,即使您永远不会真正使用它,也值得知道–因为Java代理在各种框架和库中无处不在。

这个想法很简单:动态创建一个实现一个或多个接口的对象,但是每次调用这些接口的任何方法时,都会调用我们的自定义回调处理程序。 该处理程序接收到一个被称为( java.lang.reflect.Method实例)方法的句柄,并且可以以任何方式自由执行。 代理通常用于实现无缝的模拟,缓存,事务和安全性-即它们是AOP的基础。

在我从标题解释com.google.common.reflect.AbstractInvocationHandler的目的之前,让我们从一个简单的示例开始。 假设我们要在线程池中异步透明地运行给定接口的方法。 像Spring(参见: 27.4.3 The @Async Annotation )和Java EE(参见: Asynchronous Method Invocation )之类的流行堆栈已经使用相同的技术来支持此功能。

假设我们提供以下服务:

public interface MailServer {void send(String msg);int unreadCount();
}

我们的目标是异步运行send()以便几个后续调用不会阻塞而是排队,并在外部线程池中同时执行,而不是在调用线程中执行。 首先,我们需要将创建代理实例的工厂代码:

public class  AsyncProxy {public static <T> T wrap(T underlying, ExecutorService pool) {final ClassLoader classLoader = underlying.getClass().getClassLoader();final Class<T> intf = (Class<T>) underlying.getClass().getInterfaces()[0];return (T)Proxy.newProxyInstance(classLoader,new Class<?>[] {intf},new AsyncHandler<T>(underlying, pool));}
}

上面的代码做出了一些大胆的假设,例如,一个underlying对象(我们正在代理的真实实例)恰好实现了一个接口。 在现实生活中,一门课程当然可以实现多个接口,代理也可以实现多个接口,但是出于教育目的,我们对此进行了一些简化。 现在,对于初学者,我们将创建无操作代理,该代理将委托给基础对象而没有任何附加值:

class AsyncHandler<T> implements InvocationHandler {private static final Logger log = LoggerFactory.getLogger(AsyncHandler.class);private final T underlying;private final ExecutorService pool;AsyncHandler1(T underlying, ExecutorService pool) {this.underlying = underlying;this.pool = pool;}@Overridepublic Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable {return method.invoke(underlying, args);}}

ExecutorService pool将在以后使用。 最后一行至关重要–我们在具有相同args underlying实例上调用method 。 在这一点上,我们可以:

  • 是否调用underlying (例如,如果给定的呼叫被缓存/存储)
  • 更改参数(即出于安全目的)
  • 在异常之前/之后/周围/上运行代码
  • 通过返回不同的值来改变结果(它必须与method.getReturnType()的类型匹配)
  • …以及更多

在我们的例子中,我们将method.invoke()Callable包装在一起,并异步运行它:

class AsyncHandler<T> implements InvocationHandler {private final T underlying;private final ExecutorService pool;AsyncHandler(T underlying, ExecutorService pool) {this.underlying = underlying;this.pool = pool;}@Overridepublic Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable {final Future<Object> future = pool.submit(new Callable<Object>() {@Overridepublic Object call() throws Exception {return method.invoke(underlying, args);}});return handleResult(method, future);}private Object handleResult(Method method, Future<Object> future) throws Throwable {if (method.getReturnType() == void.class)return null;try {return future.get();} catch (ExecutionException e) {throw e.getCause();}}
}

提取了额外的handleResult()方法以正确处理非void方法。 使用这样的代理很简单:

final MailServer mailServer = new RealMailServer();final ExecutorService pool = Executors.newFixedThreadPool(10);
final MailServer asyncMailServer = AsyncProxy.wrap(mailServer, pool);

现在,即使RealMailServer.send()花费一秒钟完成,通过asyncMailServer.send()调用两次也asyncMailServer.send()花费时间,因为这两个调用都是在后台异步运行的。

损坏的

一些开发人员不了解默认InvocationHandler实现的潜在问题。 引用官方文件 :

如上所述,将对代理实例上java.lang.Object声明的hashCodeequalstoString方法的调用进行编码,并将其分派到调用处理程序的invoke方法,就像对接口方法的调用进行编码和分派一样,如上所述。

在我们的案例中,这意味着toString()MailServer其他方法在同一线程池中执行,这非常令人惊讶。 现在,假设您有一个本地代理,其中每个方法调用都会触发远程调用。 通过网络调度equals()hashCode()toString()绝对不是我们想要的。

Guava的AbstractInvocationHandler是一个简单的抽象类,可以正确处理上述问题。 默认情况下,它将equals()hashCode()toString()调度到Object类,而不是将其传递给调用处理程序。 从直接的InvocationHandler重构为AbstractInvocationHandler非常简单:

import com.google.common.reflect.AbstractInvocationHandler;class AsyncHandler<T> extends AbstractInvocationHandler {//...@Overrideprotected Object handleInvocation(Object proxy, final Method method, final Object[] args) throws Throwable {//...}@Overridepublic String toString() {return "Proxy of " + underlying;}
}

而已! 我决定重写toString()来帮助调试。 equals()hashCode()都是从Object继承而来的,一开始就很好。 现在,请查看您的代码库并搜索自定义代理。 如果到目前为止您尚未使用AbstractInvocationHandler或类似的程序,则可能会引入一些细微的错误。

参考: Java和社区博客上的JCG合作伙伴 Tomasz Nurkiewicz 用Guava的AbstractInvocationHandler正确完成了代理 。

翻译自: https://www.javacodegeeks.com/2013/12/proxies-done-right-with-guavas-abstractinvocationhandler.html

guava的正确引入方式

guava的正确引入方式_使用Guava的AbstractInvocationHandler正确完成代理相关推荐

  1. guava缓存数据到本地_扩展Guava缓存以溢出到磁盘

    guava缓存数据到本地 缓存使您可以轻松地显着加速应用程序. Java平台的两种出色的缓存实现是Guava缓存和Ehcache . 尽管Ehcache功能丰富得多(例如其Searchable API ...

  2. 异星工场服务器直连,#联机教程#异星工厂多人游戏的正确打开方式_异星工厂吧_百度贴吧...

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 3互联网服务器 上文提及互联网游戏实际为基于国内网络环境的局域网,如何创建可以显示在官方互联网游戏联机平台才能世界人民大团结.也是体现正版玩家联机便利性所 ...

  3. 经常玩电脑正确的坐姿_细说用电脑的正确坐姿

    常用电脑必须保证正确的坐姿 1. 上半身应保持颈部直立,使头部获得支撑,两肩自然下垂,上臂贴近身体,手肘弯曲呈90度,操作键盘或滑鼠,尽量使手腕保持水平姿势,手掌中线与前臂中线 应保持一直线.下半身腰 ...

  4. 关于python的常量和变量下列描述正确的是_下面关于数据类型说法正确的是?

    [填空题]计算机自己的语言,其指令是以 代码的形式存在,历史上计算机使用的程序语言,经历了 语言,之后是 语言,20世纪50年代,出现了 语言,高级程序设计语言编写的指令成为 [名词解释]综合赔付率 ...

  5. 经常玩电脑正确的坐姿_电脑族玩游戏正确坐姿 7要点坐不伤身

    现代人一整天大部分时间都坐在椅子上,日本嵴骨神经医学专家木津直昭在<医学大发现!坐姿决定你的健康>一书中指出,长时间维持同样的姿势,包覆肌肉.肌腱.骨头.关节及内脏的「筋膜」就会产生沾黏现 ...

  6. 连接 mysql 数据库的正确方法是_连接 MySQL 数据库的正确方法是

    连接侣山堂书院的创建者是( ) 据库乐官太师是<诗经>的最早编集者 确方<诗经>是中国文学现实主义的鼻祖 连接下列属于<诗经>的表现手法的是 <蒹葭>的 ...

  7. 关于python类说法正确的是_关于Python的说法正确的是

    [判断题]1-5压强是大量分子对器壁碰撞的结果,具有统计意义. [单选题]1-10 在常温下有1mol的氢气和1mol的氦气各一瓶,若将它们升高相同的温度,则 [单选题]1-8 [单选题]2-8 一容 ...

  8. java中赋值语句不正确的是_下面哪一个赋值语句不正确

    [单选题]下列说法中错误的是 ( ) [单选题]下列哪一类人是希腊城邦时代的公民?() [单选题]在音乐创作中使两条或者更多条相互独立的旋律同时发声并且彼此融洽的技术称为(). [单选题]AT89C5 ...

  9. 使用Guava的AbstractInvocationHandler正确完成代理

    不太经常,但有时我们被迫使用java.lang.reflect.Proxy编写自定义动态代理类 . 这种机制的确没有魔力,而且即使您永远不会真正使用它,也值得知道-因为Java代理在各种框架和库中无处 ...

最新文章

  1. Unity创造没有代码的游戏学习教程
  2. The current directory must be set to the ITT directory解决办法
  3. node-express-1
  4. [JSOI2008]火星人
  5. Request.InputStream 将数据作为XML数据发送
  6. 从零点五开始用Unity做半个2D战棋小游戏(一)
  7. java获取实体类的属性和值
  8. poj2778DNA Sequence (AC自动机+矩阵快速幂)
  9. win10更新后无法进入linux,双系统win10更新后无法进入linux
  10. 10分钟虚拟设备接入阿里云IoT平台实战
  11. 浅谈NB-IoT应用场景及方案
  12. 深度学习优化算法大全系列7:NAdam,算法选择,调参
  13. 计算机英语词汇的特点,计算机专业英语词汇特点.ppt
  14. 高速电路设计与仿真之PCB篇(二)
  15. Java 责任链模式demo
  16. win10 OCX控件的注册
  17. UV材质图片合并后UV坐标的变换
  18. 平面桁架静力计算程序
  19. 性能测试连载 (7)-jmeter 压力测试中的难点解析
  20. Tomcat 部署方式

热门文章

  1. Codeforces Round #672 (Div. 2)
  2. 【AC自动机】屏蔽词删除(ybtoj AC自动机-4)
  3. 实战 | 利用Delta Lake使Spark SQL支持跨表CRUD操作
  4. 面试阿里挂了却拿到网易、点我达offer,一个三年经验Java程序员的面试总结
  5. 面试:你说你精通Java并发,给我讲讲Java并发之J.U.C
  6. UserCF,基于用户的协同过滤算法
  7. (十)IDEA添加mybatis-mapp.xml文件
  8. 【填坑】博客搬家造成的博客重复问题
  9. 《四世同堂》金句摘抄(九)
  10. struts+hibernate+oracle+easyui实现lazyout组件的简单案例——工具类