【设计模式】动态代理Proxy_02

我们继续上一次的动态代理探讨。

上一篇我们说道,所以我们要实现一种"通用"代理,可以对任意对象代理。
那么怎么实现呢?

我们规定产生代理的时候,被代理的类一定要实现一个接口。这样我们可以根据接口来生成代理对象,而不是根据具体的类。

我们明确一下我们的目标:我们可以对任何的对象,任何的类(前提是这个类实现了某个接口),我们就可以给它生成一个代理。

我们怎么样给它生成这个代理呢?为了模拟JDK的实现,我们添加一个新的类叫Proxy.java:

package cn.edu.hpu.proxy;

public class Proxy {

	//这个方法用来产生新的代理类
	public static Object newProxyInstance(){
		return null;
	}
}
里面的newProxyInstance()方法用来产生新的代理类。我们假设他可以产生新的代理类,那么在Client类中执行的时候,就是这个样子的:
package cn.edu.hpu.proxy;

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

在这里,代理的类叫什么名字我们都不需要知道。

在我们将来做设计的时候都是站在使用者的角度去设计的,刚刚我们就先去模拟Proxy的使用场景,再回头去完善这个类。

现在我们要求对坦克进行代理,将来也可能对别的类进行代理,所有的代理都将由Proxy的newProxyInstance()方法来完成。动态代理的意义就是,我将不会再看到像TankTimeProxy和TankLogProxy等代理类的名字,而是在动态代理类中完成代理,然后产生一个被代理之后的类。

我们在Proxy类中引入TankTimeProxy的所有代码(这里为了简单,将所有stop()方法去除了),将它作为字符串,成为其成员变量src:

package cn.edu.hpu.proxy;

public class Proxy {

	//这个方法用来产生新的代理类
	public static Object newProxyInstance(){

		String src=
			"package cn.edu.hpu.proxy;"+

		"public class TankTimeProxy implements 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();"+
			"}"+

		"}";

		return null;
	}
}

我们现在做这样一个假设,假设我们将src所存储的源码编译完,生成一个类,再把这个类load到内存,再由这个内存产生一个新的对象....想象一下,是不是这里面就是TankTimeProxy的代理吗。
如果我们真的能动态编译,那么我们就不再需要TankTimeProxy类了。我们只需要调用Proxy的newProxyInstance()方法,它会给我们动态编译一段代码,这段代码是实现了我们想要的代理的业务逻辑,比如说"时间"代理。实现完了之后,它会产生这个代理类的一个对象,然后反馈给我们。

可能大家有些绕,总结一下:动态代理怎么产生的呢?动态代理是你根本看不到那个代理类的名字,原来我们还要写一个TankTimeProxy,现在不用写,只要调用Proxy的newProxyInstance()方法,它会自动的返回一个具体的代理。而这个代理是Proxy内部生成一段代码,这段代码编译完之后生产一个具体对象,这段代码你想完成什么逻辑就完成什么逻辑,所以最后我们的难题就变成了,如果我们能动态编译这段代码的话,我们就能够生成这段代码的代理类,可以说我想在运行的时候生成什么类就生成什么类,我想怎么写就怎么写。

实现动态编译有N中方式,Jdk本身已经有动态编译的API(Complier API),也可以用一些网上现有的编译API,如CGLib,ASM,这些甚至不用调编译器来编译,它们会直接帮你生成编译好的二进制文件(因为Java的二进制编译规则在网上也是公开的)。

下面我们讲编译(注意编译API在JDK6及之后才有):
我们先简单的测试一下JDK1.6提供的Complier功能,我们平时编译都是在命令行中编译,输入javac来进行编译,我们下面在类中进行编译:

package cn.edu.hpu.ProxyTest;

import java.io.File;
import java.io.FileWriter;

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

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();
	}
}

我们运行main方法,之后在cn.edu.hpu.proxy包下生成了一个TankTimeProxy.Java类,然后我们查看Navigator视窗(表示工程在硬盘上的最原始情况),可以发现被编译好的class文件:

如图

编译好了,我们要把编译好的对象给load到内存中,因为我们的class文件在原始路径中,我么无法直接使用ClassLoad,要使用一个特殊的ClassLoad----URLClassLoader,在上面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);

运行main方法,发现打印出了class cn.edu.hpu.proxy.TankTimeProxy,说明类已经成功Load到内存中了。

