likes
comments
collection
share

Java 学习 - JDK8 动态代理

作者站长头像
站长
· 阅读数 1

前言

有了静态代理,为什么 Java 还需要动态代理呢?为了解决静态代理存在的以下缺点:

1、代理类和被代理的真实类要实现相同的接口,如果接口增加一个方法,两者都要做修改,不满足“开闭原则”;

2、一个代理类只服务于一个接口/真实类,如果要服务多个接口/真实类,就需要多个代理类;

为了解决这两个问题,动态代理把“代理类”与“接口/真实类”解耦,不要求代理类实现接口,而是在运行时动态地创建代理类对象。

代理模式的组成如图1所示:

Java 学习 - JDK8 动态代理

图1. 代理模式的组成

Client 实际调用的事 Proxy 对象,Proxy 对象持有 RealSubject 引用并且具有 RealSubject 的同名方法。静态代理是预编译了 Proxy 类,动态代理是在运行时创建 Proxy 类对象。

JDK动态代理怎么实现动态地创建Proxy对象?

Java对象都是由JVM根据编译完成的 .class 文件创建的,动态代理要能够动态创建 Proxy 对象且持有 RealSubject 引用,需要完成以下几件事:

1、创建 Proxy 的 .class 文件,加载到 JVM 中,并创建 Class 对象

2、根据 Proxy 的 Class 对象,调用构造器创建 Proxy 对象实例,并具有 RealSubject 的同名方法

3、Proxy 对象直接或间接持有 RealSubject 对象的引用,在调用同名方法时能够调用 RealSubject 的方法

示例代码

1、被代理的接口和实现类

// 被代理的接口
public interface UserService {

    /**
     * 根据ID查询用户对象
     * @param id 用户ID
     * @return User
     */
    User getUserById(Long id);
}

// 被代理的实现类
@Slf4j
public class UserServiceImpl implements UserService {

    @Override
    public User getUserById(Long id) {
        User user = new User().setId(id);
        log.info("user ===== {}", user);
        return user;
    }
}

2、InvocationHandler 接口的实现类

在JDK 8动态代理中,Proxy 需要实现 InvocationHandler 接口。

@Slf4j
public class UserInvokeHandler implements InvocationHandler {
	// 被代理的对象
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log.info("before getUserById === class: {}", this.getClass().getName());
        Object result = method.invoke(target, args);
        log.info("after getUserById === class: {}", this.getClass().getName());
        return result;
    }
}

3、使用 Proxy.newProxyInstance() 方法创建代理类对象

public static void main(String[] args) {
    UserInvokeHandler handler = new UserInvokeHandler(new UserServiceImpl());

    UserService userService = (UserService) Proxy.newProxyInstance(
        handler.getClass().getClassLoader(), 
        new Class[]{UserService.class},
        handler);
    userService.getUserById(200L);
    System.out.println("userService ==== " + userService.getClass());
}

打印结果如下:

21:34:24.718 [main]UserInvokeHandler - before getUserById === class: UserInvokeHandler
21:34:24.718 [main]UserServiceImpl - user ===== User(id=200)
21:34:24.718 [main]UserInvokeHandler - after getUserById === class: UserInvokeHandler
userService ==== class com.sun.proxy.$Proxy3

示例代码中主要有3个步骤:

1、创建需要被代理的接口和实现类

2、创建 InvocationHandler 接口的实现类

3、使用 Proxy.newProxyInstance() 方法创建代理类对象

调用代理对象的方法时,会调用 handler 的 invoke() 方法,并调用 被代理真实类 的同名方法,最后打印的“class com.sun.proxy.$Proxy3”是代理的类名。

下面,我们通过 Proxy.newProxyInstance() 方法的源码,来学习JDK的动态代理过程。

InvocationHandler

在JDK动态代理中,因为 Proxy 对象是动态创建,并不直接持有 RealSubject 的引用,所以让 InvocationHandler 接口的某个实现类持有 RealSubject 引用,从而让 Proxy 持有 RealSubject 的引用。InvocationHandler 接口只有一个方法:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

该方法有三个参数:

  • Object proxy:代理类对象
  • Method method:被代理的真实类(RealSubject)的方法
  • Object[] args:被代理的真实类(RealSubject)方法(Method)的参数

在 InvocationHandler 接口的实现类中,通过实现 invoke 方法对 RealSubject 方法的代理和拦截。

Proxy

