代理模式(动态代理底层实现探索)

一、动态代理

代理类在程序运行时创建的代理方式被称为动态代理。

      一般有两种实现的方式:
jdk实现特点:由java内部的反射机制来实现(反射机制生成类比较高效
cglib实现特点:借助asm实现(asm在生成类之后的相关执行过程较高效

二、jdk实现示例

动态代理就是根据被代理的接口生成所有的方法,也就是说给定一个接口,动态代理会宣称“我已经实现该接口下的所有方法了”,但是,你只能查到一个$proxy0的名称。

1、先定义被代理的接口

public interface Work {
	public void work();
}

2、被代理类(真实角色)---角色定义参考代理模式(静态代理)

public class HonestWork implements Work {

	@Override
	public void work() {
		System.out.println("~~~~~~~~~~~~~~这里放着的可是具体实现(乃核心机密)~~~~~~~~~~~~");
	}

}
3、动态代理类(实现InvocationHandler接口)-----位于代理类与委托类之间的中介类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @author Ashes
 * @Description 
 * <p>所谓的动态代理是这样一种class</p>
 * <p>它是运行时生成的class</p>
 * <p>该class需要实现一组interface</p>
 * <p>使用动态代理的时候,必须实现InvocationHandler接口</p>
 * 2017年4月27日
 */	
public class StudentHandler implements InvocationHandler {
	private Object target;
	
	public StudentHandler(Object target) {
		super();
		this.target = target;
	}

	/* (non-Javadoc)
	 * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
	 * 参数:
	 *  proxy 被代理对象
	 *  method 被代理对象方法
	 *  args方法的参数
	 *  
	 *  返回值: 
	 *  Object 方法的返回值
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		
		System.out.println("~~~~~~~~~~~~~~~~~~我负责拿下项目,我是学生,有资格做这份工作~~~~~~~~~~~~~~~");		
		
		method.invoke(target);
		
		System.out.println("~~~~~~~~~~~~~~~~我无情的使用了你的核心机密,现在来做点善后工作~~~~~~~~~~~~~~~~~~~~~");
		return null;
	}

}
4、场景测试
/**
 * @author Ashes
 * @Description 所谓场景就是为了测试,老师要来收报告了
 * 所谓的增强其实是为了做功能的叠加
 * 2017年6月23日
 */
public class TeacherClient {
	
	public static void main(String[] args) {
		
		HonestWork w = new HonestWork();   //首先我们要有一个老实人给我们写核心机密
		Class<?> cls = w.getClass();
		InvocationHandler ih = new StudentHandler(w);
		
		/*
		 * loader - 定义代理类的类加载器
		 * interfaces - 代理类要实现的接口列表
		 * h - 指派方法调用的调用处理程序 
		 * return 一个带有代理类的指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口
		 * 返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在接口中声明过的方法)
		 * */
		Work m = (Work) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), ih);
		
		System.out.println("cls.getClassLoader()  ===>  " + cls.getClassLoader());
		System.out.println("cls.getInterfaces()  ===>   " + cls.getInterfaces());
		//未指定代理类的非限定名称。但是,以字符串 "$Proxy" 开头的类名空间应该为代理类保留。
		System.out.println("代理类的名字 ===> " + m.getClass().getName());  //com.sun.proxy.$Proxy0
		m.work(); //现在调用的是动态产生的类$Proxy0
	}
	
}
结果是什么呢??
cls.getClassLoader()  ===>  sun.misc.Launcher$AppClassLoader@3e25a5
cls.getInterfaces()  ===>   [Ljava.lang.Class;@156ee8e
代理类的名字 ===> $Proxy0
~~~~~~~~~~~~~~~~~~我负责拿下项目,我是学生,有资格做这份工作~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~这里放着的可是具体实现(乃核心机密)~~~~~~~~~~~~
~~~~~~~~~~~~~~~~我无情的使用了你的核心机密,现在来做点善后工作~~~~~~~~~~~~~~~~~~~~~

三、jdk实现原理---怎么感受$proxy0的存在

1、动态代理实现步骤:

a、创建一个实现接口InvocationHandler的类,它必须实现invoke方法
b、创建被代理的类以及接口
c、调用Proxy的静态方法,创建一个代理类
public static Object newProxyInstance(ClassLoader loader,
                                         
Class<?>[] interfaces,
                                          InvocationHandler h)
d、通过代理调用方法

2、模拟JDK代理的内部实现思路分析: 

     实现功能:通过Proxy的newProxyInstance返回代理对象
     1、声明一段源码(动态产生代理)
     2、编译源码(JDK Compiler API),产生新的类(代理类)
     3、将这个类load到内存中,产生一个新的对象(代理对象)
     4、return 代理对象 
     
不能在运行时定义这些方法的新代码。而是要提供一个调用处理器(invocationhandler)。调用处理器是实现了invocationhandler的类对象,在这个接口中只有一个方法:public void invoke(Object o, Method m);


3、代码示例

public interface Work {
	public void work();
}

public interface InvocationHandler {
	/**
	 * @param o 被代理对象
	 * @param m 被代理对象方法
	 * 对某个对象的方法进行处理
	 */
	public void invoke(Object o, Method m);
}

/**
 * @author Ashes
 * @Description 老实的工作者,只想着完成实际功能
 * 2017年6月23日
 */
public class HonestWork implements Work {

	@Override
	public void work() {
		System.out.println("~~~~~~~~~~~~~~这里放着的可是具体实现(乃核心机密)~~~~~~~~~~~~");
	}

}

/**
 * @author Ashes
 * @Description 事务处理器
 * 2017年6月30日
 */
public class StudentHandler implements InvocationHandler {
	
	private Object target;
	
	public StudentHandler(Object target) {
		this.target = target;
	}
	
	@Override
	public void invoke(Object o, Method m) {
		try {
			
			System.out.println("~~~~~~~~~~~~~~~~~~我负责拿下项目,我是学生,有资格做这份工作~~~~~~~~~~~~~~~");

			m.invoke(target);
			
			System.out.println("~~~~~~~~~~~~~~~~我无情的使用了你的核心机密,现在来做点善后工作~~~~~~~~~~~~~~~~~~~~~ ");
			
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

a、场景如何调用实现

public class TeacherClient {

	public static void main(String[] args) {
		try {
			HonestWork hon = new HonestWork();
			InvocationHandler h = new StudentHandler(hon);
			
			//只能是接口
			Work w = (Work) Proxy.newProxyInstance(Work.class, h);
			w.work();

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

b、那我们的$proxy0到底是如何生成的呢???

public class Proxy {
	public static Object newProxyInstance(Class infc, InvocationHandler h) throws Exception {
		String rt = "\r\n";
		String methodstr = "";

		//我要实现该接口下的所有方法
		for(Method m : infc.getMethods()) {

			methodstr += "@Override" + rt +
					"public void " + m.getName() + "() {" + rt +

					"try {" + rt +
					"Method md = " + infc.getName() + ".class.getMethod(\""
					+ m.getName() + "\");" + rt +
					"h.invoke(this, md);" + rt +
					"}catch(Exception e) {e.printStackTrace();}" + rt +
					"}";
		}

		String str = 
				"package com.ashes.myselfproxy2;" + rt +
				"import com.ashes.myselfproxy2.InvocationHandler;" + rt +
				"import java.lang.reflect.Method;" + rt +

				"public class $Proxy0 implements " + infc.getName() + " {" + rt +

				"private InvocationHandler h;" + rt +

				"public $Proxy0(InvocationHandler h) {" + rt +
				"super();" + rt +
				"	this.h = h;" + rt +
				"}" + rt +
				methodstr + rt +
				"}";

		//2、产生代理类的java文件
		//System.getProperty确定当前的系统属性。 
		String filename = System.getProperty("user.dir") + "/bin/com/ashes/myselfproxy2/$Proxy0.java";
		System.out.println(filename); //E:\DesignPatterns\ProxyMode
		File file = new File(filename);

		FileUtils.write(file, str);


		//3、编译
		//拿到编译器
		JavaCompiler compile = ToolProvider.getSystemJavaCompiler();
		//文件管理者
		StandardJavaFileManager fileMgr = compile.getStandardFileManager(null, null, null);
		//获取文件
		Iterable units = fileMgr.getJavaFileObjects(file);
		//编译任务
		CompilationTask t = compile.getTask(null, fileMgr, null, null, null, units);
		//进行编译
		t.call();
		try {
			fileMgr.close();  //关闭资源
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		//load到内存
		ClassLoader cl = ClassLoader.getSystemClassLoader();
		Class c = cl.loadClass("com.ashes.myselfproxy2.$Proxy0");
		System.out.println(c.getName());

		//产生代理类对象并返回
		/*Constructor ctr = c.getConstructor(infc);
		return ctr.newInstance(new Car());*/
		//Constructor 提供关于类的单个构造方法的信息以及对它的访问权限。
		Constructor ctr = c.getConstructor(InvocationHandler.class);
		return ctr.newInstance(h);
	
		//return null;
	}
}

代理类是在程序运行过程中创建的,一旦被创建就变成了常规类,与虚拟机中的任何其他类没有什么区别

四、cglib实现

Cglib是一个优秀的动态代理框架,它的底层使用ASM在内存中动态的生成被代理类的子类
和jdk实现的第一个不同就是我们需要导包


public class Work {
	public void work() {
		System.out.println("我要学习,吼吼吼.......");
	}
}

public class CglibProxy implements MethodInterceptor {
	
	private Enhancer enhancer = new Enhancer();
	
	public Object getProxy(Class clazz) {
		//创建子类的类
		enhancer.setSuperclass(clazz);
		//设置回调
		enhancer.setCallback(this);
		
		return enhancer.create();//创建子类实例并返回
	}

	/* (non-Javadoc)
	 * @see net.sf.cglib.proxy.MethodInterceptor#intercept(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], net.sf.cglib.proxy.MethodProxy)
	 * 拦截所有目标方法的调用
	 * obj 目标类的实例
	 * m 目标方法的反射对象
	 * args 方法的参数
	 * proxy 代理类的实例
	 */
	@Override
	public Object intercept(Object obj, Method m, Object[] args, MethodProxy proxy) 
							throws Throwable {
		System.out.println("日志开始.......");
		
		//代理类调用父类的方法
		proxy.invokeSuper(obj, args);
		
		System.out.println("日志结束.....");
		return null;
	}

}

public class Client {
	public static void main(String[] args) {
		
		CglibProxy cp = new CglibProxy();
		Work t = (Work) cp.getProxy(Work.class);
		
		t.work();
	}
}


猜你喜欢

转载自blog.csdn.net/Ashes18/article/details/78140702