CGLib与JDK的动态代理

一、CGLib 简介

CGLib (Code Generation Library) 是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。Hibernate用它来实现PO字节码的动态生成。CGLib比 Java 的 java.lang.reflect.Proxy 类更强的在于它不仅可以接管接口类的方法,还可以接管普通类的方法。

CGLib 的底层是Java字节码操作框架 —— ASM。CGLib就是封装了ASM,简化了ASM的操作,实现了在运行期动态生成新的class。ASM是直接生成class类的方式,不会有性能问题(相对来说)

二、JDK的动态代理 (VS) CGLib

1、实现原理

JDK的动态代理——根据java的反射机制动态生成

利用反射,获取委托类的类加载器,委托类的所有接口,实例化代理类。通过反射类Proxy以及InvationHandler回调接口实现的。但是动态代理类只能对该类所实现的接口中的方法进行代理。具有一定的局限性,并且反射的效率也不是很高。

Proxy 毕竟是通过反射实现的,必须在效率上付出代价:有实验数据表明,调用反射比一般的函数开销至少要大 10 倍。从程序实现上可以看出,对 proxyclass 的所有方法调用都要通过使用反射的 invoke 方法。因此,对于性能关键的应用,使用 proxy class是需要精心考虑的,以避免反射成为整个应用的瓶颈。

CGLib ——利用ASM生成字节码原理,利用增强类,创建代理类

CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比反射效率要高。但是需要主要注意的,CGLib不能对声明为final的方法进行代理。因为CGLib原理是动态的生成被代理类的子类。

ASM能够通过改造既有类,直接生成需要的代码。增强的代码是硬编码在新生成的类文件内部的,没有反射带来性能上的付出。它是一个普通的 Java 类而不是 proxy类,甚至可以在应用程序的类框架中拥有自己的位置,派生自己的子类。

在调用目标方法的时候,CGLib会回调MethodInterceptor接口方法拦截,来实现你自己的代理逻辑,类似于JDK中的InvocationHandler接口

2、代码实现

JDK动态代理

package DynamicProxy2upgrade;

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

/**
 * @ClassName: DynamicProxy
 * @Description: 动态代理类,在这里做了一个升级,把代理绑定真实类的关系,Proxy生成实例的代码移到了动态代理这里
 * @author 张薄- [email protected]
 * @date 2015年5月29日 下午9:21:19
 */
public class DynamicProxy implements InvocationHandler {

	private Object obj ;  //委托类,真实类

	DynamicProxy(Object obj){
		this.obj=obj;
	}

	//绑定代理与委托类,生成新的代理类
	public Object bindProxy(Object obj){

		//返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序,如ProxyHandler
		return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		return method.invoke(obj, args); //普通的java反射代码,通过反射执行某个类的方法(invoke方法内部实现预处理,对委托类方法调用,事后处理等逻辑。)
	}

}

JDK动态代理运行结果:‘

CGLib动态代理

package CGLibDynamicProxy;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer; //一个字节码增强类,它可以方便的对你想要处理的类进行扩展
import net.sf.cglib.proxy.MethodInterceptor; //方法拦截器
import net.sf.cglib.proxy.MethodProxy; //代理类,JDK的java.long.reflect.Method类的代理类,可以方便的实现对源对象方法的调用(如invokeSuper)
/**
 * @ClassName: DynamicProxybyCGLib
 * @Description: 基于CGLib的动态代理
 * @author 张薄- [email protected]
 * @date 2015年5月31日 上午8:44:19
 */
public class DynamicProxybyCGLib implements MethodInterceptor {

	private Object obj;  //委托类
	public Object getInstance(Object obj){
		this.obj = obj;
		Enhancer enhancer = new Enhancer();  //增强类
		//不同于JDK的动态代理,它不能在创建代理时传obj对 象,obj对象必须被CGLIB包来创建
		enhancer.setSuperclass(this.obj.getClass()); //设置被代理类字节码(obj将被代理类设置成父类;作为产生的代理的父类传进来的),CGLIB根据字节码生成被代理类的子类
		enhancer.setCallback(this);	//设置回调函数,即一个方法拦截
		return enhancer.create(); //创建代理类
	}

	@Override
	public Object intercept(Object obj, Method method, Object[] args,
			MethodProxy methodProxy) throws Throwable {
		System.out.println("obj:" + obj.getClass().getName());  //由CGLib动态生成的代理类实例
		System.out.println("method:" + method.getName());	//上文中实体类所调用的被代理的方法引用
		System.out.println("methodProxy:" + methodProxy.getClass().getName()); //生成的代理类对方法的代理引用
		System.out.println("args:"+ args); //参数值列表

		Object object = methodProxy.invokeSuper(obj, args);  //调用代理类实例上的methodProxy方法的父类方法(即实体类RealSubject中的对应方法)

		return object;
	}

}

CGLib动态代理运行结果:

三、小结

CGLib实现的原理,很具体的原理可能我还是不太懂,但是,大概知道一个过程,首先根据Enhancer创建代理类,如何创建的呢?根据方法setSuperclass将被代理类作为父类引入,再根据CGLib生成被代理类的子类,根据方法setCallback,回调函数(这里我们的动态代理类实现MethodInterceptor拦截器,根据拦截器的方法intercept,调用代理类的父类方法,实现了代理被代理类的方法的目的),实现方法拦截。最后用create方法创建代理类。

