Java反射和動態(tài)代理及SpringAOP原理解析

文章來源于Java官方文檔,原文請點這里

一,Classes

Java中的類型,要么是引用類型,要么是原始數(shù)據(jù)類型。原始數(shù)據(jù)類型是一個固定的集合,包括:boolean, byte, short, int, long, char, float, and double。其它都是引用類型,例如:arrays, string, enum 等。

對于任意一類的對象,在JVM中都會為其實例化一個不變的 Class 對象,該對象提供了可以檢查該對象運行時屬性、方法和類型信息的方法。同時,Class 對象也具備可以創(chuàng)建一個新的 class 或 對象的方法。更重要的是,Class 對象是反射API發(fā)揮作用的入口點。

1,Retrieving Class Objects

第一種,使用 Object.getClass()

當一個類是繼承自 Object 類的時候,那么該類的實例則可以通過調(diào)用 .getClass() 方法來獲取到這個類的 Class 對象。例如:

Class c = "foo".getClass();  //返回字符串的 Class 對象

enum E {
        A, B
}
Class c = E.A.getClass(); //返回枚舉類 E 的 Class 對象

byte[] bytes = new byte[1024];
Class c = bytes.getClass(); //返回 byte 類型的 Class 對象

Set<String> s = new HashSet<String>();
Class c = s.getClass();  //返回 HashSet 類型的 Class 對象
第二種,使用 .class

當沒有類的實例時,可以使用 .class 語法來獲取類的 Class 對象,同時,使用 .class 也可以獲取原始數(shù)據(jù)類型的 Class 對象。例如:

boolean b;
Class c = b.getClass();   // compile-time error
Class c = boolean.class;  // 正確返回 boolean 類型的 Class 對象

Class c = java.io.PrintStream.class; //返回 PrintStream 類的 Class 對象

Class c = int[][][].class; //返回 int 三維數(shù)組的 Class 對象
第三種,使用 Class.forName()

如果有類的完全限定名稱,則可以使用 Class.forName() 來獲取對應類的 Class 對象。這種方式不能應用到原始數(shù)據(jù)類型上。例如:

Class c = Class.forName("com.duke.MyLocaleServiceProvider"); //返回 MyLocaleServiceProvider 類的 Class 對象

//返回一個表示 double 類型一維數(shù)組的 Class 對象
//等價于 double[].class 或者 double[] darr = new  double[9]; darr.getClass()
Class cDoubleArray = Class.forName("[D");

//返回一個表示 String 類型的二維數(shù)組的 Class 對象
Class cStringArray = Class.forName("[[Ljava.lang.String;");

2,原始包裝類型的 TYPE 字段

對于原始數(shù)據(jù)類型 .class 這種方式是一種非常合適的方式來獲取它的 Class 對象。但是這里還有另外一種方式,對于每一種原始數(shù)據(jù)類型和 void 類型,在 java.lang 包中都有其一個對應的包裝類型。每個包裝類型都有一個 TYPE 字段可以用來獲取對應原始數(shù)據(jù)類型的 Class 對象。例如:

//以下都表示獲取 double 類型的 Class 對象
Class c = Double.TYPE;
Class c1 = double.class;

3,返回 Class 對象的方法

下面舉例的這些反射API都可以返回一個 Class 對象,但是前提是,你必須首先得直接或間接的已經(jīng)得到一個 Class 對象。

Class.getSuperclass() 獲取指定類的父類的 Class 對象

Class c = javax.swing.JButton.class.getSuperclass();

獲取定義成員類的 Class 對象
Class.getDeclaringClass()
java.lang.reflect.Field.getDeclaringClass()
java.lang.reflect.Method.getDeclaringClass()
java.lang.reflect.Constructor.getDeclaringClass()

class T {
    public String name; //這里必須是 public 類型
}
Class c = T.class.getField("name").getDeclaringClass(); //返回 T 類的 Class 對象

4,Examining Class Modifiers and Types

一個類可以聲明一個或多個修飾符(Modifiers),這些修飾符將影響它運行時的行為。

  • Access modifiers: public, protected, and private
  • Modifier requiring override: abstract
  • Modifier restricting to one instance: static
  • Modifier prohibiting value modification: final
  • Annotations

java.lang.reflect.Modifier 類包含了所有可用的修飾符,同時它包含了一些方法用來解析由 Class.getModifiers() 類返回的修飾符集合。

以下代碼示例展示了如何獲取一個類的相關聲明的組件信息,包括類修飾符,泛型類型參數(shù),實現(xiàn)的接口和繼承路徑。同時因為 Class 類實現(xiàn)了 java.lang.reflect.AnnotatedElement 接口,因此也可以去獲取類在運行時的注解定義信息。

public interface ITest {
}

public class ATest {
}

@Deprecated
public class Test<T> extends ATest implements ITest {
}

public class ClassDeclarationSpy {
    public static void main(String[] args) {
        Class c = Test.class;

        //類的全限定名稱
        System.out.println(c.getCanonicalName());

        //獲取類的修飾符
        System.out.println(Modifier.toString(c.getModifiers()));

        //獲取泛型類型參數(shù)
        TypeVariable[] typeVariables = c.getTypeParameters();
        for (int i = 0; i < typeVariables.length; i++) {
            System.out.println(typeVariables[i].getName());
        }

        //獲取實現(xiàn)的接口
        Type[] intfs = c.getGenericInterfaces();
        for (int i = 0; i < intfs.length; i++) {
            System.out.println(intfs[i].toString());
        }

        //獲取繼承的類
        List<Class> l = new ArrayList<Class>();
        printAncestor(c, l); //遞歸的獲取
        l.forEach(clazz -> System.out.println(clazz.getCanonicalName()));

        //獲取運行時定義的注解
        Annotation[] ann = c.getAnnotations();
        for (Annotation a : ann) {
            System.out.println(a.toString());
        }
    }

    private static void printAncestor(Class<?> c, List<Class> l) {
        Class<?> ancestor = c.getSuperclass();
        if (ancestor != null) {
            l.add(ancestor);
            printAncestor(ancestor, l);
        }
    }
}

5,Discovering Class Members

在 Class 類中包括兩種類型的方法,用來訪問類的字段、方法和構造器。一種是枚舉這些成員信息,一種是獲取指定的成員,如下圖:

1559122617186.jpg

二,Members

反射API定義了一個 java.lang.reflect.Member 接口,這個接口由 java.lang.reflect.Field, java.lang.reflect.Method, 和 java.lang.reflect.Constructor 三個類實現(xiàn)。

1,F(xiàn)ields

Field 由類型和值組成。java.lang.reflect.Field 類提供了可以操作某個對象中指定 Field 的類型信息、值信息的方法。

Obtaining Field Types

一個 Field 可能是原始數(shù)據(jù)類型也可能是引用類型。下面的代碼向你展示了打印 Filed 的類型信息、泛型類型以及 Field 名稱。

public class TestField {
    public static void main(String[] args) {
        Class c = FieldSpy.class;

        Field[] fields = c.getDeclaredFields();
        for (Field field : fields) {
            System.out.println("name: "+field.getName()+", type: "+field.getType()+", GenericType: "+field.getGenericType());
        }
    }

    static class FieldSpy<T> {
        public boolean[][] b = {{ false, false }, { true, true } };
        public String name  = "Alice";
        public List<Integer> list;
        public T val;
    }
}
Retrieving and Parsing Field Modifiers

Field 修飾符可以包含如下幾類:

  • Access modifiers: public, protected, and private
  • Field-specific modifiers governing runtime behavior: transient and volatile
  • Modifier restricting to one instance: static
  • Modifier prohibiting value modification: final
  • Annotations

通過 Field.getModifiers() 方法可以獲取到一個使用整數(shù)表示的該 Field 的修飾符集合。這個整數(shù)中的字節(jié)表示的修飾符定義在 java.lang.reflect.Modifier 類中,我們可以使用該類提供的方法來判斷 Field 的修飾符類型。

下面這個類向你展示了如何通過給定的修飾符檢索一個 Field,同時也展示了如何判斷一個 Field 是不是合成的,或者是一個枚舉的常量。

public class FieldModifierSpy {

