`
gongjiayun
  • 浏览: 159682 次
  • 性别: 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  
刚学习代理模式,表示非常抽象

相关推荐

    Java代理模式Java动态代理

    ### Java代理模式与Java动态代理详解 #### 一、代理模式概述 代理模式是一种软件设计模式,它在客户端和目标对象之间提供了一种间接层。这种模式的主要目的是控制客户端对目标对象的访问,并且可以在不修改原有...

    java 代理模式详解

    总结一下,Java代理模式的核心在于`Proxy`类和`InvocationHandler`接口,它们共同实现了在运行时动态创建符合特定接口的代理对象。通过代理,我们可以在不修改原始对象代码的情况下,添加额外的功能或者控制对原始...

    Java 代理模式

    Java代理模式是一种设计模式,它允许我们为一个对象创建一个代理对象,该代理对象在调用实际对象的方法之前或之后可以执行额外的操作。这在不修改原有对象代码的情况下,提供了扩展功能的可能性。代理模式通常分为两...

    java代理模式总结

    Java代理模式是一种设计模式,它允许我们为现有的对象创建一个代理对象,以便在不修改原对象的情况下增强或扩展其功能。这种模式在处理需要添加额外逻辑但又不想修改原始类的场景时尤其有用,比如日志、事务管理、...

    java代理模式和动态代理

    ### Java代理模式和动态代理详解 #### 一、概述 在软件工程中,代理模式是一种常用的软件设计模式,主要用于在不直接暴露目标对象的情况下提供一个替代接口。这种模式可以简化客户端与目标对象之间的交互,同时还...

    Java代理模式模板代码,包含动态代理与静态代理

    Java代理模式是一种设计模式,它允许我们为一个对象创建一个代理,这个代理对象可以在不影响原始对象功能的基础上,增强或扩展其行为。代理模式在软件开发中广泛应用,例如用于权限控制、事务管理、日志记录等场景。...

    java代理模式示例源码

    在给定的“java代理模式示例源码”中,我们可以通过房产中介的例子来理解这种模式。 房产中介在现实生活中,是客户与房东之间的桥梁,它可以代表客户处理租赁事宜,也可以在房东和客户之间传递信息,这与代理模式的...

    16-Java代理模式的学习笔记1

    Java代理模式是一种设计模式,它允许我们为一个对象创建一个代理,这个代理对象可以在调用原始对象的方法之前或之后执行额外的操作。代理模式在软件工程中有多种应用,例如:增加安全控制、性能监控、事务管理等。...

    Java代理模式例子

    Java代理模式 事例很生动,非常容易理解,推荐给大家。

    java 代理模式

    Java代理模式是一种设计模式,它允许我们为一个对象创建一个代理,这个代理对象可以在调用原始对象的方法之前或之后执行额外的操作。代理模式在很多场景下都非常有用,比如在访问对象时增加安全性、性能监控、事务...

    JAVA设计模式之代理模式实例

    在本实例中,我们将深入探讨Java中的代理模式及其应用。 代理模式的核心思想是为一个对象创建一个代理对象,这个代理对象在客户端和目标对象之间起到中介的作用。它可以控制目标对象的访问,也可以在调用目标对象的...

    java实现代理模式

    用java实现代理模式,使用房屋中介的现实例子进行代理模式的说明

    Java代理模式.pdf

    Java代理模式.pdf

Global site tag (gtag.js) - Google Analytics