03反射及动态代理

反射与动态代理

反射

  • 反射机制是 Java 语言提供的一种基础功能,赋予程序在运行时自省(introspect,官方用语)的能力。通过反射我们可以直接操作类或者对象,比如获取某个对象的类定义,获取类声明的属性和方法,调用方法或者构造对象,甚至可以运行时修改类定义
  • Java9中引入了所谓 Open 的概念,只有当被反射操作的模块和指定的包对反射调用者模块 Open,才能使用 setAccessible

静态代理

  • 通过手动编写代理类来增强或控制对目标对象的访问。静态代理的核心是代理类和目标类实现相同的接口

动态代理

  • 动态代理是一种方便运行时动态构建代理、动态处理代理方法调用的机制,很多场景都是利用类似机制做到的,比如用来包装 RPC 调用、面向切面的编程(AOP)

JDK动态代理

  • 动态代码生成发生在newProxyInstance 生成代理类实例的时候

  • 为什么只能对接口进行动态代理?

    • 因为代理类继承了Proxy类
    • 也可以绕过这一限制,在invoke方法中判断一下,如果接口类和当前类不一致,则通过下面代码获取meth 然后在调用
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      // TODO:扩展的新功能
      if (method.getDeclaringClass() != impl.getClass()) {
      // 找到相同签名的方法
      Method realMethod = impl.getClass().getDeclaredMethod(method.getName(), method.getParameterTypes());
      return realMethod.invoke(impl, args);
      }else{
      return method.invoke(impl,args);
      }
      }
  • 在newProxyInstance的过程中生成的代理类,需要调用的方法里直接调用了InvocationHandler的invoke方法。

  • 具体步骤

    • 获取 RealSubject 上的所有接口列表;
    • 确定要生成的代理类的类名,默认为:com.sun.proxy.$ProxyXXXX;
    • 根据需要实现的接口信息,在代码中动态创建 该 Proxy 类的字节码;
    • 将对应的字节码转换为对应的 class 对象;
    • 创建 InvocationHandler 实例 handler,用来处理 Proxy 所有方法调用;
    • Proxy 的 class 对象 以创建的 handler 对象为参数,实例化一个 proxy 对象。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      import java.lang.reflect.InvocationHandler;
      import java.lang.reflect.Method;
      import java.lang.reflect.Proxy;

      // 1. 定义业务接口
      interface UserService {
      void createUser(String username);
      void deleteUser(String username);
      }

      // 2. 实现业务接口(真实对象)
      class UserServiceImpl implements UserService {
      @Override
      public void createUser(String username) {
      System.out.println("【真实调用】创建用户: " + username);
      }

      @Override
      public void deleteUser(String username) {
      System.out.println("【真实调用】删除用户: " + username);
      }
      }

      // 3. 实现调用处理器(增强逻辑)
      class LoggingHandler implements InvocationHandler {
      private final Object target; // 被代理的真实对象

      public LoggingHandler(Object target) {
      this.target = target;
      }

      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      // 前置增强
      System.out.println("\n[日志] 开始执行方法: " + method.getName());
      System.out.println("[参数] " + (args != null ? String.join(", ", (CharSequence[]) args) : "无"));

      // 调用真实对象的方法
      Object result = method.invoke(target, args);

      // 后置增强
      System.out.println("[日志] 方法执行完成");

      // 返回调用结果(本例中方法返回void,此处返回null)
      return result;
      }
      }

      public class DynamicProxyDemo {
      public static void main(String[] args) {
      // 1. 创建真实对象
      UserService realService = new UserServiceImpl();

      // 2. 创建调用处理器(传入真实对象)
      InvocationHandler handler = new LoggingHandler(realService);

      // 3. 创建动态代理对象
      UserService proxy = (UserService) Proxy.newProxyInstance(
      realService.getClass().getClassLoader(), // 类加载器
      new Class<?>[] { UserService.class }, // 代理接口
      handler // 调用处理器
      );

      // 4. 通过代理对象调用方法
      proxy.createUser("张三");
      proxy.deleteUser("李四");

      // 5. 打印代理类信息
      System.out.println("\n代理类: " + proxy.getClass().getName());
      System.out.println("代理类的父类: " + proxy.getClass().getSuperclass().getName());
      System.out.println("代理类实现的接口: ");
      for (Class<?> intf : proxy.getClass().getInterfaces()) {
      System.out.println(" - " + intf.getName());
      }
      }
      }

CGlib

  • 主要没有JDK动态代理必须依赖接口的性质
  • 创建目标类的子类的方式,因为是子类化,我们可以达到近似使用被调用者本身的效果
  • 基于继承代理(字节码操作 + 子类继承)
  • 需要默认构造函数(子类化要求)
  • CGLib代理创建成本高,应缓存复用
  • 实现MethodInterceptor接口
  • 简单使用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    public static void main(String[] args) {
    // 实现了MethodInterceptor的类
    ServiceProxy serviceProxy = new ServiceProxy();
    Enhancer enhancer = new Enhancer();
    // 设置要代理的类
    enhancer.setSuperclass(UserService.class);
    enhancer.setCallback(new NoOp() {
    @Override
    public int hashCode() {
    // 生成的代理类使用原来的hashCode,确保不是同一个对象才初始化
    return super.hashCode();
    }
    });
    enhancer.setCallback(serviceProxy);
    enhancer.setCallbackFilter(new CallbackFilter() {
    @Override
    public int accept(Method method) {
    return 0;
    }
    });
    UserService userService = (UserService) enhancer.create();
    }

    public class ServiceProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

    System.out.println("here is interceptor1");

    return proxy.invokeSuper(obj,args);
    }
    }