    public static void main(String[] args) {
        Class c = FieldModifierDemo.class;

        Field[] flds = c.getDeclaredFields();
        for (Field field : flds) {
            int modifiers = field.getModifiers();
            if (Modifier.isPublic(modifiers)) {
                System.out.println("Public FieldName: "+field.getName());
            } else if (Modifier.isProtected(modifiers)) {
                System.out.println("Protected FieldName: "+field.getName());
            }

            if (field.isSynthetic()) {
                System.out.println("Field: "+field.getName()+" is Synthetic");
            }

            if (field.isEnumConstant()) {
                System.out.println("Field: "+field.getName()+" is EnumConstant");
            }
        }

        System.out.println("----------------------");

        c = FieldModifierDemo.Spy.class;

        flds = c.getDeclaredFields();
        for (Field field : flds) {
            int modifiers = field.getModifiers();
            if (Modifier.isPublic(modifiers)) {
                System.out.println("Public FieldName: "+field.getName());
            } else if (Modifier.isProtected(modifiers)) {
                System.out.println("Protected FieldName: "+field.getName());
            } else if (Modifier.isPrivate(modifiers)) {
                System.out.println("Private FieldName: "+field.getName());
            }

            if (field.isSynthetic()) {
                System.out.println("Field: "+field.getName()+" is Synthetic");
            }

            if (field.isEnumConstant()) {
                System.out.println("Field: "+field.getName()+" is EnumConstant");
            }
        }

        System.out.println("----------------------");

        c = FieldModifierDemo.Inner.class;

        flds = c.getDeclaredFields();
        // flds = c.getFields(); //這里將不包含合成字段
        for (Field field : flds) {
            int modifiers = field.getModifiers();
            if (Modifier.isPublic(modifiers)) {
                System.out.println("Public FieldName: "+field.getName());
            } else if (Modifier.isProtected(modifiers)) {
                System.out.println("Protected FieldName: "+field.getName());
            } else if (Modifier.isPrivate(modifiers)) {
                System.out.println("Private FieldName: "+field.getName());
            } else if (Modifier.isFinal(modifiers)) {
                System.out.println("Final FieldName: "+field.getName());
            }

            if (field.isSynthetic()) {
                System.out.println("Field: "+field.getName()+" is Synthetic");
            }

            if (field.isEnumConstant()) {
                System.out.println("Field: "+field.getName()+" is EnumConstant");
            }
        }
    }

    static class FieldModifierDemo {
        protected volatile int share;
        public int instance;

        enum Spy { BLACK , WHITE }

        class Inner {}
    }
}

需要注意的是,合成字段在 getFields() 方法的返回值中并未包含,這是該方法和 getDeclaredFields() 方法的一點區(qū)別。同時,由于 Field 類實現(xiàn)了 java.lang.reflect.AnnotatedElement 接口,因此我們也可以方便的獲取到該字段上的所有運行時注解。

Getting and Setting Field Values

下面的代碼展示了如何設置 Field 的值。

public class SetFieldValTest {
    enum Tweedle { DEE, DUM }

    public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException {
        Book book = new Book();
        String fmt = "%6S:  %-12s = %s%n";

        Class<?> c = book.getClass();

        //對于要設置的字段類型是原始數(shù)據(jù)類型的包裝類型時,不能使用 setLong, setInt 等方法
        //因為在反射中不會自動裝箱和拆箱,因此只能用 set 方法實現(xiàn)
        Field chap = c.getDeclaredField("chapters");
        chap.set(book, Long.valueOf(12));
        out.format(fmt, "after", "chapters", chap.get(book));

        Field age = c.getDeclaredField("age");
        age.setInt(book, 10);
        out.format(fmt, "after", "age", age.get(book));

        Field chars = c.getDeclaredField("characters");
        String[] newChars = { "Queen", "King" };
        chars.set(book, newChars);
        out.format(fmt, "after", "characters", Arrays.asList(book.characters));

        Field t = c.getDeclaredField("twin");
        t.set(book, Tweedle.DUM);
        out.format(fmt, "after", "twin", t.get(book));

        //對于用 private、final 修飾的字段,在設置值之前必須要設置其訪問屬性
        //setAccessible 方法只有在 security context 中被允許時才能夠成功
        Field flag = c.getDeclaredField("flag");
        flag.setAccessible(true);
        flag.set(book, true);
        out.format(fmt, "after", "flag", flag.getBoolean(book));
        flag.setAccessible(false);
    }

    static class Book {
        public String[] characters = { "Alice", "White Rabbit" };
        public Tweedle twin = Tweedle.DEE;
        public int age = 5;
        public Long chapters = 0L;
        private boolean flag = false;
    }
}

