java一步一步实现动态代理

项目开发中,用户的需求经常是变动的。面对用户多变的需求,系统的灵活性是很重要的。

在原来的系统上,想计算方法执行的时间,作为参评系统性能的数据。

方法一:最直观简单做法就是,在每个方法中都添加计算执行时间的逻辑代码。

/**
 * 移动
 */
public interface Moveable {
	public void move();
}

/**
 * 坦克
 */
public class Tank implements Moveable {

	@Override 
	public void move(){
		try {
			System.out.println("tank--moving...");
			Thread.sleep(new Random().nextInt(10000));
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

/**
 * 坦克——计算了方法执行的时间
 */
public class Tank implements Moveable {

	@Override 
	public void move(){
		//方法执行前,计算系统时间
		Long start = System.currentTimeMillis();
		try {
			System.out.println("tank--moving...");
			Thread.sleep(new Random().nextInt(10000));
		} catch (Exception e) {
			e.printStackTrace();
		}
		//方法执行后,计算系统时间
		Long end = System.currentTimeMillis();
		
		System.out.println("time=" + (end-start));
	}
}
这样的做法明显违背了对修改关闭,扩展开放的原则。

方法二:利用继承重写方法

/**
 * Tank2继承Tank
 *
 */
public class Tank2 extends Tank {
	
	/**
	 * 重写move方法,加入计算执行时间代码
	 */
	public void move(){
		Long start = System.currentTimeMillis();
		super.move();
		Long end = System.currentTimeMillis();
		System.out.println("time=" + (start-end));
	} 
}
这样的做法同样有很大的问题,第一,Tank2继承了Tank就不能再继承其它的类。第二,Tank类一旦做了修改,子类必定跟着改。

方法三:另外添加Moveable接口的实现,利用组合。

/**
 * Tank3 实现了Moveable接口
 */
public class Tank3 implements Moveable {

	Moveable m;
	
	public Tank3(Moveable m){
		this.m = m;
	}

	/**
	 * 借助原来的实现,再加工
	 */
	@Override
	public void move() {
		Long start = System.currentTimeMillis();
		m.move();
		Long end = System.currentTimeMillis();
		System.out.println("time=" + (start - end));
	}
}
这其实就是一个代理模式——静态代理,Tank3代理了Tank,在Tank原有的move方法上添加了自己的逻辑处理,大大增加了系统的灵活性,但是假设一个系统500个类500个方法都需要这样做,那就会添加500个类,工作量还是很大的。

方法四:动态代理。动态代理,其实就是不针对具体的类产生代理类,系统执行期间,动态的为某个类产生代理。

动态代理的实现:

1.借助jdk提供的API:被代理的类必须实现接口,JDK1.5以上才支持动态代理。

2.CGLib:不要求有接口,可以为任意类提供代理

这里介绍的是JDK的动态代理,一步一步的分析:

(1):动态的产生代理类:

/**
 * 代理类
 * 动态生成代理对象
 */
public class Proxy{
	
	public static Object newInstance() throws Exception{
		String rt = "\r\n";
		
		//将代理类拼成字符串
		String src = 
			"package com.tgb.proxy;" + rt +

			"public class $Proxy1 implements Moveable{" + rt +
			"	Moveable m;" + rt +
				
			"	public $Proxy1(Moveable m){" + rt + 
			"		this.m = m;" + rt +
			"	}" + rt +
		
			"	public void move(){" + rt +
			"		Long start = System.currentTimeMillis();" + rt +
			"		m.move();" + rt +
			"		Long end = System.currentTimeMillis();" + rt + 
			"		System.out.println(\"time=\" + (start-end));" + rt +
			"	}" + rt +
			"}" ;
		
		//将src写到文件中,生成$Proxy1.java源文件
		String filename = "E:/java/com/tgb/proxy/$Proxy1.java";
		File f = new File(filename);
		FileWriter fw = new FileWriter(f);
		fw.write(src);
		fw.flush();
		fw.close();
		
		//-----利用JDK的编译器,编译文件——就相当于静态编写了代理类,经过了编译----
		//编译器
		JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
		//文件管理器
		StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null,null);
		//通过文件管理器,获得需要编译的文件
		Iterable unites = fileMgr.getJavaFileObjects(filename);
		//编译器获得编译任务
		CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, unites);
		//进行编译
		t.call();
		//关闭文件管理器
		fileMgr.close();
		//-----利用JDK的编译器,编译文件——就相当于静态编写了代理类,经过了编译----
		
		//读取编译好的文件,放入到内存中
		URL[] urls = new URL[] {new URL("file:/" + "E:/java/")};
		URLClassLoader ul = new URLClassLoader(urls);
		Class c = ul.loadClass("com.tgb.proxy.$Proxy1");
		
		//利用带参数的构造器,生成代理对象,并返回
		Constructor ctr = c.getConstructor(Moveable.class);
		Moveable m = (Moveable)ctr.newInstance(new Tank());
		return m;
	}
}
通过代理类(Proxy)的newInstance方法,经过动态编写代理文件,经过编译,再加载的内存,生成真正的代理对象并返回。完成动态代理。

src这个字符串是固定的,所以需要做如下调整:1.动态的代理接口,方法 2.对原来的方法不仅仅是计算执行时间的处理。

(2)调整1:动态的代理接口、方法

/**
 * 代理类
 * 调整1:动态的代理接口、方法
 */
public class Proxy{
	
