1. 程式人生 > 實用技巧 >spring-AOP實現原理(二)

spring-AOP實現原理(二)

參考:

https://www.cnblogs.com/pypua/p/11351772.html

https://blog.csdn.net/yhl_jxy/article/details/80633194

https://www.cnblogs.com/liuling/archive/2013/05/25/asm.html

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動態代理例項

使用JDK建立動態代理有一個限制, 即它只能為介面建立代理例項. 對於沒有定義介面的業務方法的類, 使用CDGlib 進行動態代理.

CGLib是一個強大的, 高效能的程式碼生成庫. 被廣泛應用於 AOP 框架. 用以提供方法攔截操作.

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:[email protected]">[email protected]</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()方法,從而完成了由代理物件訪問到目標物件的動態代理實現。

JDK動態代理和 CGLib 動態代理的特點:

JDK動態代理: (1)代理類繼承 Proxy 類, 並且實現委託類介面, 主要通過代理類呼叫 InvocationHandler 實現類的重寫方法 invoke() 來實現動態代理.

(2)只能對介面進行代理. (只能對實現介面的委託類進行代理)

(3)底層使用反射機制進行方法掉呼叫.

CGLib動態代理: (1)代理類繼承了委託類, 在代理方法中, 會判斷是否存在實現了 MethodInterceptor 介面的物件, 若存在則呼叫物件的 invoke() 方法, 對委託方法進行代理.

(2)不能對 final 類以及 final , private方法進行代理.

(3)底層將方法全部放入一個數組中, 通過索引直接進行方法呼叫.

AOP(這裡的AOP指的是面向切面程式設計思想,而不是Spring AOP)主要的的實現技術主要有Spring AOP和AspectJ。

1、AspectJ的底層技術。

AspectJ的底層技術是靜態代理,即用一種AspectJ支援的特定語言編寫切面,通過一個命令來編譯,生成一個新的代理類,該代理類增強了業務類,這是在編譯時增強,相對於下面說的執行時增強,編譯時增強的效能更好。

2、Spring AOP

Spring AOP採用的是動態代理,在執行期間對業務方法進行增強,所以不會生成新類,對於動態代理技術,Spring AOP提供了對JDK動態代理的支援以及CGLib的支援。

JDK動態代理只能為介面建立動態代理例項,而不能對類建立動態代理。需要獲得被目標類的介面資訊(應用Java的反射技術),生成一個實現了代理介面的動態代理類(位元組碼),再通過反射機制獲得動態代理類的建構函式,利用建構函式生成動態代理類的例項物件,在呼叫具體方法前呼叫invokeHandler方法來處理。

CGLib動態代理需要依賴asm包,把被代理物件類的class檔案載入進來,修改其位元組碼生成子類。

但是Spring AOP基於註解配置的情況下,需要依賴於AspectJ包的標準註解,但是不需要額外的編譯以及AspectJ的織入器,而基於XML配置不需要。

java位元組碼框架ASM的學習

  一、什麼是ASM

  ASM是一個java位元組碼操縱框架,它能被用來動態生成類或者增強既有類的功能。ASM 可以直接產生二進位制 class檔案,也可以在類被載入入 Java 虛擬機器之前動態改變類行為。Java class 被儲存在嚴格格式定義的 .class檔案裡,這些類檔案擁有足夠的元資料來解析類中的所有元素:類名稱、方法、屬性以及 Java 位元組碼(指令)。ASM從類檔案中讀入資訊後,能夠改變類行為,分析類資訊,甚至能夠根據使用者要求生成新類。

  使用ASM框架需要匯入asm的jar包,下載連結:asm-3.2.jar

  二、如何使用ASM

  ASM框架中的核心類有以下幾個:

  ① ClassReader:該類用來解析編譯過的class位元組碼檔案。

  ② ClassWriter:該類用來重新構建編譯後的類,比如說修改類名、屬性以及方法,甚至可以生成新的類的位元組碼檔案。

  ③ ClassAdapter:該類也實現了ClassVisitor介面,它將對它的方法呼叫委託給另一個ClassVisitor物件。

示例1.通過asm生成類的位元組碼

 1 package com.asm3;
 2 
 3 import java.io.File;
 4 import java.io.FileNotFoundException;
 5 import java.io.FileOutputStream;
 6 import java.io.IOException;
 7 
 8 import org.objectweb.asm.ClassWriter;
 9 import org.objectweb.asm.Opcodes;
10 
11 /**
12  * 通過asm生成類的位元組碼
13  * @author Administrator
14  *
15  */
16 public class GeneratorClass {
17 
18     public static void main(String[] args) throws IOException {
19         //生成一個類只需要ClassWriter元件即可
20         ClassWriter cw = new ClassWriter(0);
21         //通過visit方法確定類的頭部資訊
22         cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC+Opcodes.ACC_ABSTRACT+Opcodes.ACC_INTERFACE,
23                 "com/asm3/Comparable", null, "java/lang/Object", new String[]{"com/asm3/Mesurable"});
24         //定義類的屬性
25         cw.visitField(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL+Opcodes.ACC_STATIC,
26                 "LESS", "I", null, new Integer(-1)).visitEnd();
27         cw.visitField(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL+Opcodes.ACC_STATIC,
28                 "EQUAL", "I", null, new Integer(0)).visitEnd();
29         cw.visitField(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL+Opcodes.ACC_STATIC,
30                 "GREATER", "I", null, new Integer(1)).visitEnd();
31         //定義類的方法
32         cw.visitMethod(Opcodes.ACC_PUBLIC+Opcodes.ACC_ABSTRACT, "compareTo",
33                 "(Ljava/lang/Object;)I", null, null).visitEnd();
34         cw.visitEnd(); //使cw類已經完成
35         //將cw轉換成位元組陣列寫到檔案裡面去
36         byte[] data = cw.toByteArray();
37         File file = new File("D://Comparable.class");
38         FileOutputStream fout = new FileOutputStream(file);
39         fout.write(data);
40         fout.close();
41     }
42 }

  生成一個類的位元組碼檔案只需要用到ClassWriter類即可,生成Comparable.class後用javap指令對其進行反編譯:javap -c Comparable.class >test.txt,編譯後的結果如下:

1 public interface com.asm3.Comparable extends com.asm3.Mesurable {
2   public static final int LESS;
3 
4   public static final int EQUAL;
5 
6   public static final int GREATER;
7 
8   public abstract int compareTo(java.lang.Object);
9 }

  注:一個編譯後的java類不包含package和import段,因此在class檔案中所有的型別都使用的是全路徑。