設置字段的值有可能會產(chǎn)生兩個異常:一個是修改使用 private、final 修飾的字段時訪問權限被拒絕,一個是使用 setLong、setInt 等方法時拋出設置失敗異常。關于解決方案和有可能的原因已經(jīng)在上述代碼中注釋描述,直接看代碼。

2,Methods

方法由返回值,參數(shù)以及可能拋出的異常構成。java.lang.reflect.Method 類提供了方法可以獲取方法參數(shù)的類型信息、方法的返回值信息,以及可以執(zhí)行指定對象的方法的方法。

Obtaining Method Type Information

以下代碼向你展示了如何獲取指定類中指定方法的參數(shù)信息、返回值信息、異常信息以及判斷該方法的參數(shù)是否是可變參數(shù),如下:

public class MethodSpy {
    private static final String  fmt = "%24s: %s%n";

    public static void main(String[] args) {
        try {
            String className = "java.lang.Class";
            String methodName = "cast";

            Class<?> c = Class.forName(className);
            Method[] allMethods = c.getDeclaredMethods();
            for (Method m : allMethods) {
                if (!m.getName().equals(methodName)) {
                    continue;
                }
                out.format("%s%n", m.toGenericString()); //獲取方法的泛型形式定義

                out.format(fmt, "ReturnType", m.getReturnType());
                out.format(fmt, "GenericReturnType", m.getGenericReturnType());

                Class<?>[] pType  = m.getParameterTypes();
                Type[] gpType = m.getGenericParameterTypes();
                for (int i = 0; i < pType.length; i++) {
                    out.format(fmt,"ParameterType", pType[i]);
                    out.format(fmt,"GenericParameterType", gpType[i]);
                }

                Class<?>[] xType  = m.getExceptionTypes();
                Type[] gxType = m.getGenericExceptionTypes();
                for (int i = 0; i < xType.length; i++) {
                    out.format(fmt,"ExceptionType", xType[i]);
                    out.format(fmt,"GenericExceptionType", gxType[i]);
                }

                out.println("是否是可變參數(shù):"+m.isVarArgs());
                out.println("------------------------------");
            }

            // production code should handle these exceptions more gracefully
        } catch (ClassNotFoundException x) {
            x.printStackTrace();
        }
    }
}

上面的代碼中,如果用下面的代碼簡單修改一下:

String className = "java.io.PrintStream";
String methodName = "format";

運行之后你會發(fā)現(xiàn),對于一個類中的多個重載方法,getDeclaredMethods 方法都會把它們返回回來。

Obtaining Names of Method Parameters

Method 類提供了 getParameters() 方法,用來獲取方法的參數(shù)名稱、類型等信息。但是在 .class 文件中并沒有存儲參數(shù)的名稱信息,這是因為許多使用和生成 .class 文件的工具,不太期望 .class 文件占用更大的靜態(tài)或動態(tài)空間,因為它們處理大的 .class 文件時,則會要求 jvm 占用更多的內(nèi)存,同時,方法參數(shù)名稱可能會暴露方法的一些安全信息,如果 passwrod 名稱等。

為了讓 .class 文件中存儲方法參數(shù)的名稱信息,則需要在編譯 java 源碼時給 javac 命令指定 -parameters 參數(shù)。

Retrieving and Parsing Method Modifiers

使用 getModifiers 方法可以獲取方法的修飾符信息,判斷方法具有哪些修飾符和 Field 中的操作是一樣的。

Invoking Methods

Method 類提供了一個 invoke() 方法用來通過反射的方式來執(zhí)行一個類中的某個方法,invoke() 方法需要兩個參數(shù),第一個參數(shù)是類的實例對象,如果要執(zhí)行的方法是 static 修飾的,那么第一個參數(shù)固定為 null,第二個參數(shù)是一個可變參數(shù),用來向要執(zhí)行的方法傳遞參數(shù)。

示例代碼如下:

public class MethodSpy {
    private static final String  fmt = "%24s: %s%n";

    public static void main(String[] args) throws IllegalAccessException, InstantiationException, InvocationTargetException {
        Class c = Deet.class;
        Method[] allMethods = c.getDeclaredMethods();
        Object t = c.newInstance();
        for (Method m : allMethods) {
            m.setAccessible(true);
            Object o;
            if (m.getParameters().length > 0) {
                o = m.invoke(t, new Locale("zh-CN"));
            } else {
                o = m.invoke(t);
            }
            if (Modifier.isStatic(m.getModifiers())) {
                out.println("invoke static method: "+m.getName());
                o = m.invoke(null);
            }
            out.println(o);
        }
    }

