模拟jdk实现动态代理模式
模拟jdk实现动态代理模式
small达达 发表于2年前
模拟jdk实现动态代理模式
  • 发表于 2年前
  • 阅读 66
  • 收藏 1
  • 点赞 2
  • 评论 0

新睿云服务器60天免费使用,快来体验!>>>   

摘要: 模拟reflection包下的InvocationHandler以及Proxy类的实现,深入理解动态代理模式

1、先简单使用InvocationHandler,Proxy来实现动态代理,目的:为Tank添加一个代理TimeProxy,由TimeProxy为Tank添加额外的计时功能(在不改变Tank原有特性前提下,使用动态代理再合适不过)

首先来看下需要被代理的对象Tank相关信息:

public interface Moveable {
	public void move();
}
public class Tank implements Moveable{

	@Override
	public void move() {
		System.out.println("坦克移动中...");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

}

用TimerHandler实现InvocationHandler接口,该类维护一个被代理对象target,实现InvocationHandler接口的invoke方法,通过反射调用target的指定方法:



public class TimerHandler implements InvocationHandler{
	private Object target;
	public TimerHandler(Object object){
		this.target = object;
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		long start = System.currentTimeMillis();
		System.out.println("start:"+start);
		//通过反射调用 被代理对象tank的指定方法
		method.invoke(target, args);
		long end = System.currentTimeMillis();
		System.out.println("end:"+end);
		System.out.println("total:"+(end-start));
		return null;
	}

}

测试代码:

public class ProxyTest {
	public static void main(String[] args) throws Exception {
		Moveable tank = new Tank();
		//将被代理对象注入Invocationhandler
		TimerHandler handler = new TimerHandler(tank);
		//通过Proxy.newProxyInstance方法得到TankProxy
		Moveable tankProxy = (Moveable)java.lang.reflect.Proxy.newProxyInstance(tank.getClass().getClassLoader(), tank.getClass().getInterfaces(), handler);
		tankProxy.move();
	}
}
输出结果:

start:1453883481492
坦克移动中...
end:1453883482493
total:1001

到此为止,利用jdk实现动态代理,原理是:将被代理对象Tank注入到InvocationHandler,通过自定义handler的invoke为Tank增加新特性,最后通过Proxy的newProxyInstance方法生成一个tankProxy对象(也就是tank的代理对象),通过tankProxy可以随意调用tank的方法(该方法必须是继承了共同的父类所具有的方法);也就是说jdk动态代理需要被代理对象tank和代理对象tankProxy实现共同的接口,这里是Moveable,所以tankProxy才可以调用move方法。

贴出类图:


图中Tank$Proxy并不是自己定义实现的,而是由Proxy类通过调用newProxyInstance方法生成的,生成的Tank$Proxy对象中维护一个自定义的InvocationHandler子类(TimeHandler),以达到动态代理的目的,下面我们模拟实现jdk中的InvocationHandler和Proxy类,更深入的理解动态代理

2、模拟jdk 动态代理的实现

先贴出Invocation接口,核心即invoke方法,通过invoke方法可以获得被代理对象o,和被代理方法m

public interface InvocationHandler {
	public void invoke(Object o,Method m);
}
紧接着是Proxy的实现
public class Proxy {
	public static Object newProxyInstance(Class interfaces, InvocationHandler h) throws Exception{
		StringBuffer methodStr = new StringBuffer();
		String tr = "\r\n";
		//获取接口的所有方法
		Method[] methods = interfaces.getMethods();
		for(Method method:methods){
			methodStr.append(
					"	public "+method.getReturnType()+" "+method.getName()+"(){"+tr+
					"		try{"+tr+
					"				java.lang.reflect.Method md = "+interfaces.getName() +"."+"class.getMethod(\""+method.getName()+"\");"+tr+
					"				m.invoke(this,md);"+tr+
					"			}catch(Exception e){ e.printStackTrace();}"+tr+
					"}"+tr
					);
		}
		//拼接代理类
		String src = "package com.designPattern.dynamicProxy;"+tr+
				"import com.designPattern.dynamicProxy.Moveable;"+tr+
				"public class TimeProxy implements "+interfaces.getName()+" {"+tr+
				"	private com.designPattern.dynamicProxy.InvocationHandler m;"+tr+
				"	public TimeProxy(com.designPattern.dynamicProxy.InvocationHandler m){"+tr+
				"		this.m = m;"+tr+
				"		}"+tr+methodStr.toString()+
				"}"+tr;
		//创建代理类
		String filename = System.getProperty("user.dir")+"/target/classes/com/designPattern/dynamicProxy/TimeProxy.java";
		File file = new File(filename);
		FileWriter writer = new FileWriter(file);
		writer.write(src);
		writer.flush();
		writer.close();
		
		//编译
		JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
		StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
		Iterable units = fileMgr.getJavaFileObjects(filename);
		CompilationTask cTask = compiler.getTask(null, fileMgr, null, null, null, units);
		cTask.call();
		fileMgr.close();
		
		//加载到内存
		Class c = ClassLoader.getSystemClassLoader().loadClass("com.designPattern.dynamicProxy.TimeProxy");
		Constructor constructor = c.getConstructor(InvocationHandler.class);
		Object m = constructor.newInstance(h);
		return m;
	}

}
Proxy的实现其实很简单:1)构造一个代理类的java文件(该代理类必然维护InvocationHandler实例,并且与代理类实现相同的接口,并实现所有的接口方法,每个方法的实现中都是调用自定义handler的invoke方法,handler为被代理对象提供额外的特性)。2)将该java文件编译为class文件。3)将编译好的class文件加载到内存。4)反射调用构造方法,并将InvocationHandler注入,得到代理对象

生成的java类代码如下:

package com.designPattern.dynamicProxy;
import com.designPattern.dynamicProxy.Moveable;
public class TimeProxy implements com.designPattern.dynamicProxy.Moveable {
	private com.designPattern.dynamicProxy.InvocationHandler m;
	public TimeProxy(com.designPattern.dynamicProxy.InvocationHandler m){
		this.m = m;
		}
	public void move(){
		try{
				java.lang.reflect.Method md = com.designPattern.dynamicProxy.Moveable.class.getMethod("move");
				m.invoke(this,md);
			}catch(Exception e){ e.printStackTrace();}
}
}



这里换一种写法,采用匿名内部类方式构造InvocationHandler子类:

public class TankProxy {
	@SuppressWarnings("unchecked")
	public static <T> T getBean(final Object tank) throws Exception{
		return (T)Proxy.newProxyInstance(tank.getClass().getInterfaces()[0], new InvocationHandler() {
			
			@Override
			public void invoke(Object o, Method m) {
				long start = System.currentTimeMillis();
				System.out.println("start:"+start);
				try {
				m.invoke(tank, new Object[]{});
				} catch (Exception e) {
				                     e.printStackTrace();
				}
				 long end = System.currentTimeMillis();
				System.out.println("end:"+end);
				System.out.println("time:"+(end-start));
			}
		});
	}
}
测试:

public class ProxyTest {
	public static void main(String[] args) throws Exception {
		Tank tank = new Tank();
		Moveable moveable = TankProxy.getBean(tank);
		moveable.move();
	}
}
输出结果:

start:1453887969913
坦克移动中...
end:1453887970913
time:1000

至此,模拟jdk实现动态代理完毕,为帮助理解,下面贴出类图:

标签: 动态代理 jdk uml
  • 打赏
  • 点赞
  • 收藏
  • 分享
共有 人打赏支持
粉丝 6
博文 19
码字总数 7504
×
small达达
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: