如上文:代理模式和裝飾者模式中的靜態(tài)代理實(shí)例,它具有如下缺陷:
1:代理類不可重用,具有相同代理邏輯的類會(huì)大量產(chǎn)生;
2:被代理方法惟一,如果有多個(gè)方法都需要相同邏輯的代理,那么代理類中就有大量的相似的方法存在;
3:代理方法不具有參數(shù);
4:只實(shí)現(xiàn)了單接口了代理;
所以它并不具有實(shí)戰(zhàn)意義上的價(jià)值!
那么,這些問題該如何解決呢?動(dòng)態(tài)代理又是一步一步如何演變過來的呢?
下面就讓我們來一步一步以實(shí)例的方式來探究它的演變的細(xì)節(jié)。
如果我們能動(dòng)態(tài)產(chǎn)生一個(gè)代理類的源文件,編譯后加載到內(nèi)存,那么我們就可以獲取到動(dòng)態(tài)的代理對(duì)象。
package proxy;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.net.URLClassLoader;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;
public class Proxy {
public static Object newProxyInstance(Object target) throws Exception { //JDK6 Complier API, CGLib, ASM
String rt = "\r\n";
String t = "\t";
String src ="package proxy;"+ rt + t +
"public class TankTimeProxy implements Movable {"+ rt + t +
"private Movable obj; "+ rt + t +
"public TankTimeProxy(Movable obj) {"+ rt + t +
"super();"+ rt + t +
"this.obj = obj;"+ rt + t +
"}"+ rt + t +
"@Override"+ rt + t +
"public void move() {"+ rt + t +
"long begintime = System.currentTimeMillis();"+ rt + t +
"System.out.println(\" Tank is begining to move !\");"+ rt + t +
"obj.move();"+ rt + t +
"long endtime = System.currentTimeMillis();"+ rt + t +
"System.out.println(\" Tank is stop !\");"+ rt + t +
"System.out.println(\"move time : \"+(endtime-begintime));"+ rt + t +
"}"+ rt + t +
"}";
String fileName =System.getProperty("user.dir")+"/src/proxy/TankTimeProxy.java";
File f = new File(fileName);
FileWriter fw = new FileWriter(f);
if(f.exists()){
f.delete();
fw.flush();
f = new File(fileName);
};
fw.write(src);
fw.flush();
fw.close();
//compile
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
Iterable units = fileMgr.getJavaFileObjects(fileName);
CompilationTask compilationTask = compiler.getTask(null, fileMgr, null, null, null, units);
compilationTask.call();
fileMgr.close();
//load into memory and create an instance
URL[] urls = new URL[] {new URL("file:/" + System.getProperty("user.dir")+ "/src/")};
URLClassLoader ul = new URLClassLoader(urls);
Class c = ul.loadClass("proxy.TankTimeProxy");
Constructor ctr = c.getConstructor(target.getClass().getInterfaces()[0]);
Object m = ctr.newInstance(target);
return m;
}
}
測(cè)試類:
package proxy;
public class Client {
public static void main(String[] args) throws Exception{
Movable tank = (Movable)Proxy.newProxyInstance(new Tank());
tank.move();
}
}
上面的類就實(shí)現(xiàn)了生成
TankTimeProxy.java文件,編譯,加載并被調(diào)用的功能。(全部源碼見:代理模式和裝飾者模式異同點(diǎn)比較
)
那么怎么實(shí)現(xiàn)代理任意對(duì)象呢?
在產(chǎn)生代理類的時(shí)候,只要?jiǎng)討B(tài)的注入目標(biāo)對(duì)象,就實(shí)現(xiàn)了對(duì)任意對(duì)象的代理。
怎么實(shí)現(xiàn)對(duì)任意方法的代理呢?通過java反射機(jī)制,可以獲取一個(gè)類的所有方法,即可以獲取目標(biāo)類的所有方法,在組成代理類java源碼的時(shí)候,循環(huán)遍歷嵌入處理邏輯就可以任意對(duì)多方法的代理了。
示例代碼如下:
package proxy;

import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;


public class Proxy
{

public static Object newProxyInstance(Class intface,Object target) throws Exception
{ //JDK6 Complier API, CGLib, ASM

String rt = "\r\n";
String t = "\t";
String methodStr = "" ;
Method[] methods = intface.getDeclaredMethods();

for (Method method : methods)
{
methodStr += "@Override"+ rt + t +
"public void "+method.getName()+"() {"+ rt + t +
"long begintime = System.currentTimeMillis();"+ rt + t +
"System.out.println(\" Tank is begining to move !\");"+ rt + t +
"obj."+method.getName()+"();"+ rt + t +
"long endtime = System.currentTimeMillis();"+ rt + t +
"System.out.println(\" Tank is stop !\");"+ rt + t +
"System.out.println(\"move time : \"+(endtime-begintime));"+ rt + t +
"}"+ rt + t;
}
System.out.println(methodStr);
String src ="package proxy;"+ rt + t +
"import "+intface.getName()+";"+ rt + t +
"public class $Proxy1 implements "+intface.getSimpleName()+" {"+ rt + t +
"private "+intface.getSimpleName()+" obj; "+ rt + t +
"public $Proxy1("+intface.getSimpleName()+" obj) {"+ rt + t +
"super();"+ rt + t +
"this.obj = obj;"+ rt + t +
"}" + rt + t +
methodStr +rt +
"}";
String fileName =System.getProperty("user.dir")+"/src/proxy/$Proxy1.java";
File f = new File(fileName);
FileWriter fw = new FileWriter(f);

if(f.exists())
{
f.delete();
fw.flush();
f = new File(fileName);
};
fw.write(src);
fw.flush();
fw.close();
//compile
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
Iterable units = fileMgr.getJavaFileObjects(fileName);
CompilationTask compilationTask = compiler.getTask(null, fileMgr, null, null, null, units);
compilationTask.call();
fileMgr.close();
//load into memory and create an instance

URL[] urls = new URL[]
{new URL("file:/" + System.getProperty("user.dir")+ "/src/")};
URLClassLoader ul = new URLClassLoader(urls);
Class c = ul.loadClass("proxy.$Proxy1");
Constructor ctr = c.getConstructor(intface);
Object m = ctr.newInstance(target);
return m;
}
}