    static class Deet {
        private boolean testBar() {
            return true;
        }

        private int testFoo(Locale l) {
            return 0;
        }
        private static int testStatic() {
            return 1;
        }
    }
}

3,Constructors

反射API提供了 java.lang.reflect.Constructor 類用來操作構造方法,和 Method 類類似。

Finding Constructors

通過 Class.getDeclaredConstructors() 可以獲取到指定類的全部構造方法(包括 private 修飾的構造方法),但是 Class.getConstructors() 方法則只返回 public 修飾的構造方法。

Retrieving and Parsing Constructor Modifiers

直接參考普通方法的獲取方式即可。

Creating New Class Instances

有兩種用來創(chuàng)建一個類的實例的方式:

java.lang.reflect.Constructor.newInstance() //第一種,通過構造函數(shù)

Class.newInstance() //第二種,直接使用 Class 類

一般,推薦使用第一種,因為如下原因:

  • Class.newInstance() 它不理會類的構造方法的參數(shù),只會執(zhí)行無參數(shù)的構造方法
  • Class.newInstance() 它不理會構造函數(shù)的異常是 unchecked 或者是 checked,它直接將異常包裝成 InvocationTargetException 異常,然后拋出
  • Class.newInstance() 不能執(zhí)行 private 修飾的構造方法

下面的代碼示例,使用第一種方式來創(chuàng)建一個類的實例,并且該代碼也展示了,如何獲取到指定的構造方法,如下:

public class RestoreAliases {
    private static Map<String, String> defaultAliases = new HashMap<String, String>();
    private static Set<String> defaultAliasesSet = new HashSet<String>();
    static {
        defaultAliases.put("Duke", "duke@i-love-java");
        defaultAliases.put("Fang", "fang@evil-jealous-twin");

        defaultAliasesSet.add("zh");
        defaultAliasesSet.add("cn");
    }

    public static void main(String[] args) {
        try {
            //獲取具有指定參數(shù)個數(shù)和類型的構造方法
//            Constructor ctor = EmailAliases.class.getDeclaredConstructor(HashMap.class);
            Constructor ctor = EmailAliases.class.getDeclaredConstructor(HashSet.class);
            ctor.setAccessible(true);
//            EmailAliases email = (EmailAliases)ctor.newInstance(defaultAliases);
            EmailAliases email = (EmailAliases)ctor.newInstance(defaultAliasesSet);
            email.printKeys();

            // production code should handle these exceptions more gracefully
        } catch (InstantiationException x) {
            x.printStackTrace();
        } catch (IllegalAccessException x) {
            x.printStackTrace();
        } catch (InvocationTargetException x) {
            x.printStackTrace();
        } catch (NoSuchMethodException x) {
            x.printStackTrace();
        }
    }

    static class EmailAliases {
        private Set<String> aliases;
        private EmailAliases(HashMap<String, String> h) {
            out.println("EmailAliases HashMap run");
            aliases = h.keySet();
        }

        private EmailAliases(HashSet<String> h) {
            out.println("EmailAliases HashSet run");
            aliases = h;
        }

        public void printKeys() {
            out.format("Mail keys:%n");
            for (String k : aliases)
                out.format("  %s%n", k);
        }
    }
}

三,實現(xiàn)動態(tài)代理

首先應該理解代理模式,代理模式分為靜態(tài)代理和動態(tài)代理。

JDK中提供了 Proxy 類來實現(xiàn)動態(tài)代理,示例代碼如下:

public interface MyDemoInterface {
    int run(int time);
}

public class MyDemoClass implements MyDemoInterface {
    @Override
    public int run(int time) {
        return time*10;
    }
}

public class MyProxyHandler implements InvocationHandler {
    private Object object;

    public MyProxyHandler(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(object, args);
    }
}

public class TestJdkProxy {
    public static void main(String[] args) {
        MyDemoInterface myDemoClass = new MyDemoClass();

        MyProxyHandler handler = new MyProxyHandler(myDemoClass);

        MyDemoInterface myDemoClassProxy = (MyDemoInterface) Proxy.newProxyInstance(
                MyDemoInterface.class.getClassLoader(),
                new Class<?>[] {MyDemoInterface.class},
                handler
        );

        System.out.println(myDemoClassProxy.run(10));
    }
}