这里的 Proxy 类与前面的 代理类(Proxy)含义不同,下面为了避免混淆,下文的 Proxy 类都表示 Java 中的类,前面图1所示的 Proxy 类统一用“代理类”表示

Proxy.newProxyInstance() 方法负责在运行时创建 .class 文件和 class 对象:

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

方法有三个参数,含义如下:

  • ClassLoader loader:类加载器,由哪个加载器来加载“代理类”
  • Class<?>[] interfaces:一组接口,表示“代理类”要实现的接口
  • InvocationHandler h:InvocationHandler 接口的一个具体实现类对象

下面介绍这个方法的源码。

newProxyInstance()

该方法主要分为3步:

1、进行一些必要的安全性检查,

2、创建“代理类”的 class 对象

3、传入 InvocationHandler 参数,使用 class 对象返回构造器,创建代理类的对象实例

源码如下:

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h) throws IllegalArgumentException {
    // 1、必要的检查
    Objects.requireNonNull(h);

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

    // 2、生成所需的代理类 class 对象
    Class<?> cl = getProxyClass0(loader, intfs);

    try {
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }

        // 代理类的构造器
        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;
                }
            });
        }
        // 创建代理对象,传入了 InvocationHandler 对象作为参数
        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);
    }
}

最关键的是在第2步,查找和创建代理类的 class 对象:

1、可以猜测在 **getProxyClass0() **方法中,一定是先生成了 .class 字节码,再创建了 class 对象。

2、在运行时生成 .class 字节码和创建 class 对象,是很耗时的一个操作,最好的方法是先从缓存中查找,不存在时再去创建。

下面看一下 getProxyClass0() 方法的源码。

getProxyClass0()

该方法中只是从缓存对象 proxyClassCache 中取出 class 对象:

private static Class<?> getProxyClass0(ClassLoader loader,
                                       Class<?>... interfaces) {
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }

    // 根据加载器和 Class 从缓存中查找对应的 Class 对象
    return proxyClassCache.get(loader, interfaces);
}

proxyClassCache ****在 Proxy 中的定义如下,主要用来缓存 Class 对象:

private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
    proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

给 WeakCache 类的构造器,传入 KeyFactory、ProxyClassFactory 对象构造而成,下面介绍一下 WeakCache 类。

WeakCache

WeakCache 中关键字段如下:

// 存储数据的双层 map,结构为:key : map<subkey,value>,value 是一个 Supplier,用于创建 V 对象
private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map
    = new ConcurrentHashMap<>();
// subkey 的 factory
private final BiFunction<K, P, ?> subKeyFactory;
// value 的 factory
private final BiFunction<K, P, V> valueFactory;

proxyClassCache 的定义可知:

  • K 为 ClassLoader 对象 —— 类加载器
  • P 为 class[] —— 被代理的接口数组
  • V 为 class 对象对象 —— 最终的代理类

也就是通过“classLoader 和 被代理的目标接口数组”,生成一个最终的动态代理类。

proxyClassCache 使用 new KeyFactory()new ProxyClassFactory() 分别作为 subkeyFactory 和 valueFactory,这两个类都是 Proxy 的静态内部类,在下面有介绍。

get()

在前面的 getProxyClass0() 方法中,调用了 proxyClassCache 的 get(K key, P parameter) 方法,方法的源码如下:

public V get(K key, P parameter) {
    // 简单的检查和清理
    Objects.requireNonNull(parameter);

    expungeStaleEntries();

    // 查找内层的 map,如果不存在就创建一个
    Object cacheKey = CacheKey.valueOf(key, refQueue);

    ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
    if (valuesMap == null) {
        ConcurrentMap<Object, Supplier<V>> oldValuesMap
            = map.putIfAbsent(cacheKey,
                              valuesMap = new ConcurrentHashMap<>());
        if (oldValuesMap != null) {
            valuesMap = oldValuesMap;
        }
    }

    // 创建 subKey,找到对应的 supplier
    Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
    Supplier<V> supplier = valuesMap.get(subKey);
    Factory factory = null;

    // while(true) 循环,第一次进入的时候 supplier 为 null,第二次循环后,设置为 factory 对象,不再为 null,创建 V 值
    while (true) {
        // 如果 supplier 不为 null,创建一个 value,直接返回
        if (supplier != null) {
            V value = supplier.get();
            if (value != null) {
                return value;
            }
        }
        // 如果 factory 为 null,就创建一个新的 factory 来代替 supplier
        if (factory == null) {
            factory = new Factory(key, parameter, subKey, valuesMap);
        }

        if (supplier == null) {
            supplier = valuesMap.putIfAbsent(subKey, factory);
            if (supplier == null) {
                supplier = factory;
            }
        } else {
            if (valuesMap.replace(subKey, supplier, factory)) {
                supplier = factory;
            } else {
                supplier = valuesMap.get(subKey);
            }
        }
    }
}

