Proxy

1. 从一个简单例子开始,如下定义一个接口和一个接口实现:

public interface Moveable {
	public void move();
}

 

public class Person implements Moveable {
	public void move() {
		System.out.println("Person remove");
	}
}

 

      现在要做的在人移动之前记录时间和输出日志,并且记录时间和输出日志的顺序可以任意控制,这中情况可以用静态代理来简单模拟,如下:



 如上图,记录日志和时间可以通过两个Person的代理俩完成,两个代码如下:

 

public class PersonLogProxy implements Moveable {
	private Moveable entity;
	public PersonLogProxy(Moveable entity) {
		this.entity = entity;
	}
	public void move() {
		System.out.println("[" + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.S'Z'").format(new Date()) + " Person move start]");
		entity.move();
		System.out.println("[" +  new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.S'Z'").format(new Date()) + " Person move End]");
	}
}

 

public class PersonTimeProxy implements Moveable {
	private Moveable entity;
	public PersonTimeProxy(Moveable entity) {
		this.entity = entity;
	}
	public void move() {
		Date start = new Date();
		System.out.println("START...");
		entity.move();
		System.out.println("END, TOTAL APENT: " + (new Date().getTime() - start.getTime()) + " MILLISECONDS.");
	}
}

  

如我们提出的问题,我们要实现先记录日志,再记录时间,可以执行下面代码:

Moveable person = new Person();
		Moveable timeProxy = new PersonTimeProxy(person);
		Moveable logProxy = new PersonLogProxy(timeProxy);
		logProxy.move();

 

运行结果:

[2010-08-30T17:15:09.437Z Person move start]
START...
Person remove
END, TOTAL APENT: 3984 MILLISECONDS.
[2010-08-30T17:15:13.437Z Person move End]

 

此种情况可以用下面时序图描述



 PersonLogProxy调运PersonTimeProxy,然后PersonTimeProxcy调运Person执行move方法,然后依次返回结束;

如我们要实现先记录时间,再记录日志,可以执行下面代码:

Moveable person = new Person();
		Moveable logProxy = new PersonLogProxy(person);
		Moveable timeProxy = new PersonTimeProxy(logProxy);
		timeProxy.move();

 

运行结果;

START...
[2010-08-30T17:23:27.281Z Person move start]
Person remove
[2010-08-30T17:23:29.453Z Person move End]
END, TOTAL APENT: 2203 MILLISECONDS.

 

同样先记录时间,在记录日志可以用下图说明



 PersonTimeProxy调运PersonLogProxy,然后PersonTimeProxcy调运Person执行move方法,然后依次返回结束;

      上面过程为简单静态代理,PersonTimeProxy和PersonLogProxy为Person代理,通过这种方式可以灵活实现先记录日志,或先记录时间的逻辑控制。

2. 动态代理开始

动态代理从字面上理解就是所需的代理类如上面PersonTimeProxy等不需要我们手动去完成,而是由程序动态的生成;java.lang.reflect.Proxy是JDK中用来产生动态代理的类,我们先简单模仿java.lang.reflect.Proxy生成动态代理类的过程,以动态生成运行PersonTimeProxy为例来说明,如下图所示:
 

      如上图所示要完成动态生成Person的时间代理大致需要3个步骤:

      第一:生成PersonTimeProxy.java临时文件,此文件是一个Java文件,实际JDK包中它没有删除文件,而是直接生成二进制文件;

      第二:编译第一步生成的文件,.java文件需要编译成.class文件才可以运行,所以这一步主要是如何将PersonTimeProxy.java编译成PersonTimeProxy.class文件;

      第三:运行第二步产生的PersonTimeProxy.class文件,首先需要将其加载到内存中,常用加载.class文件的方式是java.lang.ClassLoader及其子类,这里用URLClassLoader(),因为URLClassLoader不需要从他父类获得(其他的类加载器都需要从他的父类获得):

按照上面三步给出我写的代码:

package com.learn.proxy.test;

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

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

import com.learn.proxy.dynamic.Moveable;
import com.learn.proxy.dynamic.Person;

public class DynamicProxySimulation {

	public static void main(String[] args) throws IOException, ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
		//Step 1
		String str = 
			"	package com.learn.proxy.dynamic;      " + "\r\n" +
		    "                                         " + "\r\n" +
			"	import java.util.Date;                " + "\r\n" +
		    "                                         " + "\r\n" +
			"	public class PersonTimeProxy implements Moveable {   " + "\r\n" +
			"		private Moveable entity;                         " + "\r\n" +
			"		public PersonTimeProxy(Moveable entity) {        " + "\r\n" +
			"			this.entity = entity;                        " + "\r\n" +
			"		}                                                " + "\r\n" +
			"		public void move() {                             " + "\r\n" +
			"			Date start = new Date();                     " + "\r\n" +
			"			System.out.println(\"START...\");              " + "\r\n" +
			"			entity.move();                               " + "\r\n" +
			"			System.out.println(\"END, TOTAL APENT: \" + (new Date().getTime() - start.getTime()) + \" MILLISECONDS.\");" + "\r\n" +
			"		}                                                " + "\r\n" +
			"		                                                 " + "\r\n" +
			"	}                                                    " + "\r\n" ;
		String fileName = System.getProperty("user.dir") + "/proxy/com/learn/proxy/dynamic/PersonTimeProxy.java";
		File file = new File(fileName);
		FileWriter writer = new FileWriter(file);
		writer.write(str);
		writer.flush();
		writer.close();
		
		//Step 2
		JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
		StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
		Iterable units = fileManager.getJavaFileObjects(fileName);
		CompilationTask task = compiler.getTask(null, fileManager, null, null, null, units);
		task.call();
		fileManager.close();
		file.delete();
		
		//Step 3
		fileName = fileName.substring(0, fileName.length()- 4) + "class";
		URL[] urls = new URL[]{new URL("file://" + System.getProperty("user.dir") + "/proxy/")};
		URLClassLoader urlClassLoader = new URLClassLoader(urls);
		Class c = urlClassLoader.loadClass("com.learn.proxy.dynamic.PersonTimeProxy");
		
		//Step 4
		Constructor ctr = c.getDeclaredConstructor(Moveable.class);
		Moveable m = (Moveable) ctr.newInstance(new Person());
		m.move();
	}

}

 

上面代码中多出的第四步是验证代码,运行后结果如下:

START...
Person remove
END, TOTAL APENT: 8110 MILLISECONDS.

 

结果显示记录了人移动的时间。

分析:在动态代理开始种虽然我们没有类PersonTimeProxy,但我们却通过DynamicProxySimulation类完成了静态的 PersonTimeProxy类的功能如上就是简单的动态代理原理;

 

PS……接下来先继续模拟java.lang.reflect.Proxy,然后总的分析JDK中的java.lang.reflect.Proxy

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值