`

马士兵讲过的动态代理

    博客分类:
  • JAVA
阅读更多


@2014年2月12日14:14:38


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

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

2. 下面我们来个一个静态代理的实现。
我以一个坦克为例。
抽象主题角色:Moveable
package com.bjsxt.proxy;  
public interface Moveable {  
  void move();  
}

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

public class Tank implements Moveable{

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


代理主题角色:TanktimeProxy
package com.bjsxt.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));
		}
}

测试:

package com.bjsxt.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.bjsxt.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.bjsxt.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.bjsxt.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.bjsxt.DynamicProxy;

import java.lang.reflect.Method;

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

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

package com.bjsxt.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.bjsxt.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.bjsxt.DynamicProxy;
public interface Moveable {
		void move();
    }
}

测试:

package com.bjsxt.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.bjsxt.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.bjsxt.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.bjsxt.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();
		}
}
同样代理顺序可以交换,这就是动态代理的实现过程。
分享到:
评论

相关推荐

    jvm java虚拟机 调优 马士兵 笔记

    jvm java虚拟机 调优 马士兵 笔记 让你对java虚拟机调优有初步的认识

    马士兵老师spring框架学习笔记

    马士兵老师是知名的Java教育专家,他的Spring框架学习笔记深入浅出,对于初学者和进阶者来说都是一份宝贵的资源。这份笔记涵盖了Spring的核心概念、配置、AOP(面向切面编程)、DI(依赖注入)等关键知识点。 1. **...

    马士兵oracle笔记

    马士兵oracle笔记,浅显易懂。

    马士兵JAVA笔记(全).docx

    马士兵JAVA笔记(全) 本资源是马士兵的JAVA笔记,涵盖了JAVA语言的基础知识,包括标识符、常量、变量、数据类型、运算符、控制流语句等。 标识符是JAVA中的一种符号,用于命名变量、方法、类等。标识符由字母、...

    马士兵jvm调优笔记.docx

    ### 马士兵JVM调优笔记知识点梳理 #### 一、Java内存结构 Java程序运行时,其内存被划分为几个不同的区域,包括堆内存(Heap)、方法区(Method Area)、栈(Stack)、程序计数器(Program Counter Register)以及...

    马士兵课程源码11111111111111111111

    马士兵课程源码11111111111111111111

    马士兵 html css js源码

    马士兵是一位知名的IT教育专家,他的课程深入浅出,适合初学者和有一定基础的学习者。这个“马士兵 html css js源码”压缩包很可能包含了他在教学过程中用到的各种实例和特效代码,是学习和实践这些技术的宝贵资源。...

    Shopping. java 马士兵

    《Java网上商城系统——马士兵教程深度解析》 在当今数字化时代,网上商城已经成为商业活动中不可或缺的一部分。本项目,名为“Shopping”,是一个基于Java技术构建的可运行的购物系统,由知名IT教育专家马士兵倾力...

    马士兵Struts2笔记2013

    以下是关于"马士兵Struts2笔记2013"中的关键知识点详解: 1. **建立Struts2工程** 创建一个Struts2工程通常涉及在集成开发环境中(如MyEclipse)创建一个新的Web项目,然后将必要的Struts2库添加到项目的`lib`目录...

    马士兵 J2SE代码+PPT教材

    【马士兵 J2SE代码+PPT教材】是针对Java初学者和爱好者提供的一套学习资源,由知名IT教育专家马士兵编著。这套教材涵盖了Java Standard Edition (J2SE) 的核心概念和技术,旨在帮助读者掌握Java编程的基础知识和实践...

    JAVA就业班笔记【马士兵说】.nyf

    JAVA就业班笔记【马士兵说】.nyf

    马士兵老师HashMap学习笔记

    《马士兵老师HashMap学习笔记详解》 HashMap是Java编程语言中常用的一种数据结构,它提供了键值对(key-value pair)的存储功能,是基于哈希表实现的。马士兵老师的HashMap学习笔记深入剖析了这一核心组件的工作...

    马士兵_操作手册

    马士兵 struts2 操作手册

    马士兵 mybatis学习笔记

    马士兵老师的MyBatis学习笔记主要涵盖了以下几个核心知识点: 1. **MyBatis安装与环境搭建**:首先,你需要配置Java开发环境,并了解Maven或Gradle构建工具,因为MyBatis通常与这些工具结合使用来管理依赖。然后,...

    马士兵Hibernate文档

    根据提供的文件信息,“马士兵Hibernate文档”是一份与马士兵Hibernate视频教程相匹配的文档资料。这份文档旨在为学习者提供全面、详细的Hibernate框架的学习资料,帮助他们更好地理解和掌握Hibernate的相关概念和...

    Spring学习笔记(马士兵spring视频笔记).doc

    Spring学习笔记(马士兵spring视频笔记).docSpring学习笔记(马士兵spring视频笔记).docSpring学习笔记(马士兵spring视频笔记).docSpring学习笔记(马士兵spring视频笔记).doc

    马士兵老师的java io流课程

    马士兵老师的这门课程深入浅出地讲解了Java IO流的相关概念和技术,旨在帮助学员掌握数据输入、输出的各种方式。 首先,我们要了解Java IO流的基本概念。在Java中,IO(Input/Output)流是一组用于处理输入和输出...

    马士兵NewShopping代码

    是自己根据马士兵的NewShopping模仿着一步步做的,对项目的界面进行了美化,更加漂亮美观,并对视频中项目一些细节不足的地方进行了一些修正,并添加了部分功能进行完善,但是对于视频中报表和文件上传的功能我并...

    马士兵百万级并发IM即时消息系统

    《马士兵百万级并发IM即时消息系统》是针对大规模用户在线聊天场景设计的高效、稳定、可扩展的即时通讯解决方案。该系统的核心是利用Go语言的高性能特性,结合现代Web开发框架Gin来构建,旨在处理百万级别的并发连接...

    马士兵Springboot课堂笔记

    马士兵Springboot课堂笔记

Global site tag (gtag.js) - Google Analytics