【设计模式】动态代理Proxy_03

原创 2015年07月10日 11:15:41
我们继续上一篇总结。

我们把TankTimeProxy的类Load进内存之后我们要生成它的一个对象。我们先来回顾一下我们之前写好的Proxy类:
package cn.edu.hpu.ProxyTest;

import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.net.URLClassLoader;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;

import cn.edu.hpu.proxy.Moveable;

public class Test1 {
	public static void main(String[] args) throws Exception{
		String rt="\r\n";
		
		String src=
			"package cn.edu.hpu.proxy;"+ rt +


		"public class TankTimeProxy implements Moveable{"+ rt +
		"    Moveable t;"+ rt +
		"    long start;"+ rt +
		"    long end;"+ rt +


		"    public TankTimeProxy(Moveable t) {"+ rt +
			"        super();"+ rt +
			"        this.t = t;"+ rt +
		"    }"+ rt +


		"    public void before(){"+ rt +
			"        start=System.currentTimeMillis();"+ rt +
			"        System.out.println(\"开始时间:\"+start+\"ms\");"+ rt +
		"    }"+
			
		"    public void after(){"+ rt +
			"        end=System.currentTimeMillis();"+ rt +
			"        System.out.println(\"运行时间:\"+(end-start)+\"ms\");"+ rt +
		"    }"+ rt +
			
		"    @Override"+ rt +
		"	 public void move() {"+ rt +
			"        this.before();"+ rt +
			"        t.move();"+ rt +
			"        this.after();"+ rt +
		"    }"+ rt +
			
		"}";
		
		//拿到当前项目的根目录:System.getProperty("user.dir"));
		String fileName=System.getProperty("user.dir")
						+"/src/cn/edu/hpu/proxy/TankTimeProxy.java";
		
		//我们把src的源码写入自己创建的File文件中去
		File f=new File(fileName);
		FileWriter fw=new FileWriter(f);
		fw.write(src);
		fw.flush();
		fw.close();
		
		//编译(getSystemJavaCompiler()拿到系统默认的Java编译器,其实就是javac)
		JavaCompiler compiler=ToolProvider.getSystemJavaCompiler();
		//需要一个FileManager,用它来管理文件(参数1:诊断的监听器,参数2、3国际化)
		StandardJavaFileManager fileMgr=
			compiler.getStandardFileManager(null, null, null);
		//通过FileManager找到TankTimeProxy文件,然后放到一个Iterable中
		//Iterable是一个数组,用它可以进行迭代
		Iterable units=fileMgr.getJavaFileObjects(fileName);
		//参数:(输出位置,文件管理器对象,监听器,编译的时候指定的参数,用到那些class文件,需要编译哪些文件)
		CompilationTask t=compiler.getTask(null, fileMgr, null, null, null, units);
		//进行编译
		t.call();
		fileMgr.close();
		
		//把编译好的.class文件加载到内存中
		//urls指定.class文件所放的地方(使用URL还可以Load从网上传过来的类)
		URL[] urls=new URL[]{new URL("file:/"+System.getProperty("user.dir")+"/src")};
		//ClassLoader指的是吧硬盘里的Java文件放到内存中的那些个类
		URLClassLoader ul=new URLClassLoader(urls);
		Class c=ul.loadClass("cn.edu.hpu.proxy.TankTimeProxy");
		System.out.println(c);
		
	}
}


我们已经拿到TankTimeProxy类的class类,下面我们来得到它的对象:
//得到TankTimeProxy类的构造方法,构造方法的参数为Moveable类型
Constructor ctr=c.getConstructor(Moveable.class);
Moveable m=(Moveable)ctr.newInstance(new Tank());
m.move();

运行结果:
class cn.edu.hpu.proxy.TankTimeProxy
开始时间:1436429931050ms
坦克正在移动中...
运行时间:6313ms

我们把Test1的所有代码转移到Proxy类中。我们的整个的Proxy类的结构如下:
package cn.edu.hpu.proxy;

import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.net.URLClassLoader;


import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;


public class Proxy {
	
