guava的正确引入方式_使用Guava的AbstractInvocationHandler正确完成代理
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聲明的hashCode , equals或toString方法的調用進行編碼,并將其分派到調用處理程序的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或類似的程序,則可能會引入一些細微的錯誤。
翻譯自: https://www.javacodegeeks.com/2013/12/proxies-done-right-with-guavas-abstractinvocationhandler.html
guava的正確引入方式
總結
以上是生活随笔為你收集整理的guava的正确引入方式_使用Guava的AbstractInvocationHandler正确完成代理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 诡秘的近义词 诡秘的近义词有哪些
- 下一篇: 抖音青少年模式怎么关