本系列文章首发于我的个人博客:https://h2pl.github.io/

欢迎阅览我的CSDN专栏:Spring源码解析 https://blog.csdn.net/column/details/21851.html

部分代码会放在我的的Github:https://github.com/h2pl/

 

 

AOP的基础是Java动态代理,了解和使用两种动态代理能让我们更好地理解 AOP,在讲解AOP之前,让我们先来看看Java动态代理的使用方式以及底层实现原理。

转自https://www.jianshu.com/u/668d0795a95b

本文是基于jdk1.8来对动态代理的底层机制进行探究的

Java代理介绍

Java中代理的实现一般分为三种:JDK静态代理、JDK动态代理以及CGLIB动态代理。在Spring的AOP实现中,主要应用了JDK动态代理以及CGLIB动态代理。但是本文着重介绍JDK动态代理机制,CGLIB动态代理后面会接着探究。

代理一般实现的模式为JDK静态代理:创建一个接口,然后创建被代理的类实现该接口并且实现该接口中的抽象方法。之后再创建一个代理类,同时使其也实现这个接口。在代理类中持有一个被代理对象的引用,而后在代理类方法中调用该对象的方法。

其实就是代理类为被代理类预处理消息、过滤消息并在此之后将消息转发给被代理类,之后还能进行消息的后置处理。代理类和被代理类通常会存在关联关系(即上面提到的持有的被带离对象的引用),代理类本身不实现服务,而是通过调用被代理类中的方法来提供服务。

静态代理

JDK和cglib动态代理原理

接口

JDK和cglib动态代理原理

被代理类

JDK和cglib动态代理原理

代理类

JDK和cglib动态代理原理

测试类以及输出结果

我们可以看出,使用JDK静态代理很容易就完成了对一个类的代理操作。但是JDK静态代理的缺点也暴露了出来:由于代理只能为一个类服务,如果需要代理的类很多,那么就需要编写大量的代理类,比较繁琐。

下面我们使用JDK动态代理来做同样的事情

JDK动态代理

JDK和cglib动态代理原理

接口

JDK和cglib动态代理原理

被代理类

JDK和cglib动态代理原理

代理类

JDK和cglib动态代理原理

测试类以及输出结果

JDK动态代理实现原理

JDK动态代理其实也是基本接口实现的。因为通过接口指向实现类实例的多态方式,可以有效地将具体实现与调用解耦,便于后期的修改和维护。

通过上面的介绍,我们可以发现JDK静态代理与JDK动态代理之间有些许相似,比如说都要创建代理类,以及代理类都要实现接口等。但是不同之处也非常明显—-在静态代理中我们需要对哪个接口和哪个被代理类创建代理类,所以我们在编译前就需要代理类实现与被代理类相同的接口,并且直接在实现的方法中调用被代理类相应的方法;但是动态代理则不同,我们不知道要针对哪个接口、哪个被代理类创建代理类,因为它是在运行时被创建的。

让我们用一句话来总结一下JDK静态代理和JDK动态代理的区别,然后开始探究JDK动态代理的底层实现机制: JDK静态代理是通过直接编码创建的,而JDK动态代理是利用反射机制在运行时创建代理类的。 其实在动态代理中,核心是InvocationHandler。每一个代理的实例都会有一个关联的调用处理程序(InvocationHandler)。对待代理实例进行调用时,将对方法的调用进行编码并指派到它的调用处理器(InvocationHandler)的invoke方法。所以对代理对象实例方法的调用都是通过InvocationHandler中的invoke方法来完成的,而invoke方法会根据传入的代理对象、方法名称以及参数决定调用代理的哪个方法。

我们从JDK动态代理的测试类中可以发现代理类生成是通过Proxy类中的newProxyInstance来完成的,下面我们进入这个函数看一看:

Proxy类中的newProxyInstance

