深入剖析动态代理

动态代理是指在运行时,动态生成代理类。代理类的字节码将在运行时生成并载入当前的ClassLoader.

生成动态代理类的方法很多,如JDK自带的动态代理、CGLIB、Javassist或者ASM库。

JDK动态代理使用简单,它内置在JDK中,因此不需要引入第三方Jar包,但相对功能比较弱。CGLIB和Javassist都是高级的字节码生成库,总体性能比JDK自带的动态代理好,而且功能十分强大。ASM是低级的字节码生成工具,使用ASM已经近乎在于使用Javabytecode编程,对开发人员要求较高,也是性能最好的一种动态代理生辰工具。但ASM的使用是在过于繁琐,而且性能也没有数量级的提升,与CGLIB等高级字节码生成工具相比,ASM程序的可维护性也较差。

JDK实现

1、步骤

1)通过实现InvocationHandler接口创建自己的调用处理器

2)通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理类

3)通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型

4)通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入

2、创建代理

  //InvocationHandlerImpl 实现了InvocationHandler接口,并能实现方法调用从代理类到委托类的分派转发
	//其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用
	InvocationHandler handler = new InvocaitonHandlerImpl(..);

	//通过Proxy为包括Interface接口在内的一组接口动态创建代理类的对象
	Class clazz = Proxy.getProxyClass(classLoader,new Class[]{Interface.class,...});

	//通过反射从生成的类对象获得构造函数对象
        Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});

	//通过构造函数对象创建动态代理类实例
	Interface Proxy = (Interface)constructor.newInstance(new Object[]{handler});

	//Proxy类的静态方法newProxyInstance对上面具体步骤的后三步做了封装,简化了动态代理对象的获取过程。
	//InvocationHandlerImpl实现了InvocaitonHandler接口,并能实现方法调用从代理类到委托类的分派转发
	InvocaitonHandler handler = new InvocationHandlerImpl(..);
	//通过Proxy直接创建动态代理类实例

Interface proxy = (Interface)Proxy.newProxyInstance(classLoader,new Class[]{Interface.class},handler);

3、代码

/**
 * 接口
 * @author Emily
 *
 */
public interface IDBQuery {

	String request();
}

/**
 * 真实的实现类,具体的目标对象
 * @author Emily
 *
 */
public class DBQuery implements IDBQuery {

	public DBQuery(){
		try {
			Thread.sleep(1000); //可能包含数据库连接等耗时操作
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	@Override
	public String request() {

		return "request string";
	}

}

/**
 * JDK动态代理的实现类
 * @author Emily
 *
 */
public class JdkDbQueryHandler implements InvocationHandler{

	IDBQuery real = null; //主题接口

	/**
	 * 生成Handler
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		if(real == null)
			real = new DBQuery(); //如果是第一次调用,则生成真实对象
		return real.request();  //使用真实主题完成实际的操作
	}

	/**
	 * 利用Handler生成动态代理对象
	 * @return
	 */
	public static IDBQuery createJdkProxy(){

		//根据指定的类加载器和接口以及截获器,返回代理类的一个实例对象
		//ClassLoader loader :指定被代理对象的类加载器
		//Class[] Interfaces :指定被代理对象所以事项的接口
		//InvocationHandler h :指定需要调用的InvocationHandler对象
		IDBQuery jdkProxy = (IDBQuery) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{IDBQuery.class}, new JdkDbQueryHandler());
		return jdkProxy;
	}

}

CGLIB实现

package com.ltt.dynamic;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
 * CGLIB动态代理
 * @author Emily
 *
 */
public class CglibDbQueryInterceptor implements MethodInterceptor{
	IDBQuery real = null;

	/**
	 * 处理代理逻辑的切入类
	 */
	@Override
	public Object intercept(Object arg0, Method arg1, Object[] arg2,
			MethodProxy arg3) throws Throwable {
		if (real == null) {    //代理类 的内部逻辑
			real = new DBQuery();
			return real.request();
		}
		return null;
	}

