詳解JDK的動態代理+例項理解
阿新 • • 發佈:2018-12-09
引言:當有無數個代理類的話,傳統做法要寫100個代理類,顯然這樣子類會過於膨脹,那麼有沒有一種方法,可以動態產生代理,實現對不同類,不同方法的代理。
動態代理:JDK的動態代理,ca
在代理類和被代理類之間加入了InvocationHander的一個類,也叫事務處理器。
被代理類:
package com.imooc.jdkproxy; import com.imooc.jdkproxy.Moveable; import java.util.Random; public class Car implements Moveable { @Override public void move() { //實現開車 try { Thread.sleep(new Random().nextInt(1000)); System.out.println("汽車行駛中...."); } catch (InterruptedException e) { e.printStackTrace(); } } }
被代理類的方法:
package com.imooc.jdkproxy;
/**
* Created by Administrator on 2018/9/4.
*/
public interface Moveable {
void move();
}
事物處理器:
public class TimeHandler implements InvocationHandler{ private Object target; public TimeHandler(Object target) { this.target = target; } //proxy,被代理物件; //method 被代理物件的方法; //方法的引數 // @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long startTime=System.currentTimeMillis(); System.out.println("汽車開始行駛...."); method.invoke(target);// target為被代理物件 long endTime=System.currentTimeMillis(); System.out.println("汽車結束行駛.... 汽車行駛時間:"+(endTime-startTime)+"毫秒!"); return null;//該示例中car的move()沒有返回值 } }
測試類:
package com.imooc.jdkproxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; /** * Created by Administrator on 2018/9/4. */ public class Test { public static void main(String [] args){ Car car=new Car(); InvocationHandler h=new TimeHandler(car); Class<?> cls =car.getClass(); /** * loader 類載入器 * interfaces 實現介面 * h InvocationHandler */ Moveable m=(Moveable) Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(),h);//生成動態代理類,返回的值都代理類,都實現了movable介面 m.move();//呼叫返回動態代理的move方法 } }
run: 在move方法的基礎上,增加了時間的代理方法。
JDK動態的代理的原理和內部實現:
procy:
package com.imooc.proxy;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;
import org.apache.commons.io.FileUtils;
//定義一個靜態方法,返回一個代理物件
//第一步宣告一段原始碼
public class Proxy {
//(Class infce)把我們需要實現的介面傳遞過來
public static Object newProxyInstance(Class infce) throws Exception{
String rt = "\r\n";
String methodStr = "";//獲取方法
for(Method m : infce.getMethods()){
methodStr += " @Override" + rt +
" public void " + m.getName() + "() {" + rt +
" long starttime = System.currentTimeMillis();" + rt +
" System.out.println(\"汽車開始行駛....\");" + rt +
" m." + m.getName() + "();" + rt +
" long endtime = System.currentTimeMillis();" + rt +
" System.out.println(\"汽車結束行駛.... 汽車行駛時間:\" " + rt +
" + (endtime - starttime) + \"毫秒!\");" + rt +
" }" ;
}
String str =
"package com.imooc.proxy;" + rt +
"public class $Proxy0 implements " + infce.getName() + " {" + rt +
" public $Proxy0(" + infce.getName() + " m) {" + rt +
" super();" + rt +
" this.m = m;" + rt +
" }" + rt +
" private " + infce.getName() + " m;" + rt +
methodStr + rt +
"}" ;
//先產生代理類的java檔案,在對java檔案進行編譯
String filename = System.getProperty("user.dir") +"/bin/com/imooc/proxy/$Proxy0.java";//檔案的路徑取當前應用所在的路徑,然後放在bin目錄下
File file = new File(filename);
FileUtils.writeStringToFile(file, str);//將這段原始碼生成到我們的java檔案當中;
//編譯
//得到當前系統的編譯器
JavaCompiler complier = ToolProvider.getSystemJavaCompiler();
//檔案管理者
StandardJavaFileManager fileMgr =
complier.getStandardFileManager(null, null, null);
//獲取檔案
Iterable units = fileMgr.getJavaFileObjects(filename);
//編譯任務
CompilationTask t = complier.getTask(null, fileMgr, null, null, null, units);
//進行編譯
t.call();
fileMgr.close();
//編譯好的檔案()代理類load 到記憶體
ClassLoader cl = ClassLoader.getSystemClassLoader();
Class c = cl.loadClass("com.imooc.proxy.$Proxy0");//loader檔名
//產生代理物件
Constructor ctr = c.getConstructor(infce);
return ctr.newInstance(new Car());
}
}
測試類:
package com.imooc.proxy;
public class Client {
/**
* 測試類
* @throws Exception
*/
public static void main(String[] args) throws Exception {
Moveable m = (Moveable) Proxy.newProxyInstance(Moveable.class);//產生一個代理物件,並實現Moveable介面
m.move();
}
}
這樣子就好了,但是我們會發現以上程式碼中,業務邏輯是寫死的,也就是我們要怎麼做才能適用於任意類和任意方法呢?
--------即,我們需要在動態代理中新增 InvocationHandler;
package com.imooc.proxy;
import java.lang.reflect.Method;
/**
* Created by Administrator on 2018/9/14.
*/
public interface InvocationHandler{
public void invoke(Object o,Method m);//invoke 對某個物件的方法進行處理。
}
修改我們的proxy類:
package com.imooc.proxy;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;
import org.apache.commons.io.FileUtils;
public class Proxy {
@SuppressWarnings("unchecked")
public static Object newProxyInstance(Class infce,InvocationHandler h) throws Exception{
String rt = "\r\n";
String methodStr = "";
for(Method m : infce.getMethods()){
methodStr += " @Override" + rt +
" public void " + m.getName() + "() {" + rt +
" try{" + rt +
" Method md = " + infce.getName() + ".class.getMethod(\""
+ m.getName() + "\");" + rt +
" h.invoke(this,md);" +rt+
" }catch(Exception e){ e.printStackTrace();}" + rt +
" }" ;
}
String str =
"package com.imooc.proxy;" + rt +
"import java.lang.reflect.Method;" + rt +
"import com.imooc.proxy.InvocationHandler;" + rt+
"public class $Proxy0 implements " + infce.getName() + " {" + rt +
" public $Proxy0(InvocationHandler h) {" + rt +
" this.h = h;" + rt +
" }" + rt +
" private InvocationHandler h;" + rt+
methodStr + rt +
"}" ;
//產生代理類的java檔案
String filename = System.getProperty("user.dir") +"/bin/com/imooc/proxy/$Proxy0.java";
File file = new File(filename);
FileUtils.writeStringToFile(file, str);
//編譯
//拿到編譯器
JavaCompiler complier = ToolProvider.getSystemJavaCompiler();
//檔案管理者
StandardJavaFileManager fileMgr =
complier.getStandardFileManager(null, null, null);
//獲取檔案
Iterable units = fileMgr.getJavaFileObjects(filename);
//編譯任務
CompilationTask t = complier.getTask(null, fileMgr, null, null, null, units);
//進行編譯
t.call();
fileMgr.close();
//load 到記憶體
ClassLoader cl = ClassLoader.getSystemClassLoader();
Class c = cl.loadClass("com.imooc.proxy.$Proxy0");
Constructor ctr = c.getConstructor(InvocationHandler.class);
return ctr.newInstance(h);
}
}
測試類:
package com.imooc.proxy;
public class Client {
/**
* 測試類
* @throws Exception
*/
public static void main(String[] args) throws Exception {
Car car = new Car();
InvocationHandler h = new TimeHandler(car);
Moveable m = (Moveable)Proxy.newProxyInstance(Moveable.class,h);
m.move();
}
}