文档章节

模拟jdk实现动态代理模式

small达达
 small达达
发布于 2016/01/27 17:48
字数 1091
阅读 69
收藏 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
【设计模式笔记】(十六)- 代理模式

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

MrTrying
06/24
0
0
(六)观察者模式详解(包含观察者模式JDK的漏洞以及事件驱动模型)

本章我们讨论一个除前面的单例以及代理模式之外,一个WEB项目中有可能用到的设计模式,即观察者模式。 说起观察者模式,LZ还是非常激动的,当初这算是第一个让LZ感受到设计模式强大的家伙。当...

Sheamus
2015/01/29
0
0
系统架构技能之设计模式-单件模式

一、开篇 其实我本来不是打算把系统架构中的一些设计模式单独抽出来讲解的,因为很多的好朋友也比较关注这方面的内容,所以我想通过我理解及平时项目中应用到的一 些常见的设计模式,拿出来给...

wbf961127
2017/11/12
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

[MicroPython]STM32F407开发板驱动OLED液晶屏

1.实验目的 1.学习在PC机系统中扩展简单I/O 接口的方法。 2.进一步学习编制数据输出程序的设计方法。 3.学习 F407 Micropython开发板控制OLED显示字符。 2.所需元器件 F407 Micropython开发板...

bodasisiter
29分钟前
0
0
php require和include 相对路径一个有趣的坑

以前总是被教育,不要使用相对路径,这样性能比较差,但是相对路径的问题不仅仅是性能哦,看下面这里例子 这是项目结构 .├── main.php├── t│ ├── t1.php│ └── t2.php└─...

anoty
30分钟前
15
0
x64技术之SSDT_Hook

测试环境: 虚拟机: Windows 7 64bit 过PG工具 驱动加载工具 PCHunter64 系统自带的计算器和任务管理器等 实现思路: 实际思路与win32的思路一样.都是替换SSDT表里边的函数地址.不过微软被搞怕...

simpower
31分钟前
0
0
TreeMap源码分析,看了都说好

一、简介 TreeMap最早出现在JDK 1.2中,是 Java 集合框架中比较重要一个的实现。TreeMap 底层基于红黑树实现,可保证在log(n)时间复杂度内完成 containsKey、get、put 和 remove 操作,效率很...

Java小铺
41分钟前
0
0
协变、逆变

概念 假设 A、B表示类型 ≤ 表示继承关系 f<⋅>表示类型转换 若A ≤ B,则 A是B的子类,B是A的超类 协变、逆变 什么是型变?型变(type variance)允许对类型进行子类型转换。 为了下面讲解先...

obaniu
47分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部