Java反射机制与动态代理

作者: Java旺 | 来源:发表于2019-12-16 20:43 被阅读0次
    1. 概述
      Java 反射机制与动态代理我们平时写代码可能用得比较少,但在各种常见的框架(Spring、MyBatis 等)中却屡见不鲜。有句话叫“无反射,不框架;无代理,不框架”。

    由于以后打算阅读和学习框架的源码,这里先简单回顾反射机制和动态代理(暂不深入分析实现原理),为后面做些准备 。

    1. 反射机制
      Java 反射机制是在 Java 程序运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性。这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制。

    从面向对象的角度来看,我们平时用到的"类"、"构造器"、"属性"、"方法"其实也是一个"类",它们在 JDK 中分别对应 Class、Constructor、Field、Method 类。其中 Class 相当于"类的类",可称为"元类",从这个角度看,我们平时自定义的"类"可以理解为 Class 的一个对象。

    简单来说(个人理解),反射机制就是通过 Class、Constructor 等"元类"来操作其他的普通类(创建对象、调用方法等)。下面以简单代码示例。

    2.1 准备代码
    定义一个普通的 Java 类 Human,如下:

    package com.jaxer.example.reflection;
    public class Human {
      public String gender;
      private int age;
      protected void hello() {
      }
      public void hi() {
      }
    }
    定义一个普通的 Person 类,继承自 Human,如下:
    public class Person extends Human {
      // 两个属性
      public String name;
      private int age;
      // 各种修饰符的构造器
      public Person() {
      }
      private Person(String name) {
        this.name = name;
      }
      protected Person(int age) {
        this.age = age;
      }
      Person(String name, int age) {
        this.name = name;
        this.age = age;
      }
      // getter, setter 方法
      public String getName() {
        return name;
    }
      public void setName(String name) {
        this.name = name;
      }
      public int getAge() {
        return age;
      }
      public void setAge(int age) {
        this.age = age;
      }
      // 自定义方法
      private void test() {
        System.out.println("test is invoked.");
    
      }
      @Override
      public String toString() {
        return "Person{" +
            "name='" + name + '\'' +
            ", age=" + age +
            '}';
      }
    }
    

    该 Person 类定义了 name 和 age 两个成员变量及其 getter/setter 方法,还有四个构造器,分别使用不同的修饰符,自定义了一个 test 方法,重写了 toString 方法。

    PS: 这两个类仅供参考,只是为了演示。

    2.2 获取 Class
    获取 Person 的 Class 对象通常有下面三种方式:

    // 方式一:从对象获取
    Person person = new Person();
    person.getClass();
    // 方式二:类名.class
    Person.class;
    // 方式三:Class.forName
    Class<?> aClass = Class.forName("com.jaxer.example.reflection.Person");
    /*
      这三种方式的获取到的都是:
        class com.jaxer.example.reflection.Person
      而且可以证明,这三种方式得到的同一个 Class 对象,即同一个 Person 类
    */
    获取到了 Class 对象,就可以使用 Class 对象来创建 Person 对象或者做一些其他操作,例如:
    // 获取 Class 对象
    Class<?> aClass = Class.forName("com.jaxer.example.reflection.Person");
    // 使用 Class 创建 Person 对象
    System.out.println(aClass.newInstance()); // 创建 Person 对象
    // 获取 Person 的父类(Human)
    System.out.println("superClass-->" + aClass.getSuperclass());
    /*  输出结果:
     *  Person{name='null', age=0}
     *  superClass-->class com.jaxer.example.reflection.Human
     */
    

    2.3 获取 Constructor
    Class 中获取构造器(Constructor)的方法如下:
    getConstructors: 获取所有 public 构造器;
    getDeclaredConstructors: 获取所有构造器(包括 private 类型);
    getConstructor(Class… parameterTypes): 获取指定参数的 public 构造器;
    getDeclaredConstructor(Class… parameterTypes): 获取指定参数的构造器(包括 private 类型)。

    测试代码:

    private static void testConstructors(Class<?> aClass) throws Exception {
    
        // 1. 获取所有public构造器
    
        Constructor<?>[] constructors = aClass.getConstructors();
    
        for (Constructor<?> constructor : constructors) {
    
            System.out.println("constructor-->" + constructor);
    
        }
    
        // 2. 获取所有构造器
        Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println("declaredConstructor-->" + declaredConstructor);
        }
        // 4. 获取指定的构造器(根据参数类型)
        Constructor<?> constructor = aClass.getDeclaredConstructor(String.class);
        // 修改访问权限(用这种方式可以使用 private 类型构造器创建对象)
        constructor.setAccessible(true);
        // 使用该构造器创建 Person 对象
        Object instance = constructor.newInstance("Ace");
        System.out.println(instance);
    }
    

    输出结果如下:

    constructor-->public com.jaxer.example.reflection.Person()
    declaredConstructor-->com.jaxer.example.reflection.Person(java.lang.String,int)
    
    declaredConstructor-->protected com.jaxer.example.reflection.Person(int)
    
    declaredConstructor-->private com.jaxer.example.reflection.Person(java.lang.String)
    
    declaredConstructor-->public com.jaxer.example.reflection.Person()
    Person{name='Ace', age=0}
    

    2.4 获取 Field
    获取 Class 属性(Field)的方法如下:
    getFields(): 获取 public 属性(包含父类);
    getDeclaredFields(): 获取所有属性(包括 private 类型);
    getField(String name): 获取指定名称的 public 属性;
    getDeclaredField(String name): 获取指定名称的属性(包括 private 类型)。
    测试代码:

    private static void testFields(Class<?> aClass) throws Exception {
        // 1. 获取 public 属性(包含父类的 public 属性)
        Field[] fields = aClass.getFields();
        for (Field field : fields) {
            System.out.println("field-->" + field);
        }
        // 2. 获取所有属性
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println("declaredField-->" + declaredField);
        }
        // 4. 获取指定的属性(这里获取 age 属性)
        Field age = aClass.getDeclaredField("age");
        System.out.println("age-->" + age);
        // 给指定对象的属性赋值(private 类型不能赋值,需要修改访问权限)
       Object obj = aClass.newInstance();
        age.setAccessible(true); // 修改访问权限
        age.set(obj, 18); // 设置新值
        System.out.println("obj-->" + obj);
    }
    

    输出结果:

    field-->public java.lang.String com.jaxer.example.reflection.Person.name
    field-->public java.lang.String com.jaxer.example.reflection.Human.gender
    declaredField-->public java.lang.String com.jaxer.example.reflection.Person.name
    declaredField-->private int com.jaxer.example.reflection.Person.age
    age-->private int com.jaxer.example.reflection.Person.age
    obj-->Person{name='null', age=18}
    

    2.5 获取 Method
    获取 Class 方法(Method)的方法如下:
    getMethods(): 获取所有 pubic 方法(包括父类及 Object 类);
    getDeclaredMethods(): 获取所有方法(包括 private 方法);
    getMethod(String name, Class… parameterTypes): 获取指定名称和参数的 public 方法;
    getDeclaredMethod(String name, Class… parameterTypes): 获取指定名称和参数的方法(包括 private 方法)。
    测试代码:

    private static void testMethods(Class<?> aClass) throws Exception {
      // 1. 获取所有 public 方法(包括父类及 Object 类)
      Method[] methods = aClass.getMethods();
      for (Method method : methods) {
        System.out.println("method-->" + method);
      } 
      // 2. 获取该类声明的所有方法
      Method[] declaredMethods = aClass.getDeclaredMethods();
      for (Method declaredMethod : declaredMethods) {
        System.out.println("declaredMethod-->" + declaredMethod);
      }
      // 4. 获取指定的方法并调用
      Method method = aClass.getDeclaredMethod("test");
      method.setAccessible(true); // 修改访问权限
      System.out.println("方法名:" + method.getName());
      System.out.println("方法修饰符:" + Modifier.toString(method.getModifiers()));
      Object instance = aClass.newInstance();
      System.out.println(method.invoke(instance));
    }
    

    输出结果:

    // getMethods 方法返回结果:
      // Person 类的 public 方法:
    method-->public java.lang.String com.jaxer.example.reflection.Person.toString()
    method-->public java.lang.String com.jaxer.example.reflection.Person.getName()
    method-->public void com.jaxer.example.reflection.Person.setName(java.lang.String)
    method-->public void com.jaxer.example.reflection.Person.setAge(int)
    method-->public int com.jaxer.example.reflection.Person.getAge()
      // Human 类的 public 方法:
    method-->public void com.jaxer.example.reflection.Human.hi()
      // Object 类的 public 方法:
    method-->public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
    method-->public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
    method-->public final void java.lang.Object.wait() throws java.lang.InterruptedException
    method-->public boolean java.lang.Object.equals(java.lang.Object)
    method-->public native int java.lang.Object.hashCode()
    method-->public final native java.lang.Class java.lang.Object.getClass()
    method-->public final native void java.lang.Object.notify()
    method-->public final native void java.lang.Object.notifyAll()
    // getDeclaredMethods 方法返回结果:
    declaredMethod-->public java.lang.String com.jaxer.example.reflection.Person.toString()
    declaredMethod-->public java.lang.String com.jaxer.example.reflection.Person.getName()
    declaredMethod-->public void com.jaxer.example.reflection.Person.setName(java.lang.String)
    
    declaredMethod-->private void com.jaxer.example.reflection.Person.test()
    
    declaredMethod-->public void com.jaxer.example.reflection.Person.setAge(int)
    
    declaredMethod-->public int com.jaxer.example.reflection.Person.getAge()
    // 调用 test 方法:
    
    方法名:private void com.jaxer.example.reflection.Person.test()
    方法修饰符:private
    test is invoked.
    null
    

    框架中常用的反射机制主要是以上那些,这里暂不深究其实现原理,以后有需要再行补充。下面简要分析代理相关的内容。

    1. 代理
      使用代理的主要目的:对目标对象(的方法)进行功能增强,例如 Spring 的 AOP。

    既然是对目标对象的方法进行增强,代理对象的方法中一定会调用目标对象的方法。而且一般会在目标对象的方法调用前后(或者其他时机)做一些其他的处理以达到增强的效果。

    代理模式通常分为「静态代理」和「动态代理」,静态代理使用较少;而动态代理常用的有 JDK 动态代理和 CGLib 动态代理。下面简要分析。

    3.1 准备代码
    一个普通的 Java 接口及其实现类如下:

    // 接口
    public interface UserService {
        void save(String name);
    }
    // 实现类
    public class UserServiceImpl implements UserService {
        @Override
        public void save(String name) {
            System.out.println("保存用户名:" + name);
        }
    }
    

    无论静态代理还是动态代理,都是对该类的 save 方法进行增强。

    此处以在该方法执行前后各打印一句话来演示。

    3.2 静态代理
    主要思想:创建一个 UserService 接口的实现类 UserServiceProxy(代理对象),并且该类持有 UserServiceImpl 对象(目标对象),在 UserServiceProxy 类的 save 方法中调用 UserServiceImpl 的 save 方法,示例代码如下:

    public class UserServiceProxy implements UserService {
      private UserService userService;
      public UserServiceProxy(UserService userService) {
        this.userService = userService;
      }
      @Override
      public void save(String name) {
        System.out.println("---静态代理:方法执行前---");
        userService.save(name); // 调用目标类的方法
        System.out.println("---静态代理:方法执行后---");
      }
    }
    

    测试代码:

    private static void testStaticProxy() {
      UserService userService = new UserServiceImpl();
      UserService proxy = new UserServiceProxy(userService);
      proxy.save("jack");
    }
    /* 运行结果:
    
      ---静态代理:方法执行前---
    保存用户:jack
      ---静态代理:方法执行后---
    */
    

    显而易见,使用 UserServiceProxy 可以增强 UserServiceImpl 的 save 方法。但由于代码是固定的(编码期间写好的),不够灵活,因此静态代理使用较少,通常使用动态代理。

    PS: 此处代码实现形式可能不尽相同,但思路相近。

    3.3 动态代理
    与静态代理相比,动态代理则是在运行时动态生成一个代理类,该类可以对目标对象的方法进行功能增强。动态代理常用的有 JDK 动态代理和 CGLib 动态代理,下面简要分析。

    3.3.1 JDK 动态代理
    JDK 的动态代理实现方式:

    使用 Proxy 类的 newProxyInstance 方法创建代理对象,使用 InvocationHandler 来实现增强的逻辑(通常创建一个 InvocationHandler 接口的实现类,在其 invoke 方法中实现增强的逻辑)。示例代码如下(仅供参考):

    // JDK 代理工厂,作用是生成代理对象
    public class JDKProxyFactory {
        // 获取 target 的代理对象(其中 target 为目标对象)
        public Object getProxy(Object target) {
            return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(), new MyInvocationHandler(target));
        }
        // 自定义 InvocationHandler
        private static class MyInvocationHandler implements InvocationHandler {
            // 目标对象
            private Object target;
            public MyInvocationHandler(Object target) {
                this.target = target;
            }
           // 实现增强逻辑
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("目标类方法执行前-----");
                Object result = method.invoke(target, args); // 调用目标对象的方法
                System.out.println("目标类方法执行后-----");
                return result;
            }
        }
    }
    

    测试代码:

    private static void testJdkProxy() {
        UserService userService = new UserServiceImpl(); // 目标对象
        JDKProxyFactory jdkProxyFactory = new JDKProxyFactory(); // 代理工厂
        // 使用代理工厂生成代理对象
        UserService proxy = (UserService) jdkProxyFactory.getProxy(userService);
        proxy.save("jack"); // 调用代理对象的方法(已对目标对象进行增强)
    }
    /* 运行结果:
        目标类方法执行前-----
        保存用户:jack
        目标类方法执行后-----
    */
    

    实现原理可参考:https://blog.csdn.net/yhl_jxy/article/details/80586785

    3.3.2 CGLib 动态代理
    主要思路:

    创建一个目标类的子类 Enhancer,在子类中设置回调对象(MethodInterceptor),并在回调方法(intercept)中实现对目标对象的增强功能逻辑。示例代码如下:

    // CGLIB 工厂,用于生成代理对象
    public class CglibProxyFactory {
        // 获取代理对象(对目标对象进行增强)
        public Object getProxy(Object target) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(target.getClass());
            enhancer.setCallback(new MyMethodInterceptor()); // 设置回调
           return enhancer.create();
        }
        // 自定义方法拦截 MethodInterceptor
       private static class MyMethodInterceptor implements MethodInterceptor {
           @Override
            public Object intercept(Object obj, Method method, Object[] args, 
                       MethodProxy proxy) throws Throwable {
                System.out.println("cglib方法调用前");
                Object o = proxy.invokeSuper(obj, args); // 调用目标对象的方法
                System.out.println("cglib方法调用后");
                return o;
            }
        }
    }
    

    测试代码:

    private static void testCglibProxy() {
      UserService userService = new UserServiceImpl();
      CglibProxyFactory cglibProxyFactory = new CglibProxyFactory();
      UserService proxy = (UserService) cglibProxyFactory.getProxy(userService);
      proxy.save("jack");
    }
    /* 运行结果:
      cglib方法调用前
      保存用户:jack
      cglib方法调用后
    */
    

    PS: 使用 CGLib 需要导入第三方 jar 包(cglib 和 asm)。

    实现原理可参考:https://blog.csdn.net/yhl_jxy/article/details/80633194

    二者主要区别:

    JDK 动态代理不依赖第三方库,CGLib 需要依赖第三方库;

    若目标对象实现了接口,两种方式都可以使用;若未实现接口,则只能使用 CGLib。

    1. 小结
      反射机制:简单来说,反射机制主要是通过 Class、Constructor 等"元类"来操作其他的普通类,以达到在运行期间动态创建对象、动态调用方法等目的。

    静态代理&动态代理:二者主要区别在于时机,静态代理 在编码 期已写好代理类,而动态代理则是在运行期间动态生成代理类。

    JDK 动态代理& CGLib 动态代理的主要区别:

    JDK 动态代理不依赖第三方库,CGLib 则要 依赖第三方 库 ;

    JDK 使用 InvocationHandler 接口实现增强逻辑, 使用 Proxy.newProxyInstance 生成代理对象;而 CGLib 使用 MethodInterceptor 接口实现增强逻辑,使用 Enhancer 生成代理对象;

    若目标对象实现了接口,两种方式都可以使用;若未实现接口,则只能使用 CGLib。

    原文链接: http://mp.weixin.qq.com/s?__biz=MzU4NzYyMDE4MQ==&mid=2247484102&idx=1&sn=f83d5843206cf73b5eaea533944ca558

    相关文章

      网友评论

        本文标题:Java反射机制与动态代理

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