当然,Load进内存之后我们要生成它的一个对象,这个事情就要牵扯到反射了,我们下篇总结再说。

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

时间: 2024-11-03 16:03:44

【设计模式】动态代理Proxy_02的相关文章

设计模式之动态代理(dynamic proxy)

1 动态代理与静态代理 我们从上一篇设计模式之代理模式一文中已经知道,在代理模式中代理对象和被代理对象一般实现相同的接口,调用者与代理对象进行交互.代理的存在对于调用者来说是透明的,调用者看到的只是接口.这就是传统的代理模式静态代理的特点. 那么传统的静态代理模式有什么问题呢?如果需要代理的类只有一个,那么静态代理没什么问题,如果有很多类需要代理呢,用静态代理的话就需要为每一个类创建一个代理类,显然这么做太过繁琐也容易出错.为此,JDK 5引入的动态代理机制,允许开发人员在运行时刻动态的创建出代

【设计模式】动态代理Proxy_01

大家都知道设计模式中一个比较难理解的模式--动态代理模式,下面我们来通过一步一步完善一个工程来学习动态代理. 首先我们创建一个JavaProject,名字为"ProxyTest". 创建一个类Tank.java,是一个坦克类,然后我们创建一个接口Moveable.java Moveable.java: package cn.edu.hpu.proxy; public interface Moveable { void move(); } Tank类实现Moveable接口 Tank.j

【设计模式】【动态代理,在方法前和方法后加事务,AOP】

/** * Description: * <br/>网站: <a href="http://www.crazyit.org">疯狂Java联盟</a> * <br/>Copyright (C), 2001-2010, Yeeku.H.Lee * <br/>This program is protected by copyright laws. * <br/>Program Name: * <br/>Da

跟屌丝大哥学习设计模式---代理模式之动态代理

动态代理 java中动态代理机制的引入使得代理模式的思想更加完善与进步,它允许动态的创建代理并支持对动态的对所代理的方法进行调用.Java动态代理类位于Java.lang.reflect包下,一般主要涉及到以下两个类:  (1). Interface InvocationHandler:该接口中仅定义了一个方法Object:invoke(Object obj,Method method, Object[] args).在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上

【设计模式】动态代理Proxy_03

我们继续上一篇总结. 我们把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 j

设计模式之代理模式(Proxy Pattern)

1 代理模式定义 定义:给某个对象提供一个代理对象,并由代理对象控制对于原对象的访问,即客户不直接操控原对象,而是通过代理对象间接地操控原对象. 本篇文章主要介绍的是静态代理,关于动态代理请参考:设计模式之动态代理(dynamic proxy) 2 代理模式的作用 在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用. 通过代理类对原有类进行功能增强,如增加缓存.异常处理.日志处理等,Spring AOP用的就是(动态)代理模式 3 代理模式一

PHP4中实现动态代理

动态 一. 摘要本文简单阐述了Proxy模式及具体说明了如何在PHP4中实现动态代理, 本文只是给出了一个实现的方法的原型. 由于水平有限, 有任何意见和建议请反馈给Binzy [ Binzy at JustDN dot Com ]. 二. 概述在我们开始实现Dynamic Proxy之前, 也许我们应该先了解一下什么是Proxy和它有什么用. 下面是一篇来自博客堂加菲猫的不错的形象讲述Proxy的文章: "武当学艺与缓存代理". Proxy模式是"GoF"介绍的2

JDK动态代理简介

动态代理 代理模式是 Java 中的常用设计模式,代理类通过调用被代理类的相关方法,提供预处理.过滤.事后处理等服务,动态代理及通过反射机制动态实现代理机制.JDK 中的 java.lang.reflect.Proxy 类可以用来实现动态代理. 首先,准备一个简单的接口和实现类 /** * 接口 IHello.java */ public interface IHello { void hello(); } /** * 实现类 Hello.java */ public class Hello i

Java动态代理机制分析及扩展,第1部分

引言 Java 动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要 简单地指定一组接口及委托类对象,便能动态地获得代理类.代理类会负责将所 有的方法调用分派到委托对象上反射执行,在分派执行的过程中,开发人员还可 以按需调整委托类对象及其功能,这是一套非常灵活有弹性的代理框架.通过阅 读本文,读者将会对 Java 动态代理机制有更加深入的理解.本文首先从 Java 动态代理的运行机制和特点出发,对其代码进行了分析,推演了动态生成类的内 部实现. 代理:设计模式 代理是一种常用的设