原始碼專題之spring設計模式
阿新 • • 發佈:2018-12-18
jdk動態代理
jdk動態代理
程式碼實現 滿足代理模式應用場景的三個必要條件
- 兩個角色:執行者、被代理物件
- 注重過程,必須要做,被代理物件沒時間做或者不想做(怕羞羞),不專業
- 執行者必須拿到被代理物件的個人資料(執行者持有被代理物件的引用)
jdk的動態代理通過呼叫Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法,生成目標物件的代理類,interfaces引數為目標物件所實現的全部介面,InvocationHandler的實現類負責在呼叫方法前後處理自定義邏輯,下面我們以媒婆介紹物件為背景實現: 1 首先是需要找物件的人
public interface Person { void findLove(); String getSex(); String getName(); } --- public class XiaoFang implements Person{ private String sex = "女"; private String name = "小芳"; @Override public void findLove() { System.out.println("我叫" + this.name + ",性別:" + this.sex + "我找物件的要求是:"); System.out.println("高富帥"); System.out.println("有房有車的"); System.out.println("身高要求180cm以上,體重70kg"); } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
2 小芳不好意思找物件,就要找媒婆來搭橋
public class Meipo implements InvocationHandler { private Person target; //需要代理的目標物件 //獲取被代理人的個人資料 //將目標物件傳入進行代理 public Object getInstance(Person target) throws Exception{ this.target = target; Class clazz = target.getClass(); System.out.println("被代理物件的class是:"+clazz); return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);//返回代理物件 } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("開始進行海選..."); System.out.println("------------"); //代理找物件 Object ret = method.invoke(this.target, args); System.out.println("------------"); System.out.println("如果合適的話,就準備結婚); return ret; } }
3 正式開始委託媒婆找物件
public class TestFindLove {
public static void main(String[] args) {
try {
Person obj = (Person)new Meipo().getInstance(new XiaoFang());
System.out.println(obj.getClass());
obj.findLove();
} catch (Exception e) {
e.printStackTrace();
}
}
}
手寫實現jdk動態代理
這裡我們不用jdk提供的reflect api,自己手寫實現jdk動態代理,來進一步看一下動態代理的內部實現。 1 需要找物件的人,參照上面的person介面及實現
2 媒婆需要獲取被代理人的個人資訊,並生成一個替代品(代理物件)
public class MyPorxy {
private static String ln = "\r\n";
public static Object newProxyInstance(MyClassLoader classLoader, Class<?>[] interfaces, MyInvocationHandler h){
try{
//1、生成原始碼
String proxySrc = generateSrc(interfaces[0]);
//2、將生成的原始碼輸出到磁碟,儲存為.java檔案
String filePath = MyPorxy.class.getResource("").getPath();
File f = new File(filePath + "$Proxy0.java");
FileWriter fw = new FileWriter(f);
fw.write(proxySrc);
fw.flush();
fw.close();
//3、編譯原始碼,並且生成.class檔案
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
Iterable iterable = manager.getJavaFileObjects(f);
CompilationTask task = compiler.getTask(null, manager, null, null, null, iterable);
task.call();
manager.close();
//4.將class檔案中的內容,動態載入到JVM中來
//5.返回被代理後的代理物件
Class proxyClass = classLoader.findClass("$Proxy0");
Constructor c = proxyClass.getConstructor(MyInvocationHandler.class);
f.delete();
return c.newInstance(h);
}catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static String generateSrc(Class<?> interfaces){
StringBuffer src = new StringBuffer();
src.append("package com.lh.reflect;" + ln);
src.append("import java.lang.reflect.Method;" + ln);
src.append("public class $Proxy0 implements " + interfaces.getName() + "{" + ln);
src.append("MyInvocationHandler h;" + ln);
src.append("public $Proxy0(MyInvocationHandler h) {" + ln);
src.append("this.h = h;" + ln);
src.append("}" + ln);
for (Method m : interfaces.getMethods()) {
src.append("public " + m.getReturnType().getName() + " " + m.getName() + "(){" + ln);
src.append("try{" + ln);
src.append("Method m = " + interfaces.getName() + ".class.getMethod(\"" +m.getName()+"\",new Class[]{});" + ln);
src.append("this.h.invoke(this,m,null);" + ln);
src.append("}catch(Throwable e){e.printStackTrace();}" + ln);
src.append("}" + ln);
}
src.append("}");
return src.toString();
}
}
3 MyClassLoader程式碼:
//程式碼生成、編譯、重新動態load到JVM
public class MyClassLoader extends ClassLoader{
private File baseDir;
public MyClassLoader(){
String basePath = MyClassLoader.class.getResource("").getPath();
this.baseDir = new java.io.File(basePath);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String className = MyClassLoader.class.getPackage().getName() + "." + name;
if(baseDir != null){
File classFile = new File(baseDir,name.replaceAll("\\.", "/") + ".class");
if(classFile.exists()){
FileInputStream in = null;
ByteArrayOutputStream out = null;
try{
in = new FileInputStream(classFile);
out = new ByteArrayOutputStream();
byte [] buff = new byte[1024];
int len;
while ((len = in.read(buff)) != -1) {
out.write(buff, 0, len);
}
return defineClass(className, out.toByteArray(), 0,out.size());
}catch (Exception e) {
e.printStackTrace();
}finally{
if(null != in){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(null != out){
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
classFile.delete();
}
}
}
return null;
}
}
4 接下來媒婆介紹物件的環節就水到渠成了
public interface MyInvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
---
public class MyMeipo implements MyInvocationHandler {
private Person target;
//獲取被代理人的個人資料
public Object getInstance(Person target) throws Exception{
this.target = target;
Class clazz = target.getClass();
System.out.println("被代理物件的class是:"+clazz);
return MyPorxy.newProxyInstance(new MyClassLoader(), clazz.getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("開始進行海選...");
System.out.println("------------");
Object ret = method.invoke(this.target, args);
System.out.println("------------");
System.out.println("如果合適的話,就準備結婚");
return ret;
}
}
總結 jdk動態代理原理:
- 拿到被代理物件的引用,然後獲取它的介面
- JDK代理重新生成一個類,同時實現我們給的代理物件所實現的介面
- 把被代理物件的引用也拿到了
- 重新動態生成一個class位元組碼
- 然後編譯