当前位置: 代码迷 >> 综合 >> CGLib
  详细解决方案

CGLib

热度:50   发布时间:2024-01-27 01:16:56.0

1. CGLIB介绍

CGLIB(Code Generation Library)是一个开源项目!是一个强大的,高性能,高质量的Code生成类库,

它可以在运行期扩展Java类与实现Java接口。CGLIB是一个强大的高性能的代码生成包。它广泛的被许多AOP的框架使用,例如Spring AOP为他们提供方法的interception(拦截)。CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。

2. 为什么使用 CGLIB?

CGLIB是一个功能强大,高性能的代码生成它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。

CGLIB代理主要通过对字节码的操作,为对象引入间接级别,以控制对象的访问。我们知道Java中有一个动态代理也是做这个事情的,那我们为什么不直接使用Java动态代理,而要使用CGLIB呢?答案是CGLIB相比于JDK动态代理更加强大JDK动态代理虽然简单易用,但是其有一个致命缺陷是,只能对接口进行代理。如果要代理的类为一个普通类、没有接口,那么Java动态代理就没法使用了。

3. 首先说一下JDK中的动态代理:

JDK中的动态代理是通过反射类Proxy以及InvocationHandler回调接口实现的,但是,JDK中所要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中具有一定的局限性,而且使用反射的效率也并不是很高。

4. 使用CGLib实现动态代理:

使用CGLib实现动态代理,完全不受代理类必须实现接口的限制,而且CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。

5. 使用CGLib实现动态代理

实现一个业务类,注意,这个业务类并没有实现任何接口:   

package com.jpeony.spring.proxy.cglib;public class HelloService {public HelloService() {System.out.println("HelloService构造");}/*** 该方法不能被子类覆盖,Cglib是无法代理final修饰的方法的*/final public String sayOthers(String name) {System.out.println("HelloService:sayOthers>>"+name);return null;}public void sayHello() {System.out.println("HelloService:sayHello");}}

自定义MethodInterceptor:

package com.jpeony.spring.proxy.cglib;import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;/*** 自定义MethodInterceptor*/
public class MyMethodInterceptor implements MethodInterceptor{/*** sub:cglib生成的代理对象* method:被代理对象方法* objects:方法入参* methodProxy: 代理方法*/@Overridepublic Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("======插入前置通知======");Object object = methodProxy.invokeSuper(sub, objects);System.out.println("======插入后者通知======");return object;}
}

生成CGLIB代理对象调用目标方法:

package com.jpeony.spring.proxy.cglib;import net.sf.cglib.core.DebuggingClassWriter;import net.sf.cglib.proxy.Enhancer;public class Client {public static void main(String[] args) {// 代理类class文件存入本地磁盘方便我们反编译查看源码System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");// 通过CGLIB动态代理获取代理对象的过程Enhancer enhancer = new Enhancer();// 设置enhancer对象的父类enhancer.setSuperclass(HelloService.class);// 设置enhancer的回调对象enhancer.setCallback(new MyMethodInterceptor());// 创建代理对象HelloService proxy= (HelloService)enhancer.create();// 通过代理对象调用目标方法proxy.sayHello();}}

Enhancer是一个字节码增强器,用来为非接口类型创建代理它的功能与java自带的Proxy类挺相似的它会根据某个给定的类创建子类并且所有非final的方法都带有回调函数。和Proxy不一样的是,不管是接口还是类他都能正常工作

运行结果:

======插入前置通知======
HelloService:sayHello
======插入后者通知======

6. CGLIB动态代理源码分析

实现CGLIB动态代理必须实现MethodInterceptor(方法拦截器)接口,源码如下:

package net.sf.cglib.proxy;public interface MethodInterceptor extends Callback{/*** All generated proxied methods call this method instead of the original method.* The original method may either be invoked by normal reflection using the Method object,* or by using the MethodProxy (faster).* @param obj "this", the enhanced object* @param method intercepted Method* @param args argument array; primitive types are wrapped* @param proxy used to invoke super (non-intercepted method); may be called* as many times as needed* @throws Throwable any exception may be thrown; if so, super method will not be invoked* @return any value compatible with the signature of the proxied method. Method returning void will ignore this value.* @see MethodProxy*/    public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,MethodProxy proxy) throws Throwable;}

这个接口只有一个intercept()方法,这个方法有4个参数:

1)obj表示增强的对象,即实现这个接口类的一个对象;