	//这个方法用来产生新的代理类
	//参数:告诉程序使用实现哪个接口的动态代理
	public static Object newProxyInstance() throws Exception{
		String rt="\r\n";
		
		String src=
			"package cn.edu.hpu.proxy;"+ rt +


		"public class TankTimeProxy implements Moveable{"+ rt +
		"    Moveable t;"+ rt +
		"    long start;"+ rt +
		"    long end;"+ rt +


		"    public TankTimeProxy(Moveable t) {"+ rt +
			"        super();"+ rt +
			"        this.t = t;"+ rt +
		"    }"+ rt +


		"    public void before(){"+ rt +
			"        start=System.currentTimeMillis();"+ rt +
			"        System.out.println(\"开始时间:\"+start+\"ms\");"+ rt +
		"    }"+
			
		"    public void after(){"+ rt +
			"        end=System.currentTimeMillis();"+ rt +
			"        System.out.println(\"运行时间:\"+(end-start)+\"ms\");"+ rt +
		"    }"+ rt +
			
		"    @Override"+ rt +
		"	 public void move() {"+ rt +
			"        this.before();"+ rt +
			"        t.move();"+ rt +
			"        this.after();"+ rt +
		"    }"+ rt +
			
		"}";
		
		//拿到当前项目的根目录:System.getProperty("user.dir"));
		String fileName=System.getProperty("user.dir")
						+"/src/cn/edu/hpu/proxy/TankTimeProxy.java";
		
		//我们把src的源码写入自己创建的File文件中去
		File f=new File(fileName);
		FileWriter fw=new FileWriter(f);
		fw.write(src);
		fw.flush();
		fw.close();
		
		//编译(getSystemJavaCompiler()拿到系统默认的Java编译器,其实就是javac)
		JavaCompiler compiler=ToolProvider.getSystemJavaCompiler();
		//需要一个FileManager,用它来管理文件(参数1:诊断的监听器,参数2、3国际化)
		StandardJavaFileManager fileMgr=
			compiler.getStandardFileManager(null, null, null);
		//通过FileManager找到TankTimeProxy文件,然后放到一个Iterable中
		//Iterable是一个数组,用它可以进行迭代
		Iterable units=fileMgr.getJavaFileObjects(fileName);
		//参数:(输出位置,文件管理器对象,监听器,编译的时候指定的参数,用到那些class文件,需要编译哪些文件)
		CompilationTask t=compiler.getTask(null, fileMgr, null, null, null, units);
		//进行编译
		t.call();
		fileMgr.close();
		
		//把编译好的.class文件加载到内存中
		//urls指定.class文件所放的地方(使用URL还可以Load从网上传过来的类)
		URL[] urls=new URL[]{new URL("file:/"+System.getProperty("user.dir")+"/src")};
		//ClassLoader指的是吧硬盘里的Java文件放到内存中的那些个类
		URLClassLoader ul=new URLClassLoader(urls);
		Class c=ul.loadClass("cn.edu.hpu.proxy.TankTimeProxy");
		System.out.println(c);
		
		//得到TankTimeProxy类的构造方法,构造方法的参数为Moveable类型
		Constructor ctr=c.getConstructor(Moveable.class);
		Moveable m=(Moveable)ctr.newInstance(new Tank());
		
		return m;
		
	}
}

到此为止,我们就自己写了一个生成动态代理的类。自己想往这个动态代理中写任何东西都可以。

我们虽然实现了一个动态代理,但是貌似我们只实现了Moveable接口的类的动态代理。如果换成实现了其它接口的类还是不行。那是不是可以产生一个对实现任意接口的类的代理?

思路:在Proxy类中,我们产生newProxyInstance()的时候,告诉程序你要实现哪个接口就可以了。

//这个方法用来产生新的代理类
//参数:告诉程序使用实现哪个接口的动态代理
public static Object newProxyInstance(Class infce) throws Exception{
	String rt="\r\n";
		
	String src=
		"package cn.edu.hpu.proxy;"+ rt +


	"public class TankTimeProxy implements "+infce.getName()+"{"+ rt +
	......
}

测试:
package cn.edu.hpu.proxy;


public class Client {
	public static void main(String[] args) throws Exception {
		
		Moveable m=(Moveable)Proxy.newProxyInstance(Moveable.class);
		m.move();
	}
}

测试结果:
class cn.edu.hpu.proxy.TankTimeProxy
开始时间:1436430626384ms
坦克正在移动中...
运行时间:9612ms

我现在想让它去实现其它接口的代理,也完全可以。

下面我们进行下一步的模拟。我们虽然可以换接口,但是我们下面的代码还是只是实现了一个move()方法,而不是我们实现的接口的方法,严格来说,实现的接口有多少方法,我们就应该给多少个方法生成代码。那么怎么知道这个Class中有多少个方法呢?

我们再写一个反射的测试:
我们想要知道一个类中有多少个方法,其实很简单:
package cn.edu.hpu.ProxyTest;