但是新的問題又出現(xiàn)了:
在代理類中,被代理方法前后的處理邏輯已經(jīng)被“寫死了”,很難改變?cè)黾拥墓δ埽@又該如何處理呢?
我們可以這樣考慮,增加一個(gè)調(diào)用處理器InvocationHandler,把對(duì)方法的處理邏輯進(jìn)行進(jìn)一步的封閉,并把InvocationHandler分離出來,如果可以的話,就實(shí)現(xiàn)了對(duì)代理邏輯的可修改性。
那么InvocationHandler里面應(yīng)該封閉些什么東西呢?

for (Method method : methods)
{
methodStr += "@Override"+ rt + t +
"public void "+method.getName()+"() {"+ rt + t +
"long begintime = System.currentTimeMillis();"+ rt + t +
"System.out.println(\" Tank is begining to move !\");"+ rt + t +
"obj."+method.getName()+"();"+ rt + t +
"long endtime = System.currentTimeMillis();"+ rt + t +
"System.out.println(\" Tank is stop !\");"+ rt + t +
"System.out.println(\"move time : \"+(endtime-begintime));"+ rt + t +
"}"+ rt + t;
}
從這個(gè)片段代理可以看出,我們應(yīng)該在InvocationHandler中封裝obj對(duì)象,即被代理類的接口(實(shí)現(xiàn)類)。還應(yīng)該實(shí)現(xiàn):被分離出去的InvocationHandler能被代理類調(diào)用,我們應(yīng)該把InvocationHandler聚合進(jìn)來。
代碼演變示例:
package proxy;

import java.lang.reflect.Method;


public interface InvocationHandler
{
public void invoke(Object proxy, Method m);
}

package proxy;

import java.lang.reflect.Method;


public class TimeHandler implements InvocationHandler
{
private Object target;


public TimeHandler(Object target)
{
this.target = target;
}

@Override

public void invoke(Object o, Method m)
{
long start = System.currentTimeMillis();
System.out.println("starttime:" + start);
System.out.println(o.getClass().getName());

try
{
m.invoke(target);

} catch (Exception e)
{
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("time:" + (end-start));
System.out.println("endtime:" + end);
}

}

package proxy;

import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;

import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;


public class Proxy
{

public static Object newProxyInstance(Class intface,InvocationHandler h) throws Exception
{ //JDK6 Complier API, CGLib, ASM

String rt = "\r\n";
String t = "\t";
String methodStr = "" ;
Method[] methods = intface.getDeclaredMethods();

for(Method method : methods)
{
methodStr += "@Override" + rt + t + t +
"public void " + method.getName() + "() {" + rt + t + t + t +
"try {" + rt + t + t + t + t +
"Method md = " + intface.getName() + ".class.getMethod(\"" + method.getName() + "\");" + rt + t + t + t + t +
"h.invoke(this, md);" + rt + t + t + t +
"}catch(Exception e) {" + rt + t + t + t +
"e.printStackTrace();" + rt + t + t +
"}" + rt + t +
"}" + rt + t + t ;
}
System.out.println(methodStr);
String src ="package proxy;"+ rt + t +
"import "+intface.getName()+";"+ rt + t +
"import java.lang.reflect.Method;"+ rt + t +
"public class $Proxy1 implements "+intface.getSimpleName()+" {"+ rt + t +
"private InvocationHandler h ; "+ rt + t +
"public $Proxy1(InvocationHandler h) {"+ rt + t +
"super();"+ rt + t +
"this.h = h;"+ rt + t +
"}" + rt + t +
methodStr +rt +
"}";
String fileName =System.getProperty("user.dir")+"/src/proxy/$Proxy1.java";
File f = new File(fileName);
FileWriter fw = new FileWriter(f);

if(f.exists())
{
f.delete();
fw.flush();
f = new File(fileName);
};
fw.write(src);
fw.flush();
fw.close();
//compile
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
Iterable units = fileMgr.getJavaFileObjects(fileName);
CompilationTask compilationTask = compiler.getTask(null, fileMgr, null, null, null, units);
compilationTask.call();
fileMgr.close();
//load into memory and create an instance

URL[] urls = new URL[]
{new URL("file:/" + System.getProperty("user.dir")+ "/src/")};
URLClassLoader ul = new URLClassLoader(urls);
Class c = ul.loadClass("proxy.$Proxy1");
Constructor ctr = c.getConstructor(InvocationHandler.class);
Object m = ctr.newInstance(h);
return m;
}
}

這樣,就實(shí)現(xiàn)了對(duì)任意對(duì)象,任意方法的代理。
缺點(diǎn)就是:多接口代理沒有實(shí)現(xiàn),被代理對(duì)象的方法沒有支持參數(shù)。