2)method表示要被拦截的方法

3)args表示要被拦截方法的参数

4)proxy表示要触发父类的方法对象

在上面的Client代码中,通过Enhancer.create()方法创建代理对象,create()方法的源码:

 /*** Generate a new class if necessary and uses the specified* callbacks (if any) to create a new object instance.* Uses the no-arg constructor of the superclass.* @return a new instance*/public Object create() {classOnly = false;argumentTypes = null;return createHelper();}

该方法含义就是如果有必要就创建一个新类,并且用指定的回调对象创建一个新的对象实例,

使用的父类的参数的构造方法来实例化父类的部分。核心内容在createHelper()中,源码如下:

private Object createHelper() {preValidate();Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,ReflectUtils.getNames(interfaces),filter == ALL_ZERO ? null : new WeakCacheKey<CallbackFilter>(filter),callbackTypes,useFactory,interceptDuringConstruction,serialVersionUID);this.currentKey = key;Object result = super.create(key);return result;}

preValidate()方法校验callbackTypes、filter是否为空,以及为空时的处理。

通过newInstance()方法创建EnhancerKey对象,作为Enhancer父类AbstractClassGenerator.create()方法

创建代理对象的参数。

protected Object create(Object key) {try {ClassLoader loader = getClassLoader();Map<ClassLoader, ClassLoaderData> cache = CACHE;ClassLoaderData data = cache.get(loader);if (data == null) {synchronized (AbstractClassGenerator.class) {cache = CACHE;data = cache.get(loader);if (data == null) {Map<ClassLoader, ClassLoaderData> newCache = new WeakHashMap<ClassLoader, ClassLoaderData>(cache);data = new ClassLoaderData(loader);newCache.put(loader, data);CACHE = newCache;}}}this.key = key;Object obj = data.get(this, getUseCache());if (obj instanceof Class) {return firstInstance((Class) obj);}return nextInstance(obj);} catch (RuntimeException e) {throw e;} catch (Error e) {throw e;} catch (Exception e) {throw new CodeGenerationException(e);}}

真正创建代理对象方法在nextInstance()方法中,该方法为抽象类AbstractClassGenerator的一个方法,签名如下:

abstract protected Object nextInstance(Object instance) throws Exception;

在子类Enhancer中实现,实现源码如下:

protected Object nextInstance(Object instance) {EnhancerFactoryData data = (EnhancerFactoryData) instance;if (classOnly) {return data.generatedClass;}Class[] argumentTypes = this.argumentTypes;Object[] arguments = this.arguments;if (argumentTypes == null) {argumentTypes = Constants.EMPTY_CLASS_ARRAY;arguments = null;}return data.newInstance(argumentTypes, arguments, callbacks);}

看看data.newInstance(argumentTypes, arguments, callbacks)方法,

第一个参数为代理对象的构成器类型,第二个为代理对象构造方法参数,第三个为对应回调对象。