import java.lang.reflect.Method;


public class Test2 {
	public static void main(String[] args) {
		//使用了Java的反射机制中的getMethods();方法来得到一个类的所有方法
		Method[] methods=cn.edu.hpu.proxy.Moveable.class.getMethods();
		for (Method m:methods) {
			System.out.println(m.getName());
		}
	}
}
测试结果:
move

既然我们能拿到某各类所有方法,对于我们代理类的方法这么改写:
package cn.edu.hpu.proxy;


import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;


import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;


public class Proxy {
	
	//这个方法用来产生新的代理类
	//参数:告诉程序使用实现哪个接口的动态代理
	public static Object newProxyInstance(Class infce) throws Exception{
		String methodStr="";
		String rt="\r\n";
		
		//使用了Java的反射机制中的getMethods();方法来得到一个类的所有方法
		Method[] methods=infce.getMethods();
		for (Method m:methods) {
			methodStr += rt+"    @Override"+rt+
				"    public void "+m.getName()+"() {"+rt+
				"        this.before();"+ rt +
				"	     t."+m.getName()+"();"+ rt +
				"	     this.after();"+ rt +
				"    }"; //返回值通过反射机制也可以拿到,但是比较麻烦,我们这里暂时用void代替
		}
		
		
		
		String src=
			"package cn.edu.hpu.proxy;"+ rt +


		"public class TankTimeProxy implements "+infce.getName()+"{"+ rt +
		"    Moveable t;"+ rt +
		"    long start;"+ rt +
		"    long end;"+ rt +


		"    public TankTimeProxy(Moveable t) {"+ rt +
			"        super();"+ rt +
			"        this.t = t;"+ rt +
		"    }"+ rt +


		"    public void before(){"+ rt +
			"        start=System.currentTimeMillis();"+ rt +
			"        System.out.println(\"开始时间:\"+start+\"ms\");"+ rt +
		"    }"+
			
		"    public void after(){"+ rt +
			"        end=System.currentTimeMillis();"+ rt +
			"        System.out.println(\"运行时间:\"+(end-start)+\"ms\");"+ rt +
		"    }"+ rt +
			
		methodStr+ rt +
			
		"}";
		
		//拿到当前项目的根目录:System.getProperty("user.dir"));
		String fileName=System.getProperty("user.dir")
						+"/src/cn/edu/hpu/proxy/TankTimeProxy.java";
		
		//我们把src的源码写入自己创建的File文件中去
		File f=new File(fileName);
		FileWriter fw=new FileWriter(f);
		fw.write(src);
		fw.flush();
		fw.close();
		
		//编译(getSystemJavaCompiler()拿到系统默认的Java编译器,其实就是javac)
		JavaCompiler compiler=ToolProvider.getSystemJavaCompiler();
		//需要一个FileManager,用它来管理文件(参数1:诊断的监听器,参数2、3国际化)
		StandardJavaFileManager fileMgr=
			compiler.getStandardFileManager(null, null, null);
		//通过FileManager找到TankTimeProxy文件,然后放到一个Iterable中
		//Iterable是一个数组,用它可以进行迭代
		Iterable units=fileMgr.getJavaFileObjects(fileName);
		//参数:(输出位置,文件管理器对象,监听器,编译的时候指定的参数,用到那些class文件,需要编译哪些文件)
		CompilationTask t=compiler.getTask(null, fileMgr, null, null, null, units);
		//进行编译
		t.call();
		fileMgr.close();
		
		//把编译好的.class文件加载到内存中
		//urls指定.class文件所放的地方(使用URL还可以Load从网上传过来的类)
		URL[] urls=new URL[]{new URL("file:/"+System.getProperty("user.dir")+"/src")};
		//ClassLoader指的是吧硬盘里的Java文件放到内存中的那些个类
		URLClassLoader ul=new URLClassLoader(urls);
		Class c=ul.loadClass("cn.edu.hpu.proxy.TankTimeProxy");
		System.out.println(c);
		
		//得到TankTimeProxy类的构造方法,构造方法的参数为Moveable类型
		Constructor ctr=c.getConstructor(Moveable.class);
		Object m=ctr.newInstance(new Tank());
		return m;
		
	}
}

这样我们就可以生成实现任意接口的对象的代理类了。
测试:
package cn.edu.hpu.proxy;

public class Client {
	public static void main(String[] args) throws Exception {
		
		Moveable m=(Moveable)Proxy.newProxyInstance(Moveable.class);
		m.move();
	}
}

