动态代理(Proxy)

本文主要讨论代理模式和动态代理

一.代理模式的定义:

为其他对象提供一种代理以控制对这个对象的访问


二.代理模式的结构说明:

Proxy:代理对象,实现与具体的目标对象一样的接口,并且保存一个指向目标对象的引用,可以在需要的时候调用具体的目标对象。

Subject:目标接口,定义具体的接口

SubjectImpl:目标接口的具体实现


三.代理模式的示例代码:

package com.interfaces;

public interface Subject {
	void request();
}
package com.interfaces.impl;

import com.interfaces.Subject;

public class SubjectImpl implements Subject{

	@Override
	public void request() {
		System.out.println("request method run ....");
	}
	
}
package com.interfaces.impl;

import com.interfaces.Subject;

public class SubjectProxy implements Subject {
	Subject t = null;
	
	public SubjectProxy(Subject t) {
		super();
		this.t = t;
	}

	@Override
	public void request() {
		//do something
		System.out.println("log:before request ...");
		t.request();
		//do another thing
		System.out.println("log:after request method,done !!");
	}

}

四.代理模式的简单应用

package com.interfaces;

public interface Moveable {
	void move();
}
package com.interfaces.impl;

import com.interfaces.Moveable;

public class TankLogProxy implements Moveable {
	Moveable t = null;
	
	public TankLogProxy(Moveable t) {
		super();
		this.t = t;
	}

	@Override
	public void move() {
		System.out.println("log:tank start ...");
		t.move();
		System.out.println("log:tank stop !!");
	}

}
package com.interfaces.impl;

import com.interfaces.Moveable;

public class TankTimeProxy implements Moveable {
	Moveable t = null;
	
	public TankTimeProxy(Moveable t) {
		super();
		this.t = t;
	}

	@Override
	public void move() {
		long start = System.currentTimeMillis();
		System.out.println("time: start");
		t.move();
		long end = System.currentTimeMillis();
		System.out.println("time:"+(end-start));
	}

}
package com.model;

import com.interfaces.Moveable;

public class Tank implements Moveable{

	@Override
	public void move() {
		System.out.println("Tank move ....");
		try {
			Thread.currentThread().sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
}
package com.test;

import com.interfaces.impl.TankLogProxy;
import com.interfaces.impl.TankTimeProxy;
import com.model.Tank;

public class Test {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Tank t = new Tank();
		TankTimeProxy ttp = new TankTimeProxy(t);
		TankLogProxy tlp = new TankLogProxy(ttp);
		tlp.move();
	}

}

输出结果:

log:tank start ...

time: start
Tank move ....
time:3000

log:tank stop !!

代理模式针对上面tank的move方法提供了一种比较完美的解决方案,它解决了能够在move方法前后添加不同的逻辑,而且还能灵活的将这种逻辑调换位置,比较灵活;同时它也有一个很严重的弊端,假设一个系统有500个不同的接口(不同于Moveable接口,里面有不同的方法),如果我要为这500个不同的接口具体的实现的类的前后都记录日志,那岂不是需要写500个LogProxy?那么这个工作量无疑太大,而动态代理却能够很好的解决这个问题....


动态代理(Proxy)

一.下面模拟JDK的具体实现(当然JDK中具体实现动态代理的方法没这么笨,它用的是直接操作二进制的方法来实现的)

package com.interfaces;

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.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

import com.complier.test.ComplierUtil;

public class Proxy {
	