	/**
	 * 将要被代理的接口,作为参数传入
	 * @param inter
	 * @return
	 * @throws Exception
	 */
	public static Object newInstance(Class inter) throws Exception{
		String rt = "\r\n";
		
		//代理类通过接口,动态生成代理方法
		String methodStr = "";
		Method[] methods = inter.getMethods();
		for(Method m : methods){
			methodStr +="	@Override" + rt +
						"	public void " + m.getName() + "(){" + rt + 
						"		Long start = System.currentTimeMillis();" + rt +
						"		t." + m.getName() + "();" + rt +
						"		Long end = System.currentTimeMillis();" + rt + 
						"		System.out.println(\"time=\" + (start-end));" + rt +	
						"	}";
		}
		
		//代理类的实现接口有传入的参数决定
		String src = 
			"package com.tgb.proxy;" + rt +
	
			"public class $Proxy1 implements " + inter.getName() + "{" + rt +
			"	Moveable t;" + rt +
				
			"	public $Proxy1(Moveable t){" + rt + 
			"		this.t = t;" + rt +
			"	}" + rt +
			 methodStr + rt +
			
			"}" ;
		
		//将代码写入文件,生成源文件
		String filename = "E:/java/com/tgb/proxy/$Proxy1.java";
		File f = new File(filename);
		FileWriter fw = new FileWriter(f);
		fw.write(src);
		fw.flush();
		fw.close();
		
		//利用jdk将源文件进行编译
		JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
		StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
		Iterable units = fileMgr.getJavaFileObjects(filename);
		CompilationTask  ct = compiler.getTask(null, fileMgr, null, null, null, units);
		ct.call();
		fileMgr.close();
		
		//将编译的文件加载到内存中
		URL[] urls = new URL[]{new URL("file:/" + "E:/java/")};
		URLClassLoader ul = new URLClassLoader(urls);
		Class c = ul.loadClass("com.tgb.proxy.$Proxy1");
		
		Constructor ctr = c.getConstructor(Moveable.class);
		Object o = (Object)ctr.newInstance(new Tank());
		 
		return o;
	}
}
这样,基本实现了通过任意的接口,生成对应的代理对象,并返回。

(3):调整2:被代理的方法,不仅仅是做计算时间的处理

/**
 * 方法调用的处理器
 */
public interface InvocationHandler {
	public void invoke(Object o,Method m);
}

/**
 * 对被代理对象,添加 计算方法的执行时间 处理
 */
public class TimeHandler implements InvocationHandler{
	//被代理的对象
	private Object target;
	
	public TimeHandler(Object target) {
		this.target = target;
	}

	/**
	 * 处理被代理对象的方法:添加 计算方法执行时间 处理
	 */
	@Override
	public void invoke(Object o,Method m){
		try {
			Long start = System.currentTimeMillis();
			m.invoke(target, new Object[]{});
			Long end = System.currentTimeMillis();
			System.out.println("time=" + (start-end));
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

/**
 * 代理类
 */
public class Proxy{
	
	/**
	 * @param inter 代理类实现的接口
	 * @param h 代理类方法需要做的处理对象
	 * @return
	 * @throws Exception
	 */
	public static Object newInstance(Class inter,InvocationHandler h) throws Exception{
		
		String rt = "\r\n";
		//通过接口参数,代理类动态生成代理方法
		//代理方法中,通过方法处理参数,动态调用方法的处理类
		String methodStr = "";
		Method[] methods = inter.getMethods();  
		for(Method m : methods){
			methodStr +="	@Override" + rt +
						"	public void " + m.getName() + "(){" + rt +
						"		try {" + rt +
						"			Method md = " + inter.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt +
						"			h.invoke(this, md);" + rt + 
						"		}c atch(Exception e){" + rt +
						"			e.printStackTrace();" + rt + 
						"		}" + rt +
						"	}";
		}
		
		//通过接口参数,代理类动态决定实现接口		
		String src = 
				"package com.tgb.proxy;" + rt +
				"import java.lang.reflect.Method;" + rt + 
				"public class $Proxy1 implements " + inter.getName() + "{" + rt + 
				"	com.tgb.proxy.InvocationHandler h;" + rt + 
				"	public $Proxy1(InvocationHandler h){" + rt +
				"		this.h = h;" + rt +
				"	}" + rt +
				
				methodStr + rt + 
				
				"}";
 		
		String filename = "E:/java/com/tgb/proxy/$Proxy1.java";
		File file = new File(filename);
		FileWriter fw = new FileWriter(file);
		fw.write(src);
		fw.flush();
		fw.close();
		
		//编译
		JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
		StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
		Iterable units = fileMgr.getJavaFileObjects(filename);
		CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
		t.call();
		fileMgr.close();
		
		//加载到内存
		URL[] urls = new URL[]{new URL("file:/" + "E:/java/")};
		URLClassLoader ul = new URLClassLoader(urls);
		Class c = ul.loadClass("com.tgb.proxy.$Proxy1");
		//生成代理对象,并返回
		Constructor ctr = c.getConstructor(InvocationHandler.class);
		Object o = (Object)ctr.newInstance(h);
		return o;
	}
}

客户端:

public class Client {
	public static void main(String[] args) throws Exception{
		Tank t = new Tank();
		InvocationHandler h = new TimeHandler(t);
		Moveable m = (Moveable)Proxy.newInstance(Moveable.class,h);
		m.move();
	}
}

这样,完全实现了动态生成代理对象,并动态添加对方法的处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值