`
gongjiayun
  • 浏览: 158785 次
  • 性别: Icon_minigender_1
  • 来自: 长沙
社区版块
存档分类
最新评论

java代理模式

阅读更多
Java动态代理模式
1. 代理:一个角色代表别一个角色来完成某些特定的功能。
比如:生产商,中间商,客户这三者这间的关系
客户买产品并不直接与生产商打交道,也不用知道产品是如何产生的,客户只与中间商打交道,而中间商就可以对产品进行一些包装,提供一些售后的服务。

代理模式有三个角色: 1. 抽象主题角色 2. 代理主题角色 3. 实际被代理角色
其它类通过访问代理主题角色来访问实际被代理角色。

2. 下面我们来个一个静态代理的实现。
我以一个坦克为例。
抽象主题角色:Moveable
    package com.gjy.proxy;

public interface Moveable {
		void move();
}

代理主题角色:TanktimeProxy
	package com.gjy.proxy;

public class TanktimeProxy implements Moveable{
		private Moveable t;
	
		public TanktimeProxy(Moveable t) {
			super();
			this.t = t;
		}


		@Override
		public void move() {
			long time1 = System.currentTimeMillis();
			System.out.println("time1="+time1);
			t.move();
			long time2 = System.currentTimeMillis();
			System.out.println("time2="+time2);
			System.out.println("运行时间为:"+(time2-time1));
		}
}

实际被代理对象:Tank
package com.gjy.proxy;

public class Tank implements Moveable{

		@Override
		public void move() {
			System.out.println("TanK moving........");
		}
	
}


测试:
package com.gjy.proxy;

public class TestTank {
		public static void main(String[] args) {
			Tank t = new Tank();
			Moveable move = new TanktimeProxy(t);
			move.move();
		
		}
}

从上例可以看到代理主题角色:TanktimeProxy实现了对Tank的move()方法运行时间的计算,而TanktimeProxy,Tank都实现了Moveable接口,通过调用TanktimeProxy的move()方法我们可以实现对Tank的move()方法的运行时间的计算,而不用在Tank的move()方法中作任何实现,这就是代理的作用。代理实现时TanktimeProxy,Tank必需实现Moveable接口。

下面我想在TanK的move()方法前后加上日志:
我必需再写一个类来实现这一功能:
package com.gjy.proxy;

public class TanklogProxy implements Moveable{
		private Moveable t;
	
		public TanklogProxy(Moveable t) {
			super();
			this.t = t;
		}


		@Override
		public void move() {
			System.out.println("start move........");
			t.move();
			System.out.println("end move......");
		}
}

测试:
package com.gjy.proxy;

public class TestTank {
	public static void main(String[] args) {
			Tank t = new Tank();
			Moveable move = new TanktimeProxy(t);
			Moveable movet = new TanklogProxy(move);
			movet.move();
		
		}
}

这样我通过代理在Tank的move()方法前后加入了日志和时间统计的功能,由于TanktimeProxy,TanklogProxy都实现了Moveable接口,所以TanklogProxy可以代理TanktimeProxy,反过来也可以,它们对Tank的代理顺序是可以交换的。

如果我想在Tank的move()方法调用的前后加入更多的功能,是不是要写更多的代理主题角色,这样子会使得代码过于臃肿,不易于维护,那有没有什么办法可以解决呢,答案是可以的,我们可以动态的来生成代理主题角色,来代理所有的被代理对象,这就是动态代理。

下面是一个简单的动态代理的实现:
类图如下:


首先编写一个生成代理主题角色的类:Proxy
package com.gjy.DynamicProxy;

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 newProxyIntenct(Class infac,InvocationHandler h) throws Exception{
			String br ="\r\n";
		
			String methString ="";
			Method[] method = infac.getMethods();
		
			for(Method m: method){
				methString = "	@Override"+ br +
					"	public void "+m.getName()+"() {"+ br +
					"       try {" + br +
			"       Method md ="+ infac.getName()+".class.getMethod(\""+m.getName()+"\");"+ br +
			    		"       h.invoke(this,md);" + br +
			   		"       }catch (Exception e){ "+ br+ 
			    		"           e.printStackTrace();" + br + 
			    		"       }" + br +
					"	}";
			}
		
			String src = 
		    		"package com.gjy.DynamicProxy;" + br +
		    		"import java.lang.reflect.Method;" + br +
"public class $Proxy implements "+infac.getName()+"{" + br +
"	private com.gjy.DynamicProxy.InvocationHandler h;" + br +
				"	public $Proxy(InvocationHandler h) {" + br +
				"		super();" + br +
				"		this.h = h;" + br +
				"	}" + br + br +
				methString +br +
				"}";
				MakFileUtil.createFile("D:/src/com/gjy/DynamicProxy");
				//生成java文件
String fileName ="D:\\src\\com\\gjy\\DynamicProxy\\$Proxy.java";
				System.out.println(fileName);
				File file = new File(fileName);
				FileWriter fWriter = new FileWriter(file);
				fWriter.write(src);
				fWriter.flush();
				fWriter.close();
			
				//生成class文件,jdk6提供的工具类
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
				//System.out.println(compiler.getClass().getName());
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();
			
				//装载到内存,生成新对象
				URL[] urls = new URL[]{new URL("file:/"+"D:\\src\\")};
				URLClassLoader loader = new URLClassLoader(urls);
Class c = loader.loadClass("com.gjy.DynamicProxy.$Proxy");
			
				//通过有参的构造器反射生成代理类的实例
Constructor ctr = c.getConstructor(InvocationHandler.class);
				Object obj = (Object) ctr.newInstance(h); 
				return obj;
		}
}


代理对象的操作接口:
package com.gjy.DynamicProxy;

import java.lang.reflect.Method;

public interface InvocationHandler {
		void invoke(Object o,Method m);
}

通过实现代理对象的操作接口实现对被代理对象的方法调用前后的逻辑操作。

TimeInvocationHandler实现InvocationHandler接口:
package com.gjy.DynamicProxy;

import java.lang.reflect.Method;


public class TimeInvocationHandler implements InvocationHandler {
  		private Object target;
		public TimeInvocationHandler(Object target) {
			super();
			this.target = target;
		}
		@Override
		public void invoke(Object o, Method m) {
			long time1 = System.currentTimeMillis();
			System.out.println("time1="+time1);
       	 try {
				m.invoke(target);
			} catch (Exception e) {
				e.printStackTrace();
			}
			long time2 = System.currentTimeMillis();
			System.out.println("time2="+time2);
			System.out.println("Tank 的启动时间:"+(time2-time1));
		}

}


实际被代理对象:Tank
	package com.gjy.DynamicProxy;

public class Tank implements Moveable{

		@Override
		public void move() {
			int a = 5;
			int b = 6;
			int c = 0;
			int d = 0;
			for (int i = 0; i < 1000; i++) {
				d = i;
			}
			c = ((a+b)/2)*12;
			System.out.println("TanK moving..Tank 的速度是"+c);
		
		}
	

抽象代理主题:Moveable
	package com.gjy.DynamicProxy;
public interface Moveable {
			void move();
}
}





测试:
	package com.gjy.DynamicProxy;
public class TestTank {
		public static void main(String[] args) throws Exception{
			Tank t = new Tank();
Moveable moveable = (Moveable) Proxy.newProxyIntenct(Moveable.class,new TimeInvocationHandler(t));
			moveable.move();
		
		}
}

创建文件夹工具类:MakFileUtil
package com.gjy.DynamicProxy;

import java.io.File;
import java.io.IOException;
import java.util.StringTokenizer;

public class MakFileUtil {
		public static void createFile(String pathstr) throws IOException{
//		File dirFile;
//		boolean bFile;
//		bFile = false;
//	
//		dirFile = new File("E:\\test");
//		bFile = dirFile.exists();
//	
//		if( bFile == true ){
//			System.out.println("The folder exists.");
//		}else{
//	System.out.println("The folder do not exist,now trying to create a one...");
//			bFile = dirFile.mkdir();
//			if( bFile == true ){
//				System.out.println("Create successfully!");
//			}else{
//	System.out.println("Disable to make the folder,please check the disk is full or not.");
//			System.exit(1);
//		}
			//创建多级目录
		 	String path = pathstr;  
//为指定字符串构造一个 string tokenizer。 "/"字符是分隔标记的分隔符。分隔符字符本身不作为标记。
	     	StringTokenizer st = new StringTokenizer(path,"/");  
	     	String path1 = st.nextToken()+"/";  
	     	String path2 = path1;  
	    	 	while(st.hasMoreTokens())  
	     	{  
	           	path1 = st.nextToken()+"/";  
	          	path2 += path1;
	           	File inbox = new File(path2);  
	           	if(!inbox.exists())  
	                	inbox.mkdir();  
	     	} 
		}
}

以上就是动态代理的一个模拟实现,测试时我们不管Proxy和InvocationHandler是怎么实现的,我们只要实现InvocationHandler接口完成相应的逻辑,然后调用Proxy
的newProxyIntenct(Class infac, InvocationHandler h) 传入相应的接口,和InvocationHandler的实现类就可以实现对被代理对象的代理。也就是说Proxy和InvocationHandler写好之后永远不变。

在运行过程中Proxy会动态生成代理主题角色,示例中生成的代理主题角色的代码如下:
import java.lang.reflect.Method;
public class $Proxy implements com.gjy.DynamicProxy.Moveable{
		private com.gjy.DynamicProxy.InvocationHandler h;
		public $Proxy(MakFileUtil h) {
			super();
			this.h = h;
		}
		@Override
		public void move() {
      		try {
Method md =com.gjy.DynamicProxy.Moveable.class.getMethod("move");
       			h.invoke(this,md);
       		}catch (Exception e){ 
           		e.printStackTrace();
       		}
		}
}

如果我们想在Tank的move()方法被调用的前后加入其它的逻辑处理,我们只需实现InvocationHandler接口,下面是给move()加日志:
package com.gjy.DynamicProxy;

import java.lang.reflect.Method;

public class LogInvocationHandler implements InvocationHandler {
    		private Object target;
		public LogInvocationHandler(Object target) {
			super();
			this.target = target;
		}
		@Override
		public void invoke(Object o, Method m) {
			System.out.println("Tank start...........");
        		try {
				m.invoke(target);
			} catch (Exception e) {
				e.printStackTrace();
			}
			System.out.println("Tank stop..............");
		}
}


测试:
package com.gjy.DynamicProxy;

public class TestTank {
		public static void main(String[] args) throws Exception{
			Tank t = new Tank();
Moveable moveable = (Moveable) Proxy.newProxyIntenct(Moveable.class,new TimeInvocationHandler(t));
Moveable moveable2 = (Moveable) Proxy.newProxyIntenct(Moveable.class, new LogInvocationHandler(moveable));
			moveable2.move();
		}
}

同样代理顺序可以交换,这就是动态代理的实现过程。
分享到:
评论
4 楼 h416373073 2015-10-25  
多谢分享.
3 楼 liangsaifei 2014-04-25  
我说那么面熟,原来是马士兵讲过的啊!
2 楼 jm89312 2014-03-13  
马士兵的代理例子
1 楼 麻袋里有个布袋 2013-09-18  
刚学习代理模式,表示非常抽象

相关推荐

Global site tag (gtag.js) - Google Analytics