	/**
	 * 生成动态代理
	 * @return
	 */
	public static IDBQuery createCglibProxy(){
		Enhancer enhancer = new Enhancer();
		//指定切入器,定义代理类逻辑
		enhancer.setCallback(new CglibDbQueryInterceptor());
		//指定实现的接口
		enhancer.setInterfaces(new Class[]{IDBQuery.class});

		IDBQuery cglibProxy = (IDBQuery) enhancer.create();
		return cglibProxy;
	}

}

Javassist实现

一种是使用代理工厂创建,另一种通过使用动态代码创建。使用代理工厂创建时,方法与CGLIB类似,也需要实现一个用于代理逻辑处理的Handler:例如createJavassistDynProxy();使用动态代码创建,生成字节码,这种方式可以非常灵活,甚至可以在运行时生成业务逻辑,如createJavassistBytecodeDynamicProxy()方法。

/**
 * Javassist动态代理
 * @author Emily
 *
 */
public class JavassistDynDbQueryHandler implements MethodHandler{

	IDBQuery real = null;

	/*
	 * (non-Javadoc) 实现用于代理逻辑处理的Handler
	 * @see javassist.util.proxy.MethodHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.reflect.Method, java.lang.Object[])
	 */
	@Override
	public Object invoke(Object arg0, Method arg1, Method arg2, Object[] arg3)
			throws Throwable {
		if (real == null)
			real = new DBQuery();
			return real.request();

	}

	/**
	 * 创建动态代理
	 * @return
	 * @throws Exception
	 */
	public static IDBQuery createJavassistDynProxy() throws Exception{
		ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.setInterfaces(new Class[]{IDBQuery.class});  //指定接口
		Class proxyClass = proxyFactory.createClass();
		IDBQuery javassistProxy = (IDBQuery) proxyClass.newInstance(); //设置Handler处理器
		((ProxyObject) javassistProxy).setHandler(new JavassistDynDbQueryHandler());
		return javassistProxy;
	}

	/**
	 * 运行时生成业务逻辑
	 * @return
	 * @throws Exception
	 */
	public static IDBQuery createJavassistBytecodeDynamicProxy() throws Exception{
		ClassPool mPool = new ClassPool(true);
		//定义类名
		CtClass mCtc = mPool.makeClass(IDBQuery.class.getName() + "JavaassistBytecodeProxy");
		//需要实现接口
		mCtc.addInterface(mPool.get(IDBQuery.class.getName()));
		//添加构造函数
		mCtc.addConstructor(CtNewConstructor.defaultConstructor(mCtc));

		//添加类的字段信息,使用动态Java代码
		mCtc.addField(CtField.make("public" + IDBQuery.class.getName() + "real;", mCtc));
		String dbQueryname = DBQuery.class.getName();
		//添加方法,这里使用动态Java代码指定内部逻辑
		mCtc.addMethod(CtNewMethod.make("public String request() { if(real==null) real = new " +dbQueryname+"(); return real.request();}", mCtc));

		//基于以上信息,生成动态类
		Class pc = mCtc.toClass();
		//生成动态类的实例
		IDBQuery bytecodeProxy = (IDBQuery) pc.newInstance();
		return bytecodeProxy;
	}

}

三种动态代理:JDK动态代理创建速度优于CGLIB动态代理,但是在函数的调用性能上远不如CGLIB和Javassist。故CGLIB和Javassist整体性能上比JDK动态代理好。

动态代理 VS 静态代理

静态代理

1)代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。

2)如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

动态代理

1)动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定的。

2)与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。

3)动态代理可以很大幅度的减少代码行数,并提升系统的灵活性。

时间: 2024-08-08 09:42:02

深入剖析动态代理的相关文章

Java反射机制剖析(四)-深度剖析动态代理原理及总结

动态代理类原理(示例代码参见java反射机制剖析(三)) a)  理解上面的动态代理示例流程 a)  理解上面的动态代理示例流程 b)  代理接口实现类源代码剖析 咱们一起来剖析一下代理实现类($Proxy0)的源代码和整个动态代理的流程. $Proxy0生成的代码如下: import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; impo

深入剖析动态代理(上)之代理的方式

