JAVA的代理模式与动态代理深入分析


代理模式

一、逻辑

代理人  和被代理对象

代理人表现出 被代理对象具有的所有方法(因此可以设计出接口)

代理人可以做一些额外的工作,比如帮你记下时间或者检查下权限,然后实际的工作还是要调用被代理对象去做


(因为具有相同的方法,所以可以实现相同的接口,当然不实现也是可以的)


二、类的设计方案

基于这样的逻辑

我们有两种设计类的方案:

1.继承

代理类继承被代理类。这样构造出来的代理人,将被代理人传入构造方法,然后作为其父类对象。这样相当于儿子给父亲做代理,任何事情先找儿子。你要和万达集团合作,先找王思聪谈,他要审核要先预处理下,然后他再给王健林签个字就完了。(仅仅是比喻而已)




但这个方式有个很明显的问题,如果有多个代理人,只能依次调用(按照继承的顺序),如果想改变代理逻辑,则得直接修改类,重新编译。


很明显这个方案的可扩展性不好,灵活性也很差


2.聚合

聚合就是一个对象中包含另外一个对象的引用


它们可以随意组合,顺序可以调整,这个只要在客户端改动就好。如果写在配置文件里面了,直接修改配置文件,客户端都不需要改动。


题外话:

这个设计方案看起来很眼熟,可以发现这种设计和装饰模式一模一样

我个人的理解是,设计模式是一个逻辑上的概念,可能设计上一样,但它们逻辑上是不同的


三、编码方案

那么我们知道如何设计这个类了,那么我们程序中又可以有两种产生这个类和这个类对象的方法

1.静态代理

也就是直接将刚刚说的设计类的方案硬编码,需要什么代理类就写什么代理类

http://www.cnblogs.com/java-my-life/archive/2012/04/23/2466712.html


这个方案的劣势在于什么呢:你要给A,B,C三个类代理,就要分别写三个代理类(因为A.B,C表现出来的方法都不一样),如果分别是时间和日志两种代理。那么就是要写2*3=6个类。

但其实我们用户是想对A,B,C实现一样的代理逻辑,却要为A,B,C都写代理类,其实就有很多重复编码。

重复的东西------>交给程序帮我们实现

因此我们动态的来生成这些代理类,用户只需要指定代理的逻辑(时间和日志)


2.动态代理


package myproxy;

public interface UserService {

	public void addUser();
}

package myproxy;

public class UserServiceImpl implements UserService {

	public void addUser()
	{
		System.out.println("this is add user operation");
	}
}

package myproxy;
import java.lang.reflect.Method;

public interface InvocationHandler {

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

package myproxy;
import java.lang.reflect.Method;

public class TimeHandler implements InvocationHandler{

	private Object target;	
	public TimeHandler(Object  target) {
		this.target=target;
	}

	public void invoke(Object o,Method m) {
		System.out.println("start time");
		try {
			m.invoke(target, new Object[]{});
		} catch (Exception e) {
			e.printStackTrace();
		} 
		System.out.println("end time");
		
	}
}

package myproxy;
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.JavaCompiler.CompilationTask;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

public class Proxy {

	public static Object newProxyInstance(Class intfc, InvocationHandler h) throws Exception
	{
		String rt="\r\n";
		String src="";
		//src+="package myproxy;"+rt+
		src=	  rt+
			  "public class "+intfc.getSimpleName()+"$Proxy implements "+intfc.getName()+"{"+rt+
			  "     private myproxy.InvocationHandler h;"+rt+
			  "     public  "+intfc.getSimpleName()+"$Proxy(myproxy.InvocationHandler h){"+rt+
			  "           this.h=h;"+rt+
			  "     }"+rt;
		
		String methodString="";
		Method[] methods=intfc.getDeclaredMethods();
		for(Method m:methods)
		{
			methodString+="      @Override"+rt+
				          "      public void "+m.getName()+"()"+rt+
				          "      { "+rt+
				          "          try{"+rt+
				          "          java.lang.reflect.Method md="+intfc.getName()+".class.getMethod(\""+m.getName()+"\"); "+rt+
				          "       	  h.invoke(this,md);"+rt+
				          "          }catch(Exception e){}"+
				          "       }"+rt;
		}
		src=src+methodString+"}";
		System.out.println(src);
		
		//将生成的代理类写入文件
		String fileName="d:\\myproxy\\"+intfc.getSimpleName()+"$Proxy.java";
		File f=new File(fileName);
		FileWriter fw=new FileWriter(f);
		fw.write(src);
		fw.flush();
		fw.close();
		
		//在程序中进行编译
		JavaCompiler compiler=ToolProvider.getSystemJavaCompiler();
		StandardJavaFileManager fileMgr=compiler.getStandardFileManager(null, null, null);
		Iterable units=fileMgr.getJavaFileObjects(fileName);
		CompilationTask t=compiler.getTask(null, fileMgr, null, null, null, units);
		t.call();
		fileMgr.close();
		
		
		//加载到内存
		URL[] urls=new URL[]{new URL("file:/d:/myproxy/")};
		URLClassLoader url=new URLClassLoader(urls);
		Class c=url.loadClass(intfc.getSimpleName()+"$Proxy");
		
		Constructor constructor=c.getConstructor(InvocationHandler.class);
		Object o=constructor.newInstance(h);
		return o;
	}
}

package myproxy;

public class Client {

	public static void main(String[] args) throws Exception {
		UserService t=new UserServiceImpl();
		InvocationHandler h=new TimeHandler(t);
		UserService u=(UserService) Proxy.newProxyInstance(UserService.class,h);
		u.addUser();
	}
}

代码参考《马士兵设计模式教学视频》

总结这个代码的结构如下:



客户端需要做的就是按照需求创建Handler,指定代理的逻辑,然后同时将目标对象放到handler中。

然后把这个handler对象传给Proxy工具类,它就会帮我们生成对应的代理类,然后编译(也可能是直接修改Class二进制文件,就不需要编译了),加载到内存这个代理类Class对象,最后生成代理实例,返回给客户端。




四、实际应用

java的jdk自带动态代理的工具包,(上面是我们模拟的jdk里面的InvocationHandler和Proxy)

实际使用如下:

http://blog.csdn.net/hhzxj2008/article/details/24560575

(API的使用可以查看JDK文档或者网上很多用例的)


jdk的reflect包中的Proxy    只能代理有接口的了类

Cglib  可以修改字节码  反编译  生成子类


有两个操作字节码的框架ASM和Javasisst。

Cglib就是利用ASM进行改变字节码然后生成代理类的


Java动态代理机制详解(JDK 和CGLIB,Javassist,ASM)

介绍cglib:http://www.blogjava.net/stone2083/archive/2008/03/16/186615.html

http://blog.csdn.net/fenglibing/article/details/7080340



比较cglib和jdk的代理:http://insufficientinformation.blogspot.com/2007/12/spring-dynamic-proxies-vs-cglib-proxies.html

springAOP多个切面http://howtodoinjava.com/2015/01/30/spring-aop-specifying-aspects-ordering/

猜你喜欢

转载自blog.csdn.net/silviakafka/article/details/46964809