callback

  • 这里的callback可以认为是cglib用于生成字节码的实现手段,cglib一共实现了6种callback,用于对代理类目标进行不同手段的代理,非常灵活,分别为:
    • FixedValue
    • InvocationHandler
    • LazyLoader
    • MethodInterceptor
    • Dispatcher
    • NoOp
Dispatcher
  • 实现Dispatcher接口,要求实现loadObject方法,返回期望的代理类。值的一提的是,loadobject方法在每次调用被拦截方法的时候都会被调用一次
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    /****
    * 与lazy不同的是,每一次调用代理方法的时候,都会调用一次Dispatcher的loadObject获取对象
    * 而lazy则会缓存下来。
    */
    public class DispatcherCallBack implements Dispatcher {
    public Object loadObject() throws Exception {
    CallbackBean callbackBean = new CallbackBean();
    return callbackBean;
    }
    }
LazyLoader
  • 实现LazyLoader的loadObject方法,返回对象实例,该实例只有第一次调用的时候进行初始化,之后不再重新调用,proxy类初始化时进行了成员的赋值,之后使用该成员进行调用父类方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /**
    *
    * 延迟加载初始化
    * 类似于spring prototype的singleton ,在第一次调用的时候进行初始化,并且将此实例存储起来,之后都将返回改实例
    * 可参考资料:
    * https://shensy.iteye.com/blog/1881277
    */
    public class LazyLoaderCallback implements LazyLoader {
    public Object loadObject() throws Exception {
    CallbackBean callbackBean = new CallbackBean();
    return callbackBean;
    }
    }
FixedValue
  • 实现FixedValue接口,该callback同样要求实现一个loadobject方法,只不过需要注意的是该loadobject方法相同与重写了被代理类的相应方法,因为在被代理之后,FixedValue callback只会调用loadobject,而不会再调用代理目标类的相应方法!
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    /**
    * 该callback相当于重写了相应的函数实现。并不会调用原函数
    */
    public class FixValueCallback implements FixedValue {
    /**
    * 被代理方法的指定函数将会无条件的返回改object,动态的变更返回值
    * @return
    * @throws Exception
    */
    public Object loadObject() throws Exception {
    System.out.println("this is fixvalue callback ..... overwrite the code....");
    return true;
    }
    }
InvocationHandler
  • 需要实现InvocationHandler接口,实现invoke对象,该拦截传入了proxy对象,用于自定义实现,与MethodInterceptor相似,慎用method的invoke方法。切忌不要造成循环调用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public class InvocationHandlerCallback implements InvocationHandler {
    /**
    * invocationHandler的invoke方法传入的method和proxy都是代理本身对象
    * 切忌重复调用,会循环调用
    * @param proxy 代理类本身
    * @param method 代理类内部的方法
    * @param args 参数
    * @return
    * @throws Throwable
    */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.println("invocationHandlerCallback Before....");
    method.invoke(proxy.getClass().getSuperclass().newInstance(),args);
    //会无限循环
    //method.invoke(proxy,args);
    System.out.println("invocationHandlerCallback after....");
    return null;
    }
    }
MethodInterceptor
  • 实现MethodInterceptor的intercept,实现被代理对象的逻辑植入。也是最常用的callback
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /**
    * 生成代理类字节码。对方法进行拦截调用
    */
    public class MethodInterceptorCallback implements MethodInterceptor {
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
    System.out.println("before invoke........");
    proxy.invokeSuper(obj,args);
    System.out.println("after invoke.........");
    return null;
    }
    }
NoOp
  • 通过接口声明了一个单例对象,该代理不对被代理类执行任何操作
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /**
    * Methods using this {@link Enhancer} callback will delegate directly to the
    * default (super) implementation in the base class.
    */
    public interface NoOp extends Callback
    {
    /**
    * A thread-safe singleton instance of the <code>NoOp</code> callback.
    */
    public static final NoOp INSTANCE = new NoOp() { };
    }

callBackFilter

  • 当传入多个callback类时,可以通过callBackFilter来选择使用哪个callback,当多个符合时,使用首个匹配的callback

AspectJ

  • Aspect Oriented Programming,可以插入字节码的位置
    • 在方法(包括构造方法)被调用的位置。
    • 在方法体(包括构造方法)的内部。
    • 在读写变量的位置。
    • 在静态代码块内部。
    • 在异常处理的位置的前后
    • 此外,它也可以直接将原位置的代码替换为自定义的代码。
  • 缺点:
    • 切入点固定
    • 正则表达式的局限性
    • 性能比较低

03反射及动态代理
https://x-leonidas.github.io/2022/02/01/04Java/java基础/03反射及动态代理/
作者
听风
发布于
2022年2月1日
更新于
2025年6月17日
许可协议