关于动态代理,大家显式使用的可能比較少,可是说到Spring的Interceptor.各种各样的事务管理,大家会更熟悉一些,没错,这些在底层实现上,都是使用的动态代理,确切的说,想要为一个类的方法,动态加入功能,比方验证.资源释放.日志处理等,大部分都是借助动态代理. 为了平缓的过渡,先来说一下静态代理. 静态代理 静态代理的思路非常easy:把一个真实对象的实例放到代理对象的实例中.然后调用代理对象方法,代理对象的方法调用真实对象的方法,以事务管理为例.例如以下: UserDao packag

Java深入浅出系列(四)——深入剖析动态代理--从静态代理到动态代理的演化

静态代理 如上图,在程序执行之前.程序猿就要编写Proxy.然后进行编译,即在程序执行之前,代理类的字节码文件就已经生成了(Proxy类的class文件已经存在了). 静态代理尽管在增强现有的接口业务功能方面有非常大长处,可是大量使用这样的静态代理,会使系统内的类的规模大量增大,不易维护.而且Proxy类和RealSubject类功能本质上是一样的.仅仅只是Proxy起到了一个中介的作用,这样的代理在系统中的存在导致了系统结构的臃肿和松散. 为了解决问题.产生了动态代理.动态代理是在系统执行中,

深入剖析动态代理--从静态代理到动态代理的演化

静态代理 如上图,在程序运行之前,程序员就要编写Proxy,然后进行编译,即在程序运行之前,代理类的字节码文件就已经生成了(Proxy类的class文件已经存在了). 静态代理虽然在增强现有的接口业务功能方面有很大优点,但是大量使用这种静态代理,会使系统内的类的规模大量增大,不易维护:并且Proxy类和RealSubject类功能本质上是一样的,只不过Proxy起到了一个中介的作用,这种代理在系统中的存在导致了系统结构的臃肿和松散. 为了解决这个问题,产生了动态代理.动态代理是在系统运行中,在需

设计模式-深入剖析动态代理模式(3)内部运作机制-通俗代码版

public interface Subject { //业务操作 public void doSomething(String abc); } ----- public class RealSubject implements Subject { //业务操作 public void doSomething(String str) { System.out.println("do something!---->" + str); } } ---- public class My

Java中动态代理实现原理深究

一.前言 笔者平时开发使用"动态代理"不多,最近在看设计模式的时候,"动态代理"又在面前晃了几次,所以这次想从源码的角度去分析动态代理的实现原理,以窥探其精妙~ 二.正文 2.1 静态代理  本文源码基于:jdk1.6.0_33 在正式剖析动态代理的源码之前,我们可以先来看看"静态代理"(就是我们普通的代理模式)的UML图: 从上图可以看出,代理类"ProxySubject"和被代理类"RealSubject&quo

深度剖析JDK动态代理机制

摘要 相比于静态代理,动态代理避免了开发人员编写各个繁锁的静态代理类,只需简单地指定一组接口及目标类对象就能动态的获得代理对象. 代理模式 使用代理模式必须要让代理类和目标类实现相同的接口,客户端通过代理类来调用目标方法,代理类会将所有的方法调用分派到目标对象上反射执行,还可以在分派过程中添加"前置通知"和后置处理(如在调用目标方法前校验权限,在调用完目标方法后打印日志等)等功能. 使用动态代理的五大步骤 1.通过实现InvocationHandler接口来自定义自己的Invocati

Java中的静态代理、通用动态代理类以及原理剖析

代理模式和静态代理 在开发中,代理模式是常用的模式之一,一般来说我们使用的代理模式基本上都是静态代理,实现模式大致如下 : 我们以网络代理为例,简单演示一下静态代理的实现 : // 网络接口 interface Network { public void surfTheInternet(); public void gotoFacebook(); } // 普通网络 class CommonNetwork implements Network { @Override public void su

Java反射机制剖析(三)-简单谈谈动态代理

通过Java反射机制剖析(一)和Java反射机制剖析(二)的学习,已经对反射有了一定的了解,这一篇通过动态代理的例子来进一步学习反射机制. 1.     代理模式 代理模式就是为其他对象提供一种代理来控制对这个对象的访问.其实代理模式是在访问的对象时引入一定程度的间接性,这种间接性可以附加多种用途. 它 的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会 存在关联关系,一个代理类的对象与一个委托类的对象