JDK的動態(tài)代理要求要代理的類必須是實現(xiàn)了某個接口,不然無法代理。因此我們這個時候就需要使用 cglib 來實現(xiàn)動態(tài)代理,示例代碼如下:

public class DemoClass {
    public int run(int time) {
        return time*10;
    }
}

public class MyCglibProxy implements MethodInterceptor {
    private Object target;

    public Object getInstance(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        return methodProxy.invokeSuper(o, args);
    }
}

public class TestCglibProxy {
    public static void main(String[] args) {
        DemoClass demoClass = new DemoClass();

        MyCglibProxy cglibProxy = new MyCglibProxy();

        DemoClass demoClassProxy = (DemoClass) cglibProxy.getInstance(demoClass);

        System.out.println(demoClassProxy.run(10));
    }
}

四,Spring中AOP原理

在揭示SpringAOP的原理之前,我們需要先分析JDK的Proxy類的執(zhí)行流程,而后根據(jù)這個流程來反推Spring中關于AOP的兩種實現(xiàn)方式的大體原理,之后再通過查看Spring的源碼來進行證明。

首先我們把上面使用JDK實現(xiàn)動態(tài)代理的代碼中修改如下幾個地方:

public class MyProxyHandler implements InvocationHandler {
    private Object object;

    public MyProxyHandler(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.print(method.getName()+"==>");
        for (Parameter parameter :method.getParameters()) {
            System.out.println(parameter.getType().toGenericString());
        }
        System.out.println();

        return method.invoke(object, args);
    }
}

public class TestJdkProxy {
    public static void main(String[] args) {
        MyDemoInterface myDemoClass = new MyDemoClass();

        MyProxyHandler handler = new MyProxyHandler(myDemoClass);

        MyDemoInterface myDemoClassProxy = (MyDemoInterface) Proxy.newProxyInstance(
                MyDemoInterface.class.getClassLoader(),
                new Class<?>[] {MyDemoInterface.class},
                handler
        );

        //打印代理類的類名
        System.out.println(myDemoClassProxy.getClass().toGenericString());
        System.out.println();

        //打印代理類實現(xiàn)的接口信息
        for (Type type : myDemoClassProxy.getClass().getGenericInterfaces()) {
            System.out.println(type.getTypeName());
        }
        System.out.println();

        //打印代理類中的方法名稱
        for (Method method : myDemoClassProxy.getClass().getDeclaredMethods()) {
            System.out.print(method.getName()+"==>");
            for (Parameter parameter :method.getParameters()) {
                System.out.println(parameter.getType().toGenericString());
            }
            System.out.println();
        }

        System.out.println("------------------");
        System.out.println(myDemoClassProxy.run(10));
    }
}

實際就是加了一些打印信息,利用這些打印信息,再結合 Proxy 類中的如下源碼(源碼解釋看注釋):

private static final Class<?>[] constructorParams =
        { InvocationHandler.class };

//獲取到的代理類構造函數(shù)
protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
}

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        //創(chuàng)建出代理類,實際就是 Proxy 類本身
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
            //獲取到代理中參數(shù)類型為 constructorParams 類型的構造函數(shù),實際就是獲取到了上面摘抄
            //下來的構造函數(shù),
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            // 創(chuàng)建代理類的實例,實際就相當于調(diào)用了
            // new Proxy(InvocationHandler h)
            // 注意,這里的 InvocationHandler 就是我們外面?zhèn)鬟M來的,即我們上面代碼中的 MyProxyHandler 實例
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