这个方法在一个 while(true) 循环中,调用 WeakCache 内部静态类 Factory 的 get 方法,最终返回要生成的代理对象。

#Factory#get()

Factory类的 get() 方法如下:

Factory(K key, P parameter, Object subKey,
        ConcurrentMap<Object, Supplier<V>> valuesMap) {
    this.key = key; // classLoader
    this.parameter = parameter; 
    this.subKey = subKey; // subKey
    this.valuesMap = valuesMap; // 外部类中内嵌的 map
}

@Override
public synchronized V get() { // serialize access
    // 再次检查
    Supplier<V> supplier = valuesMap.get(subKey);
    if (supplier != this) {
        return null;
    }

    // 调用 valueFactory 来创建 V 值
    V value = null;
    try {
        value = Objects.requireNonNull(valueFactory.apply(key, parameter));
    } finally {
        // 如果失败,就移除 subKey
        if (value == null) {
            valuesMap.remove(subKey, this);
        }
    }
    // 检查
    assert value != null;

    // 把 V 值包装成 弱引用,然后添加进 valuesMap
    CacheValue<V> cacheValue = new CacheValue<>(value);

    reverseMap.put(cacheValue, Boolean.TRUE);

    if (!valuesMap.replace(subKey, this, cacheValue)) {
        throw new AssertionError("Should not reach here");
    }

    return value;
}

这里生成的最终结果,是一个弱引用,目的应该是为了在下一次 GC 的时候都回收掉,避免太多的动态代理类占用内存空间。

从前面 proxyClassCache 的定义可知,valueFactory 实际是 Proxy#ProxyClassFactory 类,查看它和 KeyFactory 的源码。

Proxy#KeyFactory

Proxy#KeyFactory 的作用是把传入的 ClassLoader 和 接口数组 生成一个 key,也就是作为 proxyClassCache 的的 subKey。

private static final class KeyFactory
    implements BiFunction<ClassLoader, Class<?>[], Object> {
    @Override
    public Object apply(ClassLoader classLoader, Class<?>[] interfaces) {
        switch (interfaces.length) {
            case 1: return new Key1(interfaces[0]); // the most frequent
            case 2: return new Key2(interfaces[0], interfaces[1]);
            case 0: return key0;
            default: return new KeyX(interfaces);
        }
    }
}

Proxy#ProxyClassFactory

Proxy#ProxyClassFactory 用于生成“代理类”的 class 对象:

private static final class ProxyClassFactory
    implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
    // 动态代理类的前缀
    private static final String proxyClassNamePrefix = "$Proxy";

    // 动态代理类的后缀
    private static final AtomicLong nextUniqueNumber = new AtomicLong();

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

        Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
        // 检查接口数组,保存到一个 map 中,判断有效性:
        for (Class<?> intf : interfaces) {
            Class<?> interfaceClass = null;
            //  1、能够由当前类加载器加载
            try {
                interfaceClass = Class.forName(intf.getName(), false, loader);
            } catch (ClassNotFoundException e) {
            }
            if (interfaceClass != intf) {
                throw new IllegalArgumentException(
                    intf + " is not visible from class loader");
            }
            // 2、是一个接口
            if (!interfaceClass.isInterface()) {
                throw new IllegalArgumentException(
                    interfaceClass.getName() + " is not an interface");
            }
            // 3、没有重复
            if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                throw new IllegalArgumentException(
                    "repeated interface: " + interfaceClass.getName());
            }
        }

        // 遍历传入的接口数组,判断是否有多个 non-public 接口来自不同的 package
        // 要求 non-public 的接口必须来自同一个 package
        String proxyPkg = null;
        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(0, n + 1));
                if (proxyPkg == null) {
                    proxyPkg = pkg;
                } else if (!pkg.equals(proxyPkg)) {
                    throw new IllegalArgumentException(
                        "non-public interfaces from different packages");
                }
            }
        }

        // 没有 non-public 接口,使用默认的 package 路径
        if (proxyPkg == null) {
            proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
        }

        // 生成代理类的类名
        long num = nextUniqueNumber.getAndIncrement();
        String proxyName = proxyPkg + proxyClassNamePrefix + num;

        // 生成所需的代理类的字节码
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
            proxyName, interfaces, accessFlags);
        try {
            // 加载 class 字节码
            return defineClass0(loader, proxyName,
                                proxyClassFile, 0, proxyClassFile.length);
        } catch (ClassFormatError e) {
            throw new IllegalArgumentException(e.toString());
        }
    }
}