生成的TankTimeProxy
package cn.edu.hpu.proxy;
public class TankTimeProxy implements cn.edu.hpu.proxy.Moveable{
    Moveable t;
    long start;
    long end;
    public TankTimeProxy(Moveable t) {
        super();
        this.t = t;
    }
    public void before(){
        start=System.currentTimeMillis();
        System.out.println("开始时间:"+start+"ms");
    }    public void after(){
        end=System.currentTimeMillis();
        System.out.println("运行时间:"+(end-start)+"ms");
    }


    @Override
    public void move() {
        this.before();
	     t.move();
	     this.after();
    }
}

所以我们现在实现了这样一个功能:往newProxyInstance(Class infce)传任意接口,就能生成一个实现了某接口的类的对象。我们的“动态代理”又近了一步。

但是还有一个特别麻烦的事,我们现在在自己代理的内容是写死的。。

比如
"    public void before(){"+ rt +
			"        start=System.currentTimeMillis();"+ rt +
			"        System.out.println(\"开始时间:\"+start+\"ms\");"+ rt +
		"    }"+
			
		"    public void after(){"+ rt +
			"        end=System.currentTimeMillis();"+ rt +
			"        System.out.println(\"运行时间:\"+(end-start)+\"ms\");"+ rt 
这段,里面的代码是写死的,我们只能实现时间上的代理,我们想实现其它类型代理实现不了...

怎么让before()和after()中的内容也让客户灵活指定?

不管怎么样,我们现在需要一个这样的东西:可以动态指定对方法进行处理的指令。

我们下篇总结继续

转载请注明出处:http://blog.csdn.net/acmman/article/details/46828187

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

【Java数据结构】线性表

线性表 线性表是最基本、最简单、也是最常用的一种数据结构。 线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的(注意,这句话只适用大部分线性表,而...

【设计模式】动态代理Proxy_02

我们继续上一次的动态代理探讨。 上一篇我们说道,所以我们要实现一种"通用"代理,可以对任意对象代理。 那么怎么实现呢? 我们规定产生代理的时候,被代理的类一定要实现一个接口。这样我们可以根据接口来...

【设计模式】动态代理Proxy_01

大家都知道设计模式中一个比较难理解的模式--动态代理模式,下面我们来通过一步一步完善一个工程来学习动态代理。 首先我们创建一个JavaProject,名字为"ProxyTest"。 创建一个类Ta...

Java设计模式Proxy之动态代理

Java动态代理主要涉及到两个类: InvocationHandler:该接口中仅定义了一个Object : invoke(Object proxy, Method method, Object...

设计模式之动态代理-proxy

动态代理作为代理模式的一种扩展形式,广泛应用于框架(尤其是基于AOP的框架)的设计与开发,本文将通过实例来讲解Java动态代理的实现过程。        友情提示:本文略有难度,读者需具备代理模式相...

java设计模式:动态代理模式 Proxy

代理模式基本上可以理解为:本来让A做一件事情,可以把事情交给A的代理B去处理,B不仅仅把A该做的做掉,还可以在A该做的事情的基础上在做一些相关的事情; 所谓动态代理就是利用Java的反射机制,由程序...
  • sidihuo
  • sidihuo
  • 2016年02月25日 15:16
  • 243

设计模式之Proxy(代理):模拟JDK的动态代理

1.静态代理     代理的实现方式有两种,一是继承,二是聚合。示例如下:计算Tank中move方法的运行时间,不包括JDK为其准备运行环境的时间。 2.动态代理     如果有很多对象,要想对任意...

《大话设计模式》读书笔记:代理模式与Java的Proxy动态代理

代理模式(Proxy),为其他对象提供一种代理以控制对这个对象的访问。 在面向对象编程中,直接引用某些对象会因为种种原因(比如对象创建的开销过大,访问需要安全控制,或者需要跳出当前进程等)带来很多问...

《大话设计模式》读书笔记:代理模式与Java的Proxy动态代理

代理模式(Proxy),为其他对象提供一种代理以控制对这个对象的访问。 在面向对象编程中,直接引用某些对象会因为种种原因(比如对象创建的开销过大,访问需要安全控制,或者需要跳出当前进程等)带来很多问...

设计模式—静态代理模式和动态代理模式(Proxy Pattern)

一、代理模式 所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之前起到中介的作用。代理模式给某...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:【设计模式】动态代理Proxy_03
举报原因:
原因补充:

(最多只允许输入30个字)