public static Object newProxyInstance(ClassLoader loader,
                                        Class<?>[] interfaces,
                                        InvocationHandler h)
      throws IllegalArgumentException
  {
      //如果h为空将抛出异常
      Objects.requireNonNull(h);

      final Class<?>[] intfs = interfaces.clone();//拷贝被代理类实现的一些接口,用于后面权限方面的一些检查
      final SecurityManager sm = System.getSecurityManager();
      if (sm != null) {
          //在这里对某些安全权限进行检查,确保我们有权限对预期的被代理类进行代理
          checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
      }

      /*
       * 下面这个方法将产生代理类
       */
      Class<?> cl = getProxyClass0(loader, intfs);

      /*
       * 使用指定的调用处理程序获取代理类的构造函数对象
       */
      try {
          if (sm != null) {
              checkNewProxyPermission(Reflection.getCallerClass(), cl);
          }

          final Constructor<?> cons = cl.getConstructor(constructorParams);
          final InvocationHandler ih = h;
          //假如代理类的构造函数是private的,就使用反射来set accessible
          if (!Modifier.isPublic(cl.getModifiers())) {
              AccessController.doPrivileged(new PrivilegedAction<Void>() {
                  public Void run() {
                      cons.setAccessible(true);
                      return null;
                  }
              });
          }
          //根据代理类的构造函数来生成代理类的对象并返回
          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);
      }
  }

所以代理类其实是通过getProxyClass方法来生成的:

/**
   * 生成一个代理类,但是在调用本方法之前必须进行权限检查
   */
  private static Class<?> getProxyClass0(ClassLoader loader,
                                         Class<?>... interfaces) {
      //如果接口数量大于65535,抛出非法参数错误
      if (interfaces.length > 65535) {
          throw new IllegalArgumentException("interface limit exceeded");
      }

      // 如果在缓存中有对应的代理类,那么直接返回
      // 否则代理类将有 ProxyClassFactory 来创建
      return proxyClassCache.get(loader, interfaces);
  }

那么ProxyClassFactory是什么呢?

/**
 * 里面有一个根据给定ClassLoader和Interface来创建代理类的工厂函数  
 *
 */
private static final class ProxyClassFactory
    implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
    // 代理类的名字的前缀统一为“$Proxy”
    private static final String proxyClassNamePrefix = "$Proxy";

    // 每个代理类前缀后面都会跟着一个唯一的编号,如$Proxy0、$Proxy1、$Proxy2
    private static final AtomicLong nextUniqueNumber = new AtomicLong();

    @Override
    public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

        Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
        for (Class<?> intf : interfaces) {
            /*
             * 验证类加载器加载接口得到对象是否与由apply函数参数传入的对象相同
             */
            Class<?> interfaceClass = null;
            try {
                interfaceClass = Class.forName(intf.getName(), false, loader);
            } catch (ClassNotFoundException e) {
            }
            if (interfaceClass != intf) {
                throw new IllegalArgumentException(
                    intf + " is not visible from class loader");
            }
            /*
             * 验证这个Class对象是不是接口
             */
            if (!interfaceClass.isInterface()) {
                throw new IllegalArgumentException(
                    interfaceClass.getName() + " is not an interface");
            }
            /*
             * 验证这个接口是否重复
             */
            if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                throw new IllegalArgumentException(
                    "repeated interface: " + interfaceClass.getName());
            }
        }

        String proxyPkg = null;     // 声明代理类所在的package
        int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

        /*
         * 记录一个非公共代理接口的包,以便在同一个包中定义代理类。同时验证所有非公共
         * 代理接口都在同一个包中
         */
        for (Class<?> intf : interfaces) {
            int flags = intf.getModifiers();
            if (!Modifier.isPublic(flags)) {
                accessFlags = Modifier.FINAL;
                String name = intf.getName();
                int n = name.lastIndexOf('.');
                String pkg = ((n == -1) ? "" : name.substring(

相关文章: