Spring框架中有一個(gè)核心的概念,叫做AOP(面向切面編程)。而AOP的本質(zhì)其實(shí)就是jdk動(dòng)態(tài)代理。所以學(xué)習(xí)動(dòng)態(tài)代理還是很有必要的。筆者對(duì)動(dòng)態(tài)代理也只是研究了一點(diǎn)皮毛,如有寫(xiě)的不對(duì)的地方歡迎指點(diǎn)。
JDK動(dòng)態(tài)代理有2個(gè)很重要的東西,一個(gè)是InvocationHandler接口,還有一個(gè)是Proxy類。
先來(lái)看一下InvaocationHandler接口的定義:
InvocationHandler is the interface implemented by the invocation handler of a proxy instance.
Each 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.
1.我們寫(xiě)的每一個(gè)動(dòng)態(tài)代理類都需要實(shí)現(xiàn)InvocationHandler接口。
2.每個(gè)代理類的實(shí)例都關(guān)聯(lián)著一個(gè)Handler.當(dāng)調(diào)用代理對(duì)象的方法時(shí),其實(shí)調(diào)用的是InvocaionHandler的invoke()方法。
先來(lái)寫(xiě)一個(gè)類來(lái)實(shí)現(xiàn)InvocationHandler接口:
public class MyDataInvocationHandler implements InvocationHandler {
private Service service;
//通過(guò)構(gòu)造函數(shù)把Service傳進(jìn)來(lái)
public MyDataInvocationHandler(Service service) {
this.service= service;
}
/**
* proxy參數(shù):真實(shí)對(duì)象
* method參數(shù):真實(shí)對(duì)象的方法
* args參數(shù):給真實(shí)對(duì)象的方法傳入一些參數(shù)
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();//在調(diào)用真實(shí)對(duì)象的方法前加一些操作,比如事務(wù)開(kāi)啟等。
method.invoke(service, args);
after();//在調(diào)用真實(shí)對(duì)象的方法后加一些操作,比如事務(wù)關(guān)閉等。
return null;
}
public void before(){
System.out.println("通知類------before()");
}
public void after(){
System.out.println("通知類------after()");
}
}
InvocationHandler先寫(xiě)到這,接下來(lái)看一下Proxy類
Proxy 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類提供靜態(tài)方法用來(lái)動(dòng)態(tài)創(chuàng)建一個(gè)代理對(duì)象的類。
Proxy.newProxyInstance()方法算是最常用的一個(gè)靜態(tài)方法了。
public class Test {
public static void main(String[] args) {
//獲得代理類對(duì)象的實(shí)例
/**
* 第一個(gè)參數(shù):ClassLoader類型。一個(gè)ClassLoader對(duì)象,定義了由哪個(gè)ClassLoader對(duì)象來(lái)對(duì)生成的代理對(duì)象進(jìn)行加載
* 第二個(gè)參數(shù):一個(gè)Interface對(duì)象的數(shù)組,表示的是我將要給我需要代理的對(duì)象提供一組什么接口,如果我提供了一組接口給它,那么這個(gè)代理對(duì)象就宣稱實(shí)現(xiàn)了該接口(多態(tài)),這樣我就能調(diào)用這組接口中的方法了
* 第三個(gè)參數(shù): 一個(gè)InvocationHandler對(duì)象,表示的是當(dāng)我這個(gè)動(dòng)態(tài)代理對(duì)象在調(diào)用方法的時(shí)候,會(huì)關(guān)聯(lián)到哪一個(gè)InvocationHandler對(duì)象上
*/
Service proxyInstance = (Service) Proxy.newProxyInstance(Test.class.getClassLoader(),
new Class<?>[] { Service.class }, new MyDataInvocationHandler(new ServiceImpl()));
proxyInstance.update();
}
}
下面是我的Service接口和Service的實(shí)現(xiàn)類。
public interface Service {
void update();
}
public class ServiceImpl implements Service {
@Override
public void update() {
System.out.println("這是被代理Service的實(shí)現(xiàn)類----update");
}
}
運(yùn)行一下:
好了。效果出來(lái)了。但估計(jì)會(huì)有許多疑問(wèn)吧。為什么他會(huì)自動(dòng)的調(diào)用InvocationHandler的invoke()方法???待會(huì)再解釋。。。
在上面Test測(cè)試類中通過(guò)Proxy.newProxyInstance生成的實(shí)例是屬于哪一個(gè)類的呢?通過(guò)項(xiàng)目工程,沒(méi)有找到該特殊的類。debug看一下。
我們看到,proxyInstance實(shí)例的類名叫做:"$Proxy0",默認(rèn)動(dòng)態(tài)代理類的類名取名方式是:"$"+Proxy關(guān)鍵字+一個(gè)數(shù)字的形式,我們也可以修改這個(gè)名字。
其實(shí)這個(gè)類是運(yùn)行在內(nèi)存中的。我們可以用流的形式將該類的二進(jìn)制寫(xiě)到硬盤(pán)中的.class文件中,再通過(guò)反編譯工具看一下源代碼到底是什么!
在測(cè)試類中加這樣一個(gè)方法
private static void createProxyClass() {
byte[] bs = ProxyGenerator.generateProxyClass("MyProxy",new Class<?>[] { Service.class }) ;
try {
FileOutputStream fileOutputStream = new FileOutputStream("d://a.class");
fileOutputStream.write(bs);
fileOutputStream.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
筆者剛開(kāi)始用到ProxyGenerator的時(shí)候,始終報(bào)錯(cuò),包倒不過(guò)來(lái),這個(gè)百度自己查一下,有解決方案的。。。
ProxyGenerator.generateProxyClass()方法的第一個(gè)參數(shù)就是修改了類名。
通過(guò)反編譯工具終于看到了這個(gè)神秘的java代碼
關(guān)鍵代碼已經(jīng)畫(huà)了框框了。對(duì)于上面提的那個(gè)問(wèn)題,在2中已經(jīng)給了答案了。
聲明:筆者還只是一個(gè)小小的菜鳥(niǎo),目前還在讀大三中,對(duì)于有些問(wèn)題或許理解的還不夠透徹,歡迎指正。