根據(jù)對 JDK Proxy 類的源碼分析,以及 TestJdkProxy 類中的打印信息,最終分析推理且證明得到如下結論:

  • Proxy.newProxyInstance 方法實際做的事情就是在內(nèi)部幫我們動態(tài)的生成了一個 Proxy 類,然后這個生成的類實現(xiàn)了我們準備被代理類 MyDemoClass 類的接口,即實現(xiàn)了 MyDemoInterface 接口,這也就是為什么 Proxy.newProxyInstance 返回的實例可以強制轉換成 MyDemoInterface 類型,根本原因是因為這個實例對應的類也實現(xiàn)了 MyDemoInterface 接口
  • Proxy類中生成的代理類同時也實現(xiàn)了 MyDemoInterface 中的全部方法,同時參數(shù)信息也是保持一致的,這也就解釋了為什么 Proxy.newProxyInstance 返回的實例在后續(xù)階段可以直接調(diào)用 MyDemoInterface 中的方法
  • Proxy類中生成的代理類中實現(xiàn)的 MyDemoInterface 中的方法的方法執(zhí)行流程為:直接調(diào)用 InvocationHandler 中的 invoke 方法同時傳遞進去必要的參數(shù)然后返回 invoke 方法的返回值,但是這里的返回值數(shù)據(jù)類型必須要和接口中的方法的返回值數(shù)據(jù)類型一致,不然會拋異常。
  • 我們定義的 InvocationHandler 中的 invoke 方法成為了我們實際要執(zhí)行的被代理類的方法的入口,在該入口中,我們可以自主的選擇執(zhí)行被代理類方法的時機,因此該 invoke 方法可以被認為就是 AOP 中的JointPoint

清楚了 Proxy 類的流程和動態(tài)代理的本質之后,現(xiàn)在來看 Spring AOP 的實現(xiàn)原理。在開始之前,需要先澄清一些關于 AOP 的概念。首先 AOP 是一種編程模式,稱為面向切面編程。它與傳統(tǒng)面向對象編程的區(qū)別在于,使用 AOP 可以在系統(tǒng)中的某些位置動態(tài)的插入邏輯而不影響且不用修改原來的執(zhí)行邏輯和代碼。通常的應用包括日志打印,權限控制,事物控制等。

AOP 中包括如下幾個術語:

  • JointPoint 連接點,即上面我們提到的 invoke 方法
  • Advice 通知,定義在連接點的哪個位置執(zhí)行插入的邏輯,通常包括:前置,后置,環(huán)繞等
  • Pointcut 切入點,在 AOP 中使用 AOP 表達式尋找到的一組連接點
  • Aspect 切面,上述三個概念的組合稱為一個切面

清楚了概念之后,再來看原理。AOP 是一種規(guī)范,沒有固定的實現(xiàn),它的底層技術原理就是動態(tài)代理。在 Spring AOP 中提供了兩種方式的動態(tài)代理實現(xiàn):JDK 動態(tài)代理 和 cglib 動態(tài)代理。分別對應的源碼類為:JdkDynamicAopProxy 和 CglibAopProxy,它們的公共父類接口為:AopProxy。

我們通過閱讀 JdkDynamicAopProxy 的如下部分源碼可知,Spring 的 JdkDynamicAopProxy 實現(xiàn)實際就是 JDK Proxy 的一個簡單包裝,整體實現(xiàn)流程和上面我們分析的過程基本一致。關鍵源碼和注釋說明如下:

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {

@Override
public Object getProxy(ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
        }
                // 得到代理類的接口信息,實際得到的就是 AopProxy 類,說明 Spring 中所有 Bean 的代理類實際
                // 都實現(xiàn)了  AopProxy 類,雖然該類是空的
        Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
        findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
                // 利用 JDK Proxy 創(chuàng)建代理類實例,同時指定 InvocationHandler 為它自己
        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

}

理清了 JdkDynamicAopProxy 的流程和原理,那么如果你對 cglib 較為熟悉的話,相信 CglibAopProxy 也一樣可以看明白,因為這個類實際上就是 cglib 實現(xiàn)動態(tài)代理的一個簡單包裝。

掌握了原理之后再來學習 Spring AOP,會發(fā)現(xiàn),這個實際非常簡單。首先 JointPoint 的動態(tài)織入代理 Spring 通過底層的動態(tài)代理框架已經(jīng)幫我們實現(xiàn)了,我們要做的就是學習在 Spring 中可以使用什么樣的表達式來尋找這些連接點,即首先要學習如何定義一個 Pointcut,然后有了切入點之后再根據(jù)實際情況說明我們要執(zhí)行的通知,當然這也需要符合 Spring 聲明通知的規(guī)范,最后則是學習在 Spring 中如何把 Pointcut 和 Advice 組織到一起形成一個切面,這些都是固定的規(guī)范語法,查看文檔即可,而根據(jù)的底層原理我們已經(jīng)掌握了。

關于 Spring AOP 的語法規(guī)范可以查看這里

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容