日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

使用Guava的AbstractInvocationHandler正确完成代理

發(fā)布時(shí)間:2023/12/3 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 使用Guava的AbstractInvocationHandler正确完成代理 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

不太經(jīng)常,但有時(shí)我們被迫使用java.lang.reflect.Proxy編寫自定義動(dòng)態(tài)代理類 。 這種機(jī)制的確沒有魔力,而且即使您永遠(yuǎn)不會(huì)真正使用它,也值得知道-因?yàn)镴ava代理在各種框架和庫(kù)中無處不在。

這個(gè)想法很簡(jiǎn)單:動(dòng)態(tài)創(chuàng)建一個(gè)實(shí)現(xiàn)一個(gè)或多個(gè)接口的對(duì)象,但是每次調(diào)用這些接口的任何方法時(shí),都會(huì)調(diào)用我們的自定義回調(diào)處理程序。 該處理程序接收到一個(gè)被稱為( java.lang.reflect.Method實(shí)例)方法的句柄,并且可以以任何方式自由運(yùn)行。 代理通常用于實(shí)現(xiàn)無縫的模擬,緩存,事務(wù)和安全性,即它們是AOP的基礎(chǔ)。

在我從標(biāo)題解釋com.google.common.reflect.AbstractInvocationHandler的目的之前,讓我們從一個(gè)簡(jiǎn)單的示例開始。 假設(shè)我們要在線程池中透明地異步運(yùn)行給定接口的方法。 諸如Spring(請(qǐng)參閱: 27.4.3 The @Async Annotation )和Java EE(請(qǐng)參閱: Asynchronous Method Invocation )之類的流行堆棧已經(jīng)使用相同的技術(shù)來支持此功能。

假設(shè)我們提供以下服務(wù):

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

我們的目標(biāo)是異步運(yùn)行send()以便幾個(gè)后續(xù)調(diào)用不會(huì)阻塞而是排隊(duì),并在外部線程池中同時(shí)執(zhí)行,而不是在調(diào)用線程中執(zhí)行。 首先,我們需要將創(chuàng)建代理實(shí)例的工廠代碼:

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));} }

上面的代碼很少做出大膽的假設(shè),例如,一個(gè)underlying對(duì)象(我們正在代理的實(shí)際實(shí)例)恰好實(shí)現(xiàn)了一個(gè)接口。 在現(xiàn)實(shí)生活中,一門課程當(dāng)然可以實(shí)現(xiàn)多個(gè)接口,代理也可以實(shí)現(xiàn)多個(gè)接口,但是出于教育目的,我們對(duì)此進(jìn)行了一些簡(jiǎn)化。 現(xiàn)在,對(duì)于初學(xué)者,我們將創(chuàng)建無操作代理,該代理將委托給基礎(chǔ)對(duì)象而沒有任何附加值:

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將在以后使用。 最后一行至關(guān)重要–我們?cè)诰哂邢嗤琣rgs underlying實(shí)例上調(diào)用method 。 在這一點(diǎn)上,我們可以:

  • 是否調(diào)用underlying (例如,如果給定的呼叫被緩存/存儲(chǔ))
  • 更改參數(shù)(即出于安全目的)
  • 在異常之前/之后/周圍/上運(yùn)行代碼
  • 通過返回不同的值來改變結(jié)果(它必須與method.getReturnType()的類型匹配)
  • …以及更多

在我們的例子中,我們將method.invoke()與Callable一起Callable并異步運(yùn)行:

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方法。 使用這樣的代理很簡(jiǎn)單:

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

現(xiàn)在,即使RealMailServer.send()花費(fèi)一秒鐘完成,通過asyncMailServer.send()調(diào)用兩次也asyncMailServer.send()花費(fèi)時(shí)間,因?yàn)檫@兩個(gè)調(diào)用都是在后臺(tái)異步運(yùn)行的。

損壞的

一些開發(fā)人員不了解默認(rèn)InvocationHandler實(shí)現(xiàn)的潛在問題。 引用官方文件 :

如上所述,將對(duì)代理實(shí)例上java.lang.Object聲明的hashCode , equals或toString方法的調(diào)用進(jìn)行編碼,并分派給調(diào)用處理程序的invoke方法,就像對(duì)接口方法調(diào)用進(jìn)行編碼和分派一樣。

在我們的案例中,這意味著例如toString()與MailServer其他方法在同一線程池中執(zhí)行,這非常令人驚訝。 現(xiàn)在,假設(shè)您有一個(gè)本地代理,其中每個(gè)方法調(diào)用都會(huì)觸發(fā)遠(yuǎn)程調(diào)用。 通過網(wǎng)絡(luò)調(diào)度equals() , hashCode()和toString()絕對(duì)不是我們想要的。

Guava的AbstractInvocationHandler是一個(gè)簡(jiǎn)單的抽象類,可以正確處理上述問題。 默認(rèn)情況下,它將equals() , hashCode()和toString()調(diào)度到Object類,而不是將其傳遞給調(diào)用處理程序。 從直接的InvocationHandler重構(gòu)為AbstractInvocationHandler非常簡(jiǎn)單:

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()來幫助調(diào)試。 equals()和hashCode()都是從Object繼承而來的,一開始就很好。 現(xiàn)在,請(qǐng)查看您的代碼庫(kù)并搜索自定義代理。 如果到目前為止您還沒有使用AbstractInvocationHandler或類似的東西,很可能會(huì)引入一些細(xì)微的錯(cuò)誤。

參考: Java和社區(qū)博客上的Java 合作伙伴 Tomasz Nurkiewicz 使用Guava的AbstractInvocationHandler正確完成了代理 。

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

總結(jié)

以上是生活随笔為你收集整理的使用Guava的AbstractInvocationHandler正确完成代理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。