既然口述了CGLib的实现原理,那就再来说说JDK的动态代理吧。根据Proxy的方法newProxyInstance方法创建代理类,该方法里面的参数,分别获取了被代理类的类加载器(用于动态加载),被代理类的所有接口类(以便于实现接口类的所有方法),再通过动态代理类实现InvationHandler,实现接口方法invoke,回调函数(通过反射执行被代理类的方法),这样在实例化动态代理类的时候,就真正的创建了动态代理。

啰里啰嗦的说了这么多,收获还是有的,说出我的心得,如果大家有不同的想法,欢迎拍砖哈。比如,CGLib的动态代理类,原理是继承,生成了被代理类的子类;而JDK的动态代理类,原理是反射,生成的代理类,实际不具备被代理类内部的方法,每一次调用都需要利用反射,调用被代理类的方法;再来看静态代理,代理类就是通过调用被代理类的方法进行执行的,而它自己本身并不具体被代理类的方法,JDK的动态代理的进步在于,利用反射,将类的加载延迟到程序运行中,解耦了代理类与被代理的关系。

时间: 2024-08-28 00:18:36

CGLib与JDK的动态代理的相关文章

JDK的动态代理为什么必须要使用接口与使用CGLIB动态代理

一.JDK的动态代理为什么必须要使用接口 JDK的代理Proxy必须要使用接口,才可以实现对方法的拦截.为什么呢?先让我们看一个JDK动态代理的示例: 接口类: public interface IPerson { public void sayHi(String nm); } 接口实现类: public class Person  implements IPerson{ public Person(){//构造 } private String name; public Person(Stri

JDK的动态代理与cglib动态代理

JDK的动态代理与cglib动态代理 两种代理模式: 动态代理:代理类不存在,在程序运行过程中,动态生成代理类和代理类对象,再通过代理对象调用目标对象 静态代理(装饰者模式):代理类是提前创建好的,直接创建代理类对象,再通过代理对象调用目标对象 一.JDK动态代理 jdk的动态代理是基于接口的动态代理,要求目标对象必须实现至少一个接口,核心API是java.lang.reflect.Proxy类的newProxyInstance方法. Object proxy = Proxy.newProxyI

Java--简单的Spring AOP配置以及AOP事物管理,JDK/GCLib动态代理

一.看一下简单的通过XML的AOP配置 1.首先创建一个简单的Student类 public class Student { private Integer age; private String name; public void setAge(Integer age) { this.age = age; } public Integer getAge() { System.out.println("Age : " + age); return age; } public void

Spring的AOP实现使用的JDK的动态代理必须使用接口

今天,在项目中遇到一个问题,情况是这样的:在一个项目中,我配置了一个用以处理任务的工厂类,然后将这个工厂类注入到其他的service类中进行使用.在Spring中的配置如下: <bean id="linkingDetectService" class="cn.vobile.service.linkingdetect.LinkingDetectServiceImpl"> <property name="taskPriority"

使用Jdk实现动态代理

一).创建动态代理的步骤 1.主题接口 2.代理类 3.真实类 4.使用类 1)主体接口中定义了代理类和真实类的的公共接口方法,代理类和真实类分别实现主体接口,真实类实现了接口方法的具体逻辑,代理类也实现了同样的接口方法,在方法中调用真实类的逻辑,相当于拿到了被代理人的授权,执行被代理人拥有的功能. 2)在实用类中,利用java的多态特性,使用公共接口,接收代理类,接收代理类对象,使用代理类调用公共方法,实现真实类的具体逻辑. 二).使用jdk实现动态代理 一.为什么要使用动态代理? 1).原有

基于JDK的动态代理技术详解

虽然对于Spring的基本思想Aop是基于动态代理和CGlib这一点很早就有所认识,但是什么是动态代理却不甚清楚.为了对Spring加深理解,我觉得好好学习一下java的动态代理是非常有必要的. 静态代理 在学习动态代理之前我先花一点时间了解一下静态代理,从静态代理出发了解代理到底是怎么一回事,以及了解静态代理的局限性,进而明白为什么要发展及使用动态代理技术. 相信使用过Spring框架的同学都知道Spring利用Aop完成声明式事务管理以及其他的代理增强,也就是在方法执行前后加上一些譬如时间.

手写实现JDK的动态代理

Person接口 package com.zhoucong.proxy.jdk; public interface Person { // 寻找真爱 void findlove(); } 人物实现类 package com.zhoucong.proxy.jdk; public class Zhangsan implements Person{ @Override public void findlove() { System.out.println("我叫张三,性别女,我找对象的要求如下:\n&

JDK的动态代理

代理的目的就是在你做事之前.之后.过程中...不改变你的行为(代码)加点其他东东 接口: package com.liule.code; public interface Bookinterface { public void addBook(); } 委托类: package com.liule.code; public class Book implements Bookinterface { @Override public void addBook() { System.out.prin

AOP:jdk的动态代理

1.文件结构 2.建立接口 package com.wangcf.manager; public interface IUserManager { public void add(); public void del(); public void update(); public String load(int uid); } 3.实现接口,里面的所有方法都是切入点PointCut package com.wangcf.manager; public class UserManagerImp i