	public static Object newInstanceProxy(Class inter,com.interfaces.InvocationHandler h){
		String methodStr = "";
		//利用反射得到这个接口里的所有方法
		Class cla1 = null;
		try {
			cla1 = Class.forName(inter.getName());
		} catch (ClassNotFoundException e1) {
			e1.printStackTrace();
		}
		
		//循环生成方法
		for(Method m:cla1.getMethods()){
			methodStr += "	 @Override\n"+
			" 	public void "+m.getName()+"() {\n"+
			"	try {\n"+
			"		Method m = "+inter.getName()+".class.getMethod(\""+m.getName()+"\");\n"+
			"		System.out.println(m.getName());\n"+
			"		h.invoke(this,m);\n"+
			"		} catch (SecurityException e) {\n"+
			"			e.printStackTrace();\n"+
			"		} catch (NoSuchMethodException e) {\n"+
			"			e.printStackTrace();\n"+
			"		}\n"+
			"	}\n\n";
		}
		
		String src = ""+

		"import "+inter.getName()+";\n"+
		"import java.lang.reflect.Method;\n"+

		"public class $Proxy1 implements "+inter.getName()+" {\n"+
		"    private com.interfaces.InvocationHandler h = null;\n\n"+
			
		"	 public $Proxy1(com.interfaces.InvocationHandler h) {\n"+
		"	 	super();\n"+
		" 	 	this.h = h;\n"+
		"	 }\n\n"+
		
		methodStr+
		
		"}\n";
		
		//写入文件
		FileWriter fw = null;
		File f = null;
		try {
			String path=System.getProperty("user.dir");
			f = new File(path+"/$Proxy1.java");
//			System.out.println(f.getAbsolutePath());
			if(!f.exists()){
				f.createNewFile();
			}
			fw = new FileWriter(f);
			fw.write(src);
			fw.close();
		} catch (IOException e) {
			e.printStackTrace();
		} finally{
			if(fw!=null){
				try {
					fw.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
				fw = null;
			}
		}
		//构建对象	
		Object obj = null;
		try {
			//编译java类
			ComplierUtil.complier(f);
			//从任何一个地方载入类
//			System.out.println(System.getProperty("user.dir")+"/");
			URL[] urls = new URL[] {new URL("file:/"+System.getProperty("user.dir")+"/")};
			URLClassLoader ul = new URLClassLoader(urls);
			Class cla = ul.loadClass("$Proxy1");
			Constructor construct = cla.getConstructor(InvocationHandler.class);
			obj = construct.newInstance(h);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (MalformedURLException e) {
			e.printStackTrace();
		}
		return obj;
	}
	
}
package com.complier.test;

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

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

public class ComplierUtil {
	
	public static void complier(File f){
		JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
		StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);

		Iterable compilationUnits1 =
		fileManager.getJavaFileObjectsFromFiles(Arrays.asList(f));
		compiler.getTask(null, fileManager, null, null, null, compilationUnits1).call();
		
		try {
			fileManager.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
package com.interfaces;

public interface Moveable {
	void move();
}
package com.interfaces.impl;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import com.interfaces.InvocationHandler;


public class TimeHandler implements InvocationHandler {
	
	private Object target = null;
	
	public TimeHandler(Object obj){
		target = obj	;
	}
	
	@Override
	public void invoke(Object o, Method m) {
		 long start = System.currentTimeMillis();
		 System.out.println("time: start");
		 try {
			m.invoke(target, null);
//			System.out.println(o.getClass());
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		 long end = System.currentTimeMillis();
		 System.out.println("spend time:"+(end-start)/1000+"s");
	}

	public Object getTarget() {
		return target;
	}

}
package com.interfaces;

import java.lang.reflect.Method;
//对代理对象前后需要进行的处理逻辑接口
public interface InvocationHandler {
	void invoke(Object o,Method m);
}
package com.model;

import com.interfaces.Moveable;
//具体被代理的对象
public class Tank implements Moveable{

	@Override
	public void move() {
		System.out.println("Tank move ....");
		try {
			Thread.currentThread().sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

}
package com.test;

import com.interfaces.InvocationHandler;
import com.interfaces.Moveable;
import com.interfaces.Proxy;
import com.interfaces.impl.TimeHandler;
import com.model.Tank;
//测试类
public class Client {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		InvocationHandler handler = new TimeHandler(new Tank());
		Moveable t = (Moveable) Proxy.newInstanceProxy(Moveable.class,handler);
		t.move();
	}

}
输出结果:

move

time: start

Tank move ....

spend time:3s


二.使用JDK的动态代理

package com.jdk.proxy.test;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//首先必须实现InvocationHandler接口,而且这个接口中必须包含被代理对象的引用,这点很关键
public class LogHandler implements InvocationHandler {
	private UserManagerImpl target = null;

	public LogHandler(UserManagerImpl target) {
		super();
		this.target = target;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		System.out.println("log: start record log");
		method.invoke(target, null);
		System.out.println("log: end record log");
		return null;
	}

}
package com.jdk.proxy.test;

public interface UserManager {
	void addUser();
}
package com.jdk.proxy.test;
//被代理对象
public class UserManagerImpl implements UserManager {

	@Override
	public void addUser() {
		System.out.println("save user...");
	}

}
package com.jdk.proxy.test;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Test01 {
	public static void main(String args[]){
		InvocationHandler handler = new LogHandler(new UserManagerImpl());
		UserManager userManager = (UserManager)Proxy.newProxyInstance(Test01.class.getClassLoader(),new Class[]{UserManager.class}, handler);
		userManager.addUser();
	}
}


AOP面向切面编程即动态代理思想的应用,动态代理能应用于日志记录,权限管理,Transaction的控制,等等方面;有了动态代理的基础学习Spring中AOP的核心内容,能更加深刻的理解AOP的实现。

当然,模式动态代理那个Proxy类里面有几个地方比较难,需要好好理解,怎么从硬盘上任何一个地方的java文件编译成class文件,怎么将任何一个class文件载入内存,以及运行时自动生成的动态代理类里层代码和外层代码的关系,都需要好好理解;其中invoke方法传入的第一个参数是this,虽然在InvocationHandler类中并没有使用这个对象,但并不表示这个对象就没有用。

动态代理的过程:Proxy对象会自动生成一个对象,这个对象一定会实现的客户传给他的一个接口(这个接口就是被代理对象必须实现的接口),当客户调用这个接口的一个方法时,此时调用的实际上是Proxy产生的一个代理对象中实现了这个接口的这个方法,然后这个代理对象实现这个接口方法的具体实现是去调用InvocationHandler的invoke方法,同时在InvocationHandler这个类中必须保存被代理对象的引用,以便能够在invoke方法中调用被代理对象接口的这个方法,在invoke方法中客户就可以添加自己任何想添加的逻辑...这就是动态代理

动态代理能够在不改变源码的情况下实现对任何对象,任何方法的前后添加任何逻辑!

动态代理到此基本结束,从代理模式到动态代理,从模拟JDK的动态代理到具体动态代理的使用,此文参考了《研磨设计模式》一书,且引用了尚学堂马士兵老师的讲解思路。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值