最后根据这些参数,通过反射生成代理对象,源码如下:

 /*** Creates proxy instance for given argument types, and assigns the callbacks.* Ideally, for each proxy class, just one set of argument types should be used,* otherwise it would have to spend time on constructor lookup.* Technically, it is a re-implementation of {@link Enhancer#createUsingReflection(Class)},* with "cache {@link #setThreadCallbacks} and {@link #primaryConstructor}"** @see #createUsingReflection(Class)* @param argumentTypes constructor argument types* @param arguments constructor arguments* @param callbacks callbacks to set for the new instance* @return newly created proxy*/public Object newInstance(Class[] argumentTypes, Object[] arguments, Callback[] callbacks) {setThreadCallbacks(callbacks);try {// Explicit reference equality is added here just in case Arrays.equals does not have oneif (primaryConstructorArgTypes == argumentTypes ||Arrays.equals(primaryConstructorArgTypes, argumentTypes)) {// If we have relevant Constructor instance at hand, just call it// This skips "get constructors" machineryreturn ReflectUtils.newInstance(primaryConstructor, arguments);}// Take a slow path if observing unexpected argument typesreturn ReflectUtils.newInstance(generatedClass, argumentTypes, arguments);} finally {// clear thread callbacks to allow them to be gc'dsetThreadCallbacks(null);}}

最后生成代理对象:

将其反编译后代码如下:

package com.jpeony.spring.proxy.cglib;import java.lang.reflect.Method;import net.sf.cglib.core.ReflectUtils;import net.sf.cglib.core.Signature;import net.sf.cglib.proxy.*;public class HelloService$$EnhancerByCGLIB$$4da4ebaf extends HelloServiceimplements Factory{private boolean CGLIB$BOUND;public static Object CGLIB$FACTORY_DATA;private static final ThreadLocal CGLIB$THREAD_CALLBACKS;private static final Callback CGLIB$STATIC_CALLBACKS[];private MethodInterceptor CGLIB$CALLBACK_0; // 拦截器private static Object CGLIB$CALLBACK_FILTER;private static final Method CGLIB$sayHello$0$Method; // 被代理方法private static final MethodProxy CGLIB$sayHello$0$Proxy; // 代理方法private static final Object CGLIB$emptyArgs[];private static final Method CGLIB$equals$1$Method;private static final MethodProxy CGLIB$equals$1$Proxy;private static final Method CGLIB$toString$2$Method;private static final MethodProxy CGLIB$toString$2$Proxy;private static final Method CGLIB$hashCode$3$Method;private static final MethodProxy CGLIB$hashCode$3$Proxy;private static final Method CGLIB$clone$4$Method;private static final MethodProxy CGLIB$clone$4$Proxy;static void CGLIB$STATICHOOK1(){Method amethod[];Method amethod1[];CGLIB$THREAD_CALLBACKS = new ThreadLocal();CGLIB$emptyArgs = new Object[0];// 代理类Class class1 = Class.forName("com.jpeony.spring.proxy.cglib.HelloService$$EnhancerByCGLIB$$4da4ebaf");// 被代理类Class class2;amethod = ReflectUtils.findMethods(new String[] {"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (class2 = Class.forName("java.lang.Object")).getDeclaredMethods());Method[]  = amethod;CGLIB$equals$1$Method = amethod[0];CGLIB$equals$1$Proxy = MethodProxy.create(class2, class1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");CGLIB$toString$2$Method = amethod[1];CGLIB$toString$2$Proxy = MethodProxy.create(class2, class1, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");CGLIB$hashCode$3$Method = amethod[2];CGLIB$hashCode$3$Proxy = MethodProxy.create(class2, class1, "()I", "hashCode", "CGLIB$hashCode$3");CGLIB$clone$4$Method = amethod[3];CGLIB$clone$4$Proxy = MethodProxy.create(class2, class1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");amethod1 = ReflectUtils.findMethods(new String[] {"sayHello", "()V"}, (class2 = Class.forName("com.jpeony.spring.proxy.cglib.HelloService")).getDeclaredMethods());Method[] 1 = amethod1;CGLIB$sayHello$0$Method = amethod1[0];CGLIB$sayHello$0$Proxy = MethodProxy.create(class2, class1, "()V", "sayHello", "CGLIB$sayHello$0");}final void CGLIB$sayHello$0(){super.sayHello();}public final void sayHello(){MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;if(this.CGLIB$CALLBACK_0 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_0;}if(var10000 != null) {// 调用拦截器var10000.intercept(this, CGLIB$setPerson$0$Method, CGLIB$emptyArgs, CGLIB$setPerson$0$Proxy);} else {super.sayHello();}}............}

从代理对象反编译源码可以知道,代理对象继承于HelloService拦截器调用intercept()方法intercept()方法由自定义MyMethodInterceptor实现,所以,最后调用MyMethodInterceptor中的intercept()方法,从而完成了由代理对象访问到目标对象的动态代理实现。

JDK动态代理实现原理(jdk8):https://blog.csdn.net/yhl_jxy/article/details/80586785

转载自: https://blog.csdn.net/yhl_jxy/article/details/80633194