CGLIB動態代理實現原理


一 CGLIB介紹


CGLIB(Code Generation Library)是一個開源項目!是一個強大的,高性能,高質量的Code生成類庫,

它可以在運行期擴展Java類與實現Java接口。Hibernate用它來實現PO(Persistent Object 持久化對象)字節碼的動態生成。

CGLIB是一個強大的高性能的代碼生成包。它廣泛的被許多AOP的框架使用,例如Spring AOP為他們提供

方法的interception(攔截)。CGLIB包的底層是通過使用一個小而快的字節碼處理框架ASM,來轉換字節碼並生成新的類。

除了CGLIB包,腳本語言例如Groovy和BeanShell,也是使用ASM來生成java的字節碼。當然不鼓勵直接使用ASM,

因為它要求你必須對JVM內部結構包括class文件的格式和指令集都很熟悉。

二 CGLIB動態代理實例


實現一個業務類,注意,這個業務類並沒有實現任何接口:

package com.lanhuigu.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.proxy.cglib; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * @author pypua * @date 2019年8月14日 上午10:57:56 * */
public class MyMethodInterceptor implements MethodInterceptor{ //疑問? //好像並沒有持有被代理對象的引用
        public Object getInstance(Class clazz) throws Exception{ Enhancer enhancer = new Enhancer(); //把父類設置為誰? //這一步就是告訴cglib,生成的子類需要繼承哪個類
 enhancer.setSuperclass(clazz); //設置回調
            enhancer.setCallback(this); //第一步、生成源代碼 //第二步、編譯成class文件 //第三步、加載到JVM中,並返回被代理對象
            return enhancer.create(); } /** * sub:cglib生成的代理對象 * method:被代理對象方法 * objects:方法入參 * methodProxy: 代理方法 */ @Override public 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.lanhuigu.spring.proxy.cglib;

 

package com.proxy.cglib; /** * @author pypua * @date 2019年8月14日 上午11:02:51 * */
public class Client { public static void main(String[] args) { //JDK的動態代理是通過接口來進行強制轉換的 //生成以后的代理對象,可以強制轉換為接口 //CGLib的動態代理是通過生成一個被代理對象的子類,然后重寫父類的方法 //生成以后的對象,可以強制轉換為被代理對象(也就是用自己寫的類) //子類引用賦值給父類
    try { HelloService obj = (HelloService)new MyMethodInterceptor().getInstance(HelloService.class); obj.sayHello(); obj.sayOthers("sfsdfsd"); } catch (Exception e) { e.printStackTrace(); } } }

 


運行結果:

三 CGLIB動態代理源碼分析

實現CGLIB動態代理必須實現MethodInterceptor(方法攔截器)接口,源碼如下:

/* * Copyright 2002,2003 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */
package net.sf.cglib.proxy; /** * General-purpose {@link Enhancer} callback which provides for "around advice". * @author Juozas Baliuka <a href="mailto:baliuka@mwm.lt">baliuka@mwm.lt</a> * @version $Id: MethodInterceptor.java,v 1.8 2004/06/24 21:15:20 herbyderby Exp $ */
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 one
                if (primaryConstructorArgTypes == argumentTypes || Arrays.equals(primaryConstructorArgTypes, argumentTypes)) { // If we have relevant Constructor instance at hand, just call it // This skips "get constructors" machinery
                    return ReflectUtils.newInstance(primaryConstructor, arguments); } // Take a slow path if observing unexpected argument types
                return ReflectUtils.newInstance(generatedClass, argumentTypes, arguments); } finally { // clear thread callbacks to allow them to be gc'd
                setThreadCallbacks(null); } }

最后生成代理對象:

 

將其反編譯后代碼如下:

package com.lanhuigu.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 HelloService implements 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.lanhuigu.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.lanhuigu.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()方法,從而完成了由代理對象訪問到目標對象的動態代理實現。

原文鏈接:https://blog.csdn.net/yhl_jxy/article/details/80633194


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM