文档章节

模拟jdk实现动态代理模式

small达达
 small达达
发布于 2016/01/27 17:48
字数 1091
阅读 70
收藏 1

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实现动态代理完毕,为帮助理解,下面贴出类图:

© 著作权归作者所有

共有 人打赏支持
small达达
粉丝 6
博文 19
码字总数 7504
作品 0
太原
程序员
设计模式3——Proxy设计模式

Proxy代理设计模式是一种控制对象访问的设计模式,类似于网络代理,网络代理机制如下图: Proxy代理设计模式机制如下: 代理模式UML图如下: 代理模式顺序图如下: 客户端程序通过代理程序来...

小米米儿小
2013/12/06
0
0
代理模式(Proxy Pattern):动态代理 - 最易懂的设计模式解析

前言 今天我来全面总结开发中最常用的设计模式 - 代理模式中的动态代理模式 其他设计模式介绍 1分钟全面了解“设计模式” 单例模式(Singleton) - 最易懂的设计模式解析 简单工厂模式(Sim...

Carson_Ho
04/09
0
0
设计模式: Java中的工厂设计模式

原文链接 https://github.com/shellhub/blog/issues/22 前言 工厂设计模式(Factory Design Pattern)属于创建模式之一,工厂设计模式在JDK,Spring,Stuts被广泛使用 当一个类或者接口有多个子类...

shellhub
08/22
0
0
【设计模式笔记】(十六)- 代理模式

一、简述 代理模式(Proxy Pattern),为其他对象提供一个代理,并由代理对象控制原有对象的引用;也称为委托模式。 其实代理模式无论是在日常开发还是设计模式中,基本随处可见,中介者模式中...

MrTrying
06/24
0
0
小菜学设计模式——工厂方法模式

背景 简单工厂模式最大的优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关类,对于客户端来说,去除了与具体产品的依赖。如果,项目需要扩展,新增一种产品需要简...

learn_more
2015/06/07
0
0

没有更多内容

加载失败,请刷新页面

加载更多

linux使用ntfs-3g操作ntfs格式硬盘

Linux内核目前只支持对微软NTFS文件系统的读取。 NTFS-3G 是微软 NTFS 文件系统的一个开源实现,同时支持读和写。NTFS-3G 开发者使用 FUSE 文件系统来辅助开发,同时对可移植性有益。 安装 ...

linuxprobe16
今天
1
0
kubeadm部署kubernetes集群

一、环境要求 这里使用RHEL7.5 master、etcd:192.168.10.101,主机名:master node1:192.168.10.103,主机名:node1 node2:192.168.10.104,主机名:node2 所有机子能基于主机名通信,编辑...

人在艹木中
今天
7
0
Shell特殊符号总结以及cut,sort,wc,uniq,tee,tr,split命令

特殊符号总结一 * 任意个任意字符 ? 任意一个字符 # 注释字符 \ 脱义字符 | 管道符 # #号后的备注被忽略[root@centos01 ~]# ls a.txt # 备注 a.txt[root@centos01 ~]# a=1[root@centos01...

野雪球
今天
3
0
OSChina 周二乱弹 —— 程序员圣衣

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @达尔文:分享Skeeter Davis的单曲《The End of the World》 《The End of the World》- Skeeter Davis 手机党少年们想听歌,请使劲儿戳(这里...

小小编辑
今天
18
0
[ python import module ] 导入模块

import moudle_name ----> import module_name.py ---> import module_name.py文件路径 -----> sys.path (这里进行查找文件) # from app.web import Personimport app.web.Person as Pe......

_______-
昨天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部