美文网首页java基础
Java反射和动态代理及SpringAOP原理解析

Java反射和动态代理及SpringAOP原理解析

作者: 张angang强吖 | 来源:发表于2019-05-31 00:50 被阅读70次

    文章来源于Java官方文档,原文请点这里

    一,Classes

    Java中的类型,要么是引用类型,要么是原始数据类型。原始数据类型是一个固定的集合,包括:boolean, byte, short, int, long, char, float, and double。其它都是引用类型,例如:arrays, string, enum 等。

    对于任意一类的对象,在JVM中都会为其实例化一个不变的 Class 对象,该对象提供了可以检查该对象运行时属性、方法和类型信息的方法。同时,Class 对象也具备可以创建一个新的 class 或 对象的方法。更重要的是,Class 对象是反射API发挥作用的入口点。

    1,Retrieving Class Objects

    第一种,使用 Object.getClass()

    当一个类是继承自 Object 类的时候,那么该类的实例则可以通过调用 .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 也可以获取原始数据类型的 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 三维数组的 Class 对象
    
    第三种,使用 Class.forName()

    如果有类的完全限定名称,则可以使用 Class.forName() 来获取对应类的 Class 对象。这种方式不能应用到原始数据类型上。例如:

    Class c = Class.forName("com.duke.MyLocaleServiceProvider"); //返回 MyLocaleServiceProvider 类的 Class 对象
    
    //返回一个表示 double 类型一维数组的 Class 对象
    //等价于 double[].class 或者 double[] darr = new  double[9]; darr.getClass()
    Class cDoubleArray = Class.forName("[D");
    
    //返回一个表示 String 类型的二维数组的 Class 对象
    Class cStringArray = Class.forName("[[Ljava.lang.String;");
    

    2,原始包装类型的 TYPE 字段

    对于原始数据类型 .class 这种方式是一种非常合适的方式来获取它的 Class 对象。但是这里还有另外一种方式,对于每一种原始数据类型和 void 类型,在 java.lang 包中都有其一个对应的包装类型。每个包装类型都有一个 TYPE 字段可以用来获取对应原始数据类型的 Class 对象。例如:

    //以下都表示获取 double 类型的 Class 对象
    Class c = Double.TYPE;
    Class c1 = double.class;
    

    3,返回 Class 对象的方法

    下面举例的这些反射API都可以返回一个 Class 对象,但是前提是,你必须首先得直接或间接的已经得到一个 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() 类返回的修饰符集合。

    以下代码示例展示了如何获取一个类的相关声明的组件信息,包括类修饰符,泛型类型参数,实现的接口和继承路径。同时因为 Class 类实现了 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()));
    
            //获取泛型类型参数
            TypeVariable[] typeVariables = c.getTypeParameters();
            for (int i = 0; i < typeVariables.length; i++) {
                System.out.println(typeVariables[i].getName());
            }
    
            //获取实现的接口
            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 三个类实现。

    1,Fields

    Field 由类型和值组成。java.lang.reflect.Field 类提供了可以操作某个对象中指定 Field 的类型信息、值信息的方法。

    Obtaining Field Types

    一个 Field 可能是原始数据类型也可能是引用类型。下面的代码向你展示了打印 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() 方法可以获取到一个使用整数表示的该 Field 的修饰符集合。这个整数中的字节表示的修饰符定义在 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() 方法的一点区别。同时,由于 Field 类实现了 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();
    
            //对于要设置的字段类型是原始数据类型的包装类型时,不能使用 setLong, setInt 等方法
            //因为在反射中不会自动装箱和拆箱,因此只能用 set 方法实现
            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;
        }
    }
    

    设置字段的值有可能会产生两个异常:一个是修改使用 private、final 修饰的字段时访问权限被拒绝,一个是使用 setLong、setInt 等方法时抛出设置失败异常。关于解决方案和有可能的原因已经在上述代码中注释描述,直接看代码。

    2,Methods

    方法由返回值,参数以及可能抛出的异常构成。java.lang.reflect.Method 类提供了方法可以获取方法参数的类型信息、方法的返回值信息,以及可以执行指定对象的方法的方法。

    Obtaining Method Type Information

    以下代码向你展示了如何获取指定类中指定方法的参数信息、返回值信息、异常信息以及判断该方法的参数是否是可变参数,如下:

    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("是否是可变参数:"+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";
    

    运行之后你会发现,对于一个类中的多个重载方法,getDeclaredMethods 方法都会把它们返回回来。

    Obtaining Names of Method Parameters

    Method 类提供了 getParameters() 方法,用来获取方法的参数名称、类型等信息。但是在 .class 文件中并没有存储参数的名称信息,这是因为许多使用和生成 .class 文件的工具,不太期望 .class 文件占用更大的静态或动态空间,因为它们处理大的 .class 文件时,则会要求 jvm 占用更多的内存,同时,方法参数名称可能会暴露方法的一些安全信息,如果 passwrod 名称等。

    为了让 .class 文件中存储方法参数的名称信息,则需要在编译 java 源码时给 javac 命令指定 -parameters 参数。

    Retrieving and Parsing Method Modifiers

    使用 getModifiers 方法可以获取方法的修饰符信息,判断方法具有哪些修饰符和 Field 中的操作是一样的。

    Invoking Methods

    Method 类提供了一个 invoke() 方法用来通过反射的方式来执行一个类中的某个方法,invoke() 方法需要两个参数,第一个参数是类的实例对象,如果要执行的方法是 static 修饰的,那么第一个参数固定为 null,第二个参数是一个可变参数,用来向要执行的方法传递参数。

    示例代码如下:

    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

    有两种用来创建一个类的实例的方式:

    java.lang.reflect.Constructor.newInstance() //第一种,通过构造函数
    
    Class.newInstance() //第二种,直接使用 Class 类
    

    一般,推荐使用第一种,因为如下原因:

    • Class.newInstance() 它不理会类的构造方法的参数,只会执行无参数的构造方法
    • Class.newInstance() 它不理会构造函数的异常是 unchecked 或者是 checked,它直接将异常包装成 InvocationTargetException 异常,然后抛出
    • Class.newInstance() 不能执行 private 修饰的构造方法

    下面的代码示例,使用第一种方式来创建一个类的实例,并且该代码也展示了,如何获取到指定的构造方法,如下:

    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 {
                //获取具有指定参数个数和类型的构造方法
    //            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);
            }
        }
    }
    

    三,实现动态代理

    首先应该理解代理模式,代理模式分为静态代理和动态代理。

    JDK中提供了 Proxy 类来实现动态代理,示例代码如下:

    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的动态代理要求要代理的类必须是实现了某个接口,不然无法代理。因此我们这个时候就需要使用 cglib 来实现动态代理,示例代码如下:

    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类的执行流程,而后根据这个流程来反推Spring中关于AOP的两种实现方式的大体原理,之后再通过查看Spring的源码来进行证明。

    首先我们把上面使用JDK实现动态代理的代码中修改如下几个地方:

    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();
    
            //打印代理类实现的接口信息
            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 };
    
    //获取到的代理类构造函数
    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.
             */
            //创建出代理类,实际就是 Proxy 类本身
            Class<?> cl = getProxyClass0(loader, intfs);
    
            /*
             * Invoke its constructor with the designated invocation handler.
             */
            try {
                if (sm != null) {
                    checkNewProxyPermission(Reflection.getCallerClass(), cl);
                }
                //获取到代理中参数类型为 constructorParams 类型的构造函数,实际就是获取到了上面摘抄
                //下来的构造函数,
                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;
                        }
                    });
                }
                // 创建代理类的实例,实际就相当于调用了
                // new Proxy(InvocationHandler h)
                // 注意,这里的 InvocationHandler 就是我们外面传进来的,即我们上面代码中的 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);
            }
        }
    

    根据对 JDK Proxy 类的源码分析,以及 TestJdkProxy 类中的打印信息,最终分析推理且证明得到如下结论:

    • Proxy.newProxyInstance 方法实际做的事情就是在内部帮我们动态的生成了一个 Proxy 类,然后这个生成的类实现了我们准备被代理类 MyDemoClass 类的接口,即实现了 MyDemoInterface 接口,这也就是为什么 Proxy.newProxyInstance 返回的实例可以强制转换成 MyDemoInterface 类型,根本原因是因为这个实例对应的类也实现了 MyDemoInterface 接口
    • Proxy类中生成的代理类同时也实现了 MyDemoInterface 中的全部方法,同时参数信息也是保持一致的,这也就解释了为什么 Proxy.newProxyInstance 返回的实例在后续阶段可以直接调用 MyDemoInterface 中的方法
    • Proxy类中生成的代理类中实现的 MyDemoInterface 中的方法的方法执行流程为:直接调用 InvocationHandler 中的 invoke 方法同时传递进去必要的参数然后返回 invoke 方法的返回值,但是这里的返回值数据类型必须要和接口中的方法的返回值数据类型一致,不然会抛异常。
    • 我们定义的 InvocationHandler 中的 invoke 方法成为了我们实际要执行的被代理类的方法的入口,在该入口中,我们可以自主的选择执行被代理类方法的时机,因此该 invoke 方法可以被认为就是 AOP 中的JointPoint

    清楚了 Proxy 类的流程和动态代理的本质之后,现在来看 Spring AOP 的实现原理。在开始之前,需要先澄清一些关于 AOP 的概念。首先 AOP 是一种编程模式,称为面向切面编程。它与传统面向对象编程的区别在于,使用 AOP 可以在系统中的某些位置动态的插入逻辑而不影响且不用修改原来的执行逻辑和代码。通常的应用包括日志打印,权限控制,事物控制等。

    AOP 中包括如下几个术语:

    • JointPoint 连接点,即上面我们提到的 invoke 方法
    • Advice 通知,定义在连接点的哪个位置执行插入的逻辑,通常包括:前置,后置,环绕等
    • Pointcut 切入点,在 AOP 中使用 AOP 表达式寻找到的一组连接点
    • Aspect 切面,上述三个概念的组合称为一个切面

    清楚了概念之后,再来看原理。AOP 是一种规范,没有固定的实现,它的底层技术原理就是动态代理。在 Spring AOP 中提供了两种方式的动态代理实现:JDK 动态代理 和 cglib 动态代理。分别对应的源码类为:JdkDynamicAopProxy 和 CglibAopProxy,它们的公共父类接口为:AopProxy。

    我们通过阅读 JdkDynamicAopProxy 的如下部分源码可知,Spring 的 JdkDynamicAopProxy 实现实际就是 JDK Proxy 的一个简单包装,整体实现流程和上面我们分析的过程基本一致。关键源码和注释说明如下:

    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 的代理类实际
                    // 都实现了  AopProxy 类,虽然该类是空的
            Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
            findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
                    // 利用 JDK Proxy 创建代理类实例,同时指定 InvocationHandler 为它自己
            return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
    }
    
    }
    

    理清了 JdkDynamicAopProxy 的流程和原理,那么如果你对 cglib 较为熟悉的话,相信 CglibAopProxy 也一样可以看明白,因为这个类实际上就是 cglib 实现动态代理的一个简单包装。

    掌握了原理之后再来学习 Spring AOP,会发现,这个实际非常简单。首先 JointPoint 的动态织入代理 Spring 通过底层的动态代理框架已经帮我们实现了,我们要做的就是学习在 Spring 中可以使用什么样的表达式来寻找这些连接点,即首先要学习如何定义一个 Pointcut,然后有了切入点之后再根据实际情况说明我们要执行的通知,当然这也需要符合 Spring 声明通知的规范,最后则是学习在 Spring 中如何把 Pointcut 和 Advice 组织到一起形成一个切面,这些都是固定的规范语法,查看文档即可,而根据的底层原理我们已经掌握了。

    关于 Spring AOP 的语法规范可以查看这里

    相关文章

      网友评论

        本文标题:Java反射和动态代理及SpringAOP原理解析

        本文链接:https://www.haomeiwen.com/subject/xqijiqtx.html