java动态代理_Java 动态代理和依赖注入
【注】本文譯自:https://psamsotha.github.io/jersey/2015/12/16/dynamic-proxies-dependency-injection.html
本文將討論動態(tài)代理如何在 Java 平臺中工作以及依賴注入如何利用此 Java 功能。本文的撰寫源于我在 HK2 框架(或更確切地說是 Jersey 應(yīng)用程序中的 HK2)中嘗試將請求范圍對象注入到單例對象中的的搜索。我本來打算將我的發(fā)現(xiàn)都寫在一個博客里,但是我覺得這個主題太寬泛了,不適合用兩行代碼就能解決的問題。
首先,我將快速討論代理模式,然后展示如何在 Java 語言中使用動態(tài)代理,最后介紹一個使用動態(tài)代理和自定義依賴項注入的示例。
代理模式
我不會過多介紹代理模式。在互聯(lián)網(wǎng)上到處都有很好的參考。我只給出一個簡短的類比,以及該模式的一些簡短代碼示例。
我敢肯定,你們大多數(shù)人都聽過“委托投票”這個說法。當有人投票給其他人時。例如,說公司董事會成員之間有一些隨意的投票。成員 B 生病住院了,因此無法參加董事會會議。所以,成員 A 代表成員 B 進行了投票。因此,在表決會議上,成員 A 只是成員 B 的委托。
代理模式的工作原理與此相同。這是一個類圖(來自維基百科)。
假設(shè) Member 是接口
public?interface?Member?{????void?vote();}那么你有 MemberA 和 MemberB
public?class?MemberA?implements?Member?{????public?void?vote()?{}}public?class?MemberB?implements?Member?{????public?void?vote()?{}}由于成員 B 將不存在,因此我們需要一個代理。 代理還應(yīng)實現(xiàn) Member 接口,并應(yīng)包含對 MemberB 的引用。
public?class?MemberBProxy?implements?Member?{????private?MemberB?memberB;????public?void?vote?{????????memberB.vote();????}}現(xiàn)在 MemberA 可以讓 a 成為成員 B 的代理,讓代理為成員 B 進行代理投票。
這可能不是最好的例子,因為代理僅對成員 B 進行表決。但是對于真正的代理,通常情況下還會發(fā)生其他事情。例如,在使用遠程代理的情況下, vote() 方法實際上可能會對遠程 MemberB 進行網(wǎng)絡(luò)調(diào)用。Java平臺中的一個示例就是 RMI(遠程方法調(diào)用)。后面的示例將描述另一個用例,通常對于開發(fā)人員來說是透明的。
動態(tài)代理
在上面的示例中,我們終須手動編寫代理類。但是,在 Java 中,隨著 1.3 中引入動態(tài)代理,這不是必需的。動態(tài)代理的核心接口是 java.lang.reflect.Proxy。要使用它,我們需要組件,我們的代理接口和一個 InvocationHandler。 讓我們看一個簡單的例子,使用與上面相同的類比。
Member?memberBProxy?=?(Memeber)?Proxy.newProxyInstance(????????Memeber.class.getClassLoader(),????????new?Class[]?{?Member.class?},????????new?InvocationHandler()?{????????????@Override????????????public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?{????????????????return?method.invoke(new?MemberB(),?args);????????????}????????});就是這樣。現(xiàn)在 memberBProxy 是 Proxy 的一個實例,而不是 MemberB 的實例。如果你打印出 Member 對象的類名,實際上會看到類名是 com.sun.proxy.ProxyX,而不是 MemberB。
讓我們快速地瀏覽一下。這是 Proxy#newProxyInstance 的簽名
newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h)它首先需要 ClassLoader 用于定義代理,其次需要一個實現(xiàn)的接口,最后是 InvocationHandler。InvocationHandler 只有一個需要實現(xiàn)的 callback 方法。
Object invoke(Object proxy, Method method, Object[] args)第一個參數(shù)是實際的代理對象。你應(yīng)該很少使用它。第二個參數(shù)是 java.lang.reflect.Method 。如果你有 Java 反射的經(jīng)驗,那么應(yīng)該熟悉這個接口。 使用 Method 時,我們可以通過傳遞要調(diào)用該方法的對象以及任何參數(shù)(最后一行)來調(diào)用:
return method.invoke(new MemberB(), args);在這里,代理將方法和傳遞給代理之上的方法調(diào)用的參數(shù)傳遞給代理。作為 InvocationHandler 的實現(xiàn)者,我們可以使用 Method 和method 參數(shù)做我們想做的任何事情。這里我們只是簡單地,應(yīng)該在 new MemberB() 對象中調(diào)用,并傳遞參數(shù)。
為了獲得更清楚地了解情況,只需將其看作 Proxy 實例具有 Member 接口具有的所有方法。因此,當我們調(diào)用 Proxy#vote() 時,它會調(diào)用 InvocationHandler#invoke 本身、方法和傳遞給 vote() 的參數(shù)(如果有)。通過調(diào)用 Method 對象上的 inovoke,InvocationHandler 實現(xiàn)只需調(diào)用該方法即可。然后,Method 對象將對實際的 MemberB 對象調(diào)用 vote()。
就是這樣。如你所見,動態(tài)代理很容易實現(xiàn)。
動態(tài)代理和自定義注入示例
- 獲取 GitHub 項目(https://github.com/psamsotha/dynamic-proxies-example)
我將在這里嘗試解釋的是在依賴項注入(DI)框架中如何使用動態(tài)代理。DI 中動態(tài)代理的主要用例之一是處理范圍。例如,您有一個處于單例作用域中的服務(wù)或控制器,這意味著每個應(yīng)用僅創(chuàng)建一個。該單例服務(wù)依賴于請求范圍內(nèi)的服務(wù)器,這意味著應(yīng)為每個請求創(chuàng)建一個服務(wù)器。類可能看起來像這樣(這完全是虛構(gòu)的---沒有特定的框架)。
@Controller(scope?=?"singleton")public?class?AppController?{????@Inject????SingletonService?service;}@Singletonpublic?class?SingletonService?{????@Inject????RequestScopedService?service;}@RequestScopedpublic?class?RequestScopedService?{}這里的問題是,在啟動時創(chuàng)建 SingletonService 時,需要執(zhí)行所有注入。但是在啟動時沒有請求,因此當前應(yīng)該沒有綁定到請求的 RequestScopedService。另一個問題是我們?nèi)绾喂芾砟膫€請求獲取哪個 RequestScopedService。也許我們可以在 SingletonService 中添加一個 setter,在其中我們可以為每個請求設(shè)置一個新的 RequestScopedService。但這是行不通的,因為 SingletonService 將被并發(fā)訪問,就像一些服務(wù)器的工作方式一樣(每個請求一個線程)。
這是動態(tài)代理發(fā)揮作用的地方。This is where dynamic proxies come to the rescue. When the當啟動創(chuàng)建 SingletonService 時我們將注入服務(wù)的 Proxy,而不是注入實際的 RequestScopedService。當從 from inside the SingletonService, 內(nèi)部對 RequestScopedService 進行調(diào)用時,實際上將在 Proxy 上進行調(diào)用,該代理將調(diào)用委派給 InvocationHandler#invoke 方法,該方法實現(xiàn)對從 ThreadLocal 獲得的 RequestScopedService 的調(diào)用。每次處理一個請求時,都會在 ThreadLocal 中設(shè)置一個新的 RequestScopedService,這個請求將在一個單獨的線程中處理。如果你聽說過 “線程本地代理”一詞,那么這幾乎就是它的工作原理。
讓我們嘗試實現(xiàn)這些。我們甚至將實現(xiàn)自己的依賴注入。我們將實現(xiàn)一個簡單的服務(wù)器框架,該框架允許用戶實現(xiàn)一個自定義 RequestHandler ,它可以注入我們的 SingletonService ,而 SingletonService 又依賴于 RequestScopedService。以下是類圖。(接下來,最好從上面的鏈接中獲取 GitHub 項目)。
如前所述,用戶將能夠?qū)崿F(xiàn)自定義 RequestHandler 并注入我們的 SingletonService。在項目中,有一個默認的實現(xiàn),它僅只返回來自 SingletonService 的消息作為 Response。
public?class?DefaultRequestHandler?implements?RequestHandler?{????????@Inject????private?SingletonService?singletonService;????@Override????public?Response?handleRequest(Request?request)?{????????return?new?Response(singletonService.getMessage());????}??}然后用戶創(chuàng)建 Server 傳入實現(xiàn)類給構(gòu)造器。
Server server = new Server(DefaultRequestHandler.class);
在服務(wù)器構(gòu)造函數(shù)中,您將看到兩件事,用戶定義的 RequestHandler 類驗證,以及創(chuàng)建 SingletonService。驗證在這里并不重要,這是 SingletonService 的創(chuàng)建。
private?static?SingletonService?initSingletonService()?{????Service?proxyService?=?(Service)?Proxy.newProxyInstance(????????????Service.class.getClassLoader(),?new?Class[]{Service.class},????????????new?ServiceInvocationHandler());????return?new?SingletonService(proxyService);}我們要做的第一件事是創(chuàng)建 Service 類的代理。這里是 ServiceInvocationHandler
public?class?ServiceInvocationHandler?implements?InvocationHandler?{????@Override????public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?..?{????????Service?service?=?ThreadLocalService.get();????????return?method.invoke(service,?args);????}}它沒有太大作用。它只是從 ThreadLocalService 檢索 Service ,并在服務(wù)上調(diào)用代理方法。稍后我們將看到,RequestScopedService 的實例被設(shè)置為 ThreadLocal。
然后,使用 SingletonService 創(chuàng)建 Service。所以現(xiàn)在,當 SingletonService調(diào)用 Service 上的方法時,代理將查找線程本地 Service 并將調(diào)用委托給其方法。
服務(wù)器引導(dǎo)程序就是這樣。現(xiàn)在我們進入運行時和請求處理。下面是來自請求處理流的序列圖。
首先,Main 程序調(diào)用 Server#sendRequest(Request) 并傳入一個新的 Request 對象。 Request 對象僅具有客戶端的名稱。
當我們在 Server 上調(diào)用 sendRequest 時,它所做的就是將請求添加到 BlockingQueue。
public?void?sendRequest(Request?request)?{????try?{????????requests.put(request);????}?catch?(InterruptedException?ex)?{????????throw?new?RuntimeException(ex);????}}服務(wù)器啟動時,它會不斷輪詢 BlockingQueue,等待新的 Request
public?void?startServer()?{????executors.submit(new?Runnable()?{????????@Override????????public?void?run()?{????????????while?(true)?{????????????????try?{????????????????????Request?request?=?requests.take();????????????????????if?(request.isShutdownTrigger())?{????????????????????????break;????????????????????}????????????????????executors.submit(new?RequestProcessor(userDefineHandler,??????????????????????????????????????????????????????????singletonService,??????????????????????????????????????????????????????????request));????????????????}?catch?(InterruptedException?ex)?{????????????????????throw?new?RuntimeException(ex);????????????????}????????????}????????????System.out.println("Server?shutdown!");????????}????});??}當接收到 Request 時,Server 將創(chuàng)建一個新的 RequestProcessor,傳入 Request 對象、SingletonService 對象和使用定義的 RequestHandler 類。如果你查看 RequestProcessor 的 run() 方法,你將看到以下內(nèi)容
private?void?setThreadLocalService()?{????ThreadLocalService.set(new?RequestScopedService(request));}@Overridepublic?void?run()?{????setThreadLocalService();????RequestHandler?handler?=?initInjections();????Response?response?=?handler.handleRequest(request);????System.out.println(response.getMessage());}因此,處理器要做的第一件事就是將 RequestScopedService 設(shè)置為 ThreadLocalService。 然后使用一些反射實例化 RequestHandler ,如 initInjections() 方法所示
RequestHandler?initInjections()?{????try?{????????for?(Field?field?:?handlerCls.getDeclaredFields())?{????????????if?(field.isAnnotationPresent(Inject.class)????????????????????&&?field.getType()?==?SingletonService.class)?{????????????????return?createHandler(field,?handlerCls);????????????}????????}????????Constructor[]?cons?=?handlerCls.getConstructors();????????for?(Constructor?con?:?cons)?{????????????if?(con.isAnnotationPresent(Inject.class)????????????????????&&?con.getParameterCount()?==?1????????????????????&&?con.getParameterTypes()[0]?==?SingletonService.class)?{????????????????return?(RequestHandler)?con.newInstance(singletonService);????????????}????????}????}?catch?(Exception?ex)?{????????throw?new?RuntimeException(ex);????}????throw?new?RuntimeException("RequestHandler?could?not?be?created.");}該方法只是檢查我們是否應(yīng)該進行字段注入或構(gòu)造函數(shù)注入。它確保使用 @Inject 注解字段或構(gòu)造函數(shù)。如果字段帶有注解,并且字段類型為 SingletonService,我們將使用反射使用 SingletonService 設(shè)置字段。進程構(gòu)造函數(shù)注入也會發(fā)生類似事件。
RequestHander 做的最后一件事就是簡單地調(diào)用 RequestHandler 的 handleRequestof ,它返回一個 Response,然后處理器打印出 Response 消息。這就是處理單個請求的過程。
如果運行 Main 類,應(yīng)該會看到類似以下內(nèi)容的信息
Message: Hello Kobe??meta-info:????service class: com.sun.proxy.$Proxy2????service id: 1????thread name: pool-1-thread-2Message: Hello Lebron??meta-info:????service class: com.sun.proxy.$Proxy2????service id: 2????thread name: pool-1-thread-3... three more您應(yīng)該注意的第一件事是,服務(wù)類確實是 Proxy 實例,而不是 RequestScopedService。只要正在處理請求,底層的 RequestScopedService 都將保持不變。因此所有在 Service 內(nèi)部的服務(wù)上所做的將總是被委托給與特定線程相關(guān)聯(lián)的實際 RequestScopedService 。
就這樣了。
總結(jié)
我們介紹了代理模式的一些基礎(chǔ)知識,并了解了它如何使用包裝器或委托模型來調(diào)用底層對象。然后,我們討論了動態(tài)代理,以及如何用 Java 語言實現(xiàn)它。最后,我們通過一個示例說明了動態(tài)代理是如何使用作用域依賴注入的。如果需要將較小作用域的對象注入到更大作用域的對象中,則需要代理較小作用域的對象,以便不同的線程能夠訪問自己的較小作用域的對象實例。
這個示例與你在現(xiàn)實生活中使用的一些東西相去甚遠,但我希望你能更好地理解我們討論的兩個主題(動態(tài)代理和依賴注入)的組全背后發(fā)生的事情。
總結(jié)
以上是生活随笔為你收集整理的java动态代理_Java 动态代理和依赖注入的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 云丁密码锁使用说明书_郑州智能密码锁多少
- 下一篇: java框架_2020年Java框架排行