好程式設計師Java培訓分享Java動態代理機制詳解
好程式設計師Java培訓分享Java動態代理機制詳解, 在學習Spring的時候,我們知道Spring主要有兩大思想,一個是IoC,另一個就是AOP,對於IoC,依賴注入就不用多說了,而對於Spring的核心AOP來說,我們不但要知道怎麼透過AOP來滿足的我們的功能,我們更需要學習的是其底層是怎麼樣的一個原理,而AOP的原理就是java的動態代理機制,所以本篇隨筆就是對java的動態機制進行一個回顧。
在java的動態代理機制中,有兩個重要的類或介面,一個是 InvocationHandler(Interface)、另一個則是 Proxy(Class),這一個類和介面是實現我們動態代理所必須用到的。首先我們先來看看java的API幫助文件是怎麼樣對這兩個類進行描述的:
InvocationHandler:
1InvocationHandler is the
interface
implemented by the invocation handler of a proxy instance.
2
3Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.
每一個動態代理類都必須要實現InvocationHandler這個介面,並且每個代理類的例項都關聯到了一個handler,當我們透過代理物件呼叫一個方法的時候,這個方法的呼叫就會被轉發為由InvocationHandler這個介面的 invoke 方法來進行呼叫。我們來看看InvocationHandler這個介面的唯一一個方法 invoke 方法:
1Object invoke(Object proxy, Method method, Object[] args) throws Throwable
我們看到這個方法一共接受三個引數,那麼這三個引數分別代表什麼呢?
· proxy : 指代我們所代理的那個真實物件
· method : 指代的是我們所要呼叫真實物件的某個方法的Method物件
· args : 指代的是呼叫真實物件某個方法時接受的引數
如果不是很明白,等下透過一個例項會對這幾個引數進行更深的講解。
接下來我們來看看Proxy這個類:
1Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods.
Proxy這個類的作用就是用來動態建立一個代理物件的類,它提供了許多的方法,但是我們用的最多的就是 newProxyInstance 這個方法:
1public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
1Returns an instance of a proxy class for the specified interfaces that dispatches method invocations to the specified invocation handler.
這個方法的作用就是得到一個動態的代理物件,其接收三個引數,我們來看看這三個引數所代表的含義:
· loader : 一個ClassLoader物件,定義了由哪個ClassLoader物件來對生成的代理物件進行載入
· interfaces : 一個Interface物件的陣列,表示的是我將要給我需要代理的物件提供一組什麼介面,如果我提供了一組介面給它,那麼這個代理物件就宣稱實現了該介面(多型),這樣我就能呼叫這組介面中的方法了
· h : 一個InvocationHandler物件,表示的是當我這個動態代理物件在呼叫方法的時候,會關聯到哪一個InvocationHandler物件上
好了,在介紹完這兩個介面(類)以後,我們來透過一個例項來看看我們的動態代理模式是什麼樣的:
首先我們定義了一個Subject型別的介面,為其宣告瞭兩個方法:
1public
interface
Subject
2{
3
public
void
rent();
4
5
public
void
hello(String str);
6}
接著,定義了一個類來實現這個介面,這個類就是我們的真實物件,RealSubject類:
01public
class
RealSubject
implements
Subject
02{
03
@Override
04
public
void
rent()
05
{
06
System.out.println("I want to rent my house");
07
}
08
09
@Override
10
public
void
hello(String str)
11
{
12
System.out.println("hello: "
+ str);
13
}
14}
下一步,我們就要定義一個動態代理類了,前面說個,每一個動態代理類都必須要實現 InvocationHandler 這個介面,因此我們這個動態代理類也不例外:
01public
class
DynamicProxy
implements
InvocationHandler
02{
03
// 這個就是我們要代理的真實物件
04
private
Object subject;
05
06
// 構造方法,給我們要代理的真實物件賦初值
07
public
DynamicProxy(Object subject)
08
{
09
this.subject = subject;
10
}
11
12
@Override
13
public
Object invoke(Object object, Method method, Object[] args)
14
throws
Throwable
15
{
16
// 在代理真實物件前我們可以新增一些自己的操作
17
System.out.println("before rent house");
18
19
System.out.println("Method:"
+ method);
20
21
// 當代理物件呼叫真實物件的方法時,其會自動的跳轉到代理物件關聯的handler物件的invoke方法來進行呼叫
22
method.invoke(subject, args);
23
24
// 在代理真實物件後我們也可以新增一些自己的操作
25
System.out.println("after rent house");
26
27
return
null;
28
}
29
30}
最後,來看看我們的Client類:
01public
class
Client
02{
03
public
static
void
main(String[] args)
04
{
05
// 我們要代理的真實物件
06
Subject realSubject =
new
RealSubject();
07
08
// 我們要代理哪個真實物件,就將該物件傳進去,最後是透過該真實物件來呼叫其方法的
09
InvocationHandler handler =
new
DynamicProxy(realSubject);
10
11
/*
12
* 透過Proxy的newProxyInstance方法來建立我們的代理物件,我們來看看其三個引數
13
* 第一個引數 handler.getClass().getClassLoader() ,我們這裡使用handler這個類的ClassLoader物件來載入我們的代理物件
14
* 第二個引數realSubject.getClass().getInterfaces(),我們這裡為代理物件提供的介面是真實物件所實行的介面,表示我要代理的是該真實物件,這樣我就能呼叫這組介面中的方法了
15
* 第三個引數handler, 我們這裡將這個代理物件關聯到了上方的 InvocationHandler 這個物件上
16
*/
17
Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject
18
.getClass().getInterfaces(), handler);
19
20
System.out.println(subject.getClass().getName());
21
subject.rent();
22
subject.hello("world");
23
}
24}
我們先來看看控制檯的輸出:
01$Proxy0
02
03before rent house
04Method:public
abstract
void
com.xiaoluo.dynamicproxy.Subject.rent()
05I want to rent my house
06after rent house
07
08before rent house
09Method:public
abstractvoidcom.xiaoluo.dynamicproxy.Subject.hello(java.lang.String)
10hello: world
11after rent house
我們首先來看看 $Proxy0 這東西,我們看到,這個東西是由 System.out.println(subject.getClass().getName()); 這條語句列印出來的,那麼為什麼我們返回的這個代理物件的類名是這樣的呢?
1Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject
2
.getClass().getInterfaces(), handler);
可能我以為返回的這個代理物件會是Subject型別的物件,或者是InvocationHandler的物件,結果卻不是,首先我們解釋一下 為什麼我們這裡可以將其轉化為Subject型別的物件? 原因就是在newProxyInstance這個方法的第二個引數上,我們給這個代理物件提供了一組什麼介面,那麼我這個代理物件就會實現了這組介面,這個時候我們當然可以將這個代理物件強制型別轉化為這組介面中的任意一個,因為這裡的介面是Subject型別,所以就可以將其轉化為Subject型別了。
同時我們一定要記住,透過 Proxy.newProxyInstance 建立的代理物件是在jvm執行時動態生成的一個物件,它並不是我們的InvocationHandler型別,也不是我們定義的那組介面的型別,而是在執行是動態生成的一個物件,並且命名方式都是這樣的形式,以$開頭,proxy為中,最後一個數字表示物件的標號 。
接著我們來看看這兩句
subject.rent();
subject.hello(“world”);
這裡是透過代理物件來呼叫實現的那種介面中的方法,這個時候程式就會跳轉到由這個代理物件關聯到的 handler 中的invoke方法去執行,而我們的這個 handler 物件又接受了一個 RealSubject型別的引數,表示我要代理的就是這個真實物件,所以此時就會呼叫 handler 中的invoke方法去執行:
01public
Object invoke(Object object, Method method, Object[] args)
02
throws
Throwable
03
{
04
// 在代理真實物件前我們可以新增一些自己的操作
05
System.out.println("before rent house");
06
07
System.out.println("Method:"
+ method);
08
09
// 當代理物件呼叫真實物件的方法時,其會自動的跳轉到代理物件關聯的handler物件的invoke方法來進行呼叫
10
method.invoke(subject, args);
11
12
// 在代理真實物件後我們也可以新增一些自己的操作
13
System.out.println("after rent house");
14
15
return
null;
16
}
我們看到,在真正透過代理物件來呼叫真實物件的方法的時候,我們可以在該方法前後新增自己的一些操作,同時我們看到我們的這個 method 物件是這樣的:
1public
abstract
void
com.xiaoluo.dynamicproxy.Subject.rent()
2
3public
abstract
void
com.xiaoluo.dynamicproxy.Subject.hello(java.lang.String)
正好就是我們的Subject介面中的兩個方法,這也就證明了當我透過代理物件來呼叫方法的時候,起實際就是委託由其關聯到的 handler 物件的invoke方法中來呼叫,並不是自己來真實呼叫,而是透過代理的方式來呼叫的。
這就是我們的java動態代理機制。
本篇隨筆詳細的講解了java中的動態代理機制,這個知識點非常非常的重要,包括我們Spring的AOP其就是透過動態代理的機制實現的,所以我們必須要好好的理解動態代理的機制。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69913864/viewspace-2703358/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 好程式設計師Java教程Java動態代理機制詳解程式設計師Java
- 好程式設計師Java培訓分享For迴圈詳解程式設計師Java
- 好程式設計師Java培訓分享Java程式設計技巧程式設計師Java
- 好程式設計師Java培訓分享Java程式設計師技能提升指南程式設計師Java
- 好程式設計師Java培訓分享SpringBoot -啟動流程程式設計師JavaSpring Boot
- 好程式設計師Java培訓分享SpringBoot -YAML程式設計師JavaSpring BootYAML
- 好程式設計師Java培訓分享SpringBoot -自動配置原理程式設計師JavaSpring Boot
- 好程式設計師Java培訓分享如何快速入門Java程式設計程式設計師Java
- 好程式設計師Java培訓分享Java之反射技術程式設計師Java反射
- 好程式設計師Java培訓分享如何快速入門Java程式設計師Java
- 好程式設計師Java培訓分享Java包是什麼?程式設計師Java
- 好程式設計師Java培訓分享Java多執行緒程式設計師Java執行緒
- 好程式設計師Java培訓分享maven-概述程式設計師JavaMaven
- 好程式設計師Java培訓分享BigDecimal的用法程式設計師JavaDecimal
- 好程式設計師Java培訓分享Java程式設計師常用的工具類庫程式設計師Java
- 好程式設計師Java培訓分享20個Java程式設計師基礎題程式設計師Java
- 好程式設計師Java培訓分享Java EE與Java的區別程式設計師Java
- 好程式設計師Java培訓分享學Java程式設計要注意什麼程式設計師Java
- 好程式設計師Java培訓分享Java和HTML的區別?程式設計師JavaHTML
- 好程式設計師Java培訓分享Java物件導向概念解析程式設計師Java物件
- 好程式設計師Java培訓分享Java初學者必讀程式設計師Java
- 好程式設計師Java培訓分享Spring Ioc的原理程式設計師JavaSpring
- 好程式設計師Java培訓分享SpringBoot入門篇程式設計師JavaSpring Boot
- 好程式設計師Java培訓分享本地快取如何設計程式設計師Java快取
- 好程式設計師Java培訓分享註解與反射原理說明程式設計師Java反射
- 好程式設計師Java培訓分享Java面試題集合篇一程式設計師Java面試題
- 好程式設計師Java培訓分享Java面試題集合篇二程式設計師Java面試題
- 好程式設計師Java培訓分享學習Java需要哪些基礎程式設計師Java
- 好程式設計師Java培訓分享面試Java的注意事項程式設計師Java面試
- 好程式設計師Java培訓分享Java中級面試題合集程式設計師Java面試題
- 好程式設計師Java培訓分享Java集合的兩種排序方法程式設計師Java排序
- 好程式設計師Java培訓分享Java讀寫Properties配置檔案程式設計師Java
- 好程式設計師Java培訓分享學Java需要什麼基礎?程式設計師Java
- 好程式設計師Java培訓分享Java基礎知識總結程式設計師Java
- 好程式設計師Java培訓分享Java效能常見命令有哪些程式設計師Java
- 好程式設計師Java培訓分享Java多執行緒併發程式設計師Java執行緒
- 好程式設計師Java培訓分享面試Java要注意什麼程式設計師Java面試
- 好程式設計師Java培訓Java程式設計師必學技術程式設計師Java