经过上述过程之后,最终返回到 Proxy.newProxyInstance() 方法中,生成一个 Class 对象,然后再调用构造器创建对象,最终创建出代理类对象。

代理类字节码

我们知道了创建代理类对象的过程,符合我们之前的假设步骤类似:创建 .class 文件字节码、生成 class 文件、调用构造器创建对象。

还有一个问题没有解决: 代理类是怎么持有 RealSubject 的引用,从而能够调用同名方法的

在前面的“示例代码”中,只有在创建 InvocationHandler 接口的实现类对象时,才传入 RealSubject 对象,而 InvocationHandler 实例对象在创建“代理类”对象时作为参数传入,所以“代理类”对象应该是通过 InvocationHandler 实例来间接持有对 RealSubject 的引用

给 UserService 新增一个方法 getUserName(),把生成的“代理类”字节码文件输出到硬盘上,查看生成的 .class 文件如下:

// 生成 .class 文件
String path = "D:/$Proxy0.class";
System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");

byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", UserServiceImpl.class.getInterfaces());
FileOutputStream out = null;

try {
    out = new FileOutputStream(path);
    out.write(classFile);
    out.flush();
} catch (Exception e) {
    e.printStackTrace();
} finally {
    try {
        out.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
// .class 文件内容
public final class $Proxy0 extends Proxy implements UserService {

    private static Method m4;
    private static Method m3;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }
    
    // 省略 equals\toString\hashCode 方法
    public final User getUserById(Long var1) throws  {
        try {
            return (User)super.h.invoke(this, m4, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String getUserName() throws  {
        try {
            return (String)super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            // ...
            m4 = Class.forName("xxx.UserService").getMethod("getUserById", Class.forName("java.lang.Long"));
            // ...
            m3 = Class.forName("xxx.UserService").getMethod("getUserName");
            // ...
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

从“代理类”的 .class 文件中可以看出:

1、“代理类”是 Proxy 的子类、实现了被代理的接口 UserService,并通过反射实现了接口中所有方法;

2、在调用方法时,实际上调用了 InvocationHandler 对象的 invoke 方法,在 invoke() 方法中调用了 InvocationHandler 持有的 RealSubject 同名方法

因此,只需要在 invoke 方法中实现额外逻辑,就可以实现对 RealSubject 对象方法的控制访问。

总结

动态代理中各个类之间的关系如下图所示:

Java 学习 - JDK8 动态代理

在上面的UML图中:

  • WeakCache 的 get() 方法会取出 map 中内层的 Supplier,调用 Supplier 的 get() 方法
  • 该 Supplier 的具体实现类是 WeakCache#Factory,它的 get() 方法又会调用 WeakCache.valueFactory 属性的 apply() 方法
  • WeakCache.valueFactory 的具体实现是 Proxy#ProxyClassFactory,该实例是在 Proxy 初始化 proxyClassCache 属性时作为参数传入的
  • Proxy#ProxyClassFactory 的 get 方法生成代理类的字节码,并载入到JVM中,然后返回 Class 对象

整个调用时序如下图所示:

Java 学习 - JDK8 动态代理

动态代理的注意点

  • 生成的代理类为 public final,不能被继承
  • 类名格式是“$ProxyN”,N表示 Proxy 第N次生成代理类
  • 对于同一组接口(接口相同、顺序相同),不会重复创建动态代理类,而是返回一个先前已经创建并缓存了的代理类对象,提高了效率
  • java.lang.Object 中有三个方法也同样会被分派到handler的 invoke 方法执行,它们是 hashCode,equals 和 toString
  • 因为是在运行时创建 .class 字节码和 class 对象,所以性能要比静态代理要差
  • 为什么只能实现接口的动态代理?因为传入的目标代理类是一个 class 数组,java 不支持多继承,只支持多重实现,所以只能代理接口。