Java 动态代理

写在前面

  • 本文中的内容非原创,主要来源见文末的参考链接,本人仅作整理工作,用以记录自己的学习过程,由于个人水平有限,故部分内容可能会出现错误,还请包涵
  • 本文可与《Spring AOP 解析》 对照理解

思维导图

为什么使用代理

  • 可以隐藏委托类的实现
  • 可以实现客户与委托类间的解耦,在不修改委托类的代码的情况下能够加入额外的处理

静态代理

  • 若代理类在编译期就已经确定,则称这种代理方式为静态代理
  • 通常情况下,代理类和委托类会实现统一接口或继承自相同的父类

具体使用

  • 定义一个接口和一个具体实现类
1
2
3
4
public interface IHelloWorld {
public void sayHello();
public void sayBye();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
public class HelloWorld implements IHelloWorld {

@Override
public void sayHello() {
System.out.println("Hello World");
}

@Override
public void sayBye() {
System.out.println("Bye");
}

}
  • 定义一个日志纪录类(用于功能加强)
1
2
3
4
5
6
7
8
9
10
public class Logger {

public static void startLog() {
System.out.println("Start Logging");
}

public static void endLog() {
System.out.println("End Logging");
}
}
  • 定义一个代理类,与委托类实现了同一接口
    • 代理对象持有一个实际对象的引用,外部调用时操作的是代理对象,而在代理对象的内部实现中又会去调用实际对象的操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class HelloWorldProxy implements IHelloWorld {

private HelloWorld hw;

public HelloWorldProxy(HelloWorld hw) {
this.hw = hw;
}

@Override
public void sayHello() {
Logger.startLog();
hw.sayHello();
Logger.endLog();
}

@Override
public void sayBye() {
Logger.startLog();
hw.sayBye();
Logger.endLog();
}

}

存在的问题

  • 在运行前必须编写好代理类
  • 需要在需要增加功能的方法中都要添加相应的功能,如上文的在调用 hw 的方法前后都需要加入日志执行的代码

动态代理

  • 在运行时动态地创建代理类
  • 可以很方便地对代理类中的函数进行统一的处理
  • 内部是通过 Java 反射机制来实现的

InvocationHandler

  • 接口中仅定义了一个方法
1
2
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
  • 其中 obj 一般是代理类,method 指被代理的方法,args 为该方法的参数数组

Proxy

  • static Class getProxyClass (ClassLoader loader, Class[] interfaces):获得一个代理类,其中 loader 是类装载器,interfaces 是委托类所拥有的全部接口的数组
  • static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作委托类使用
  • 对于从 Object 中继承的方法,JDK Proxy 会把 hashCode()、equals()、toString() 这三个非接口方法转发给 InvocationHandler,其余的 Object 方法则不会转发

具体使用

  • 如上文一样定义一个接口、一个具体实现类及一个日志记录类
  • 定义一个位于委托类代理类中间的中介类,要求实现 InvocationHandler接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class LoggerHandler implements InvocationHandler {

private Object target;

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

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Logger.startLog();
Object result = method.invoke(target, args);
Logger.endLog();
return result;
}

}
  • 定义一个测试类来查看效果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class TestProxy {

@Test
void test() {
IHelloWorld hw = new HelloWorld();
LoggerHandler handler = new LoggerHandler(hw);
IHelloWorld proxy = (IHelloWorld) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
hw.getClass().getInterfaces(), handler);
proxy.sayHello();
System.out.println("----------");
proxy.sayBye();
}

}

输出结果为:

1
2
3
4
5
6
7
Start Logging
Hello World
End Logging
----------
Start Logging
Bye
End Logging
  • 同样也可以对调用的方法进行不同的处理,如只有调用sayHello方法前才开始记录日志,只有调用sayBye方法后才结束日志
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class LoggerHandler implements InvocationHandler {

private Object target;

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

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("sayHello".equals(method.getName())) {
Logger.startLog();
}

Object result = method.invoke(target, args);

if ("sayBye".equals(method.getName())) {
Logger.endLog();
}
return result;
}

}

输出结果为:

1
2
3
4
5
Start Logging
Hello World
----------
Bye
End Logging
  • 从上文可以看出,中介类持有一个委托类对象引用,在 invoke 方法中调用了委托类对象的相应方法,即中介类与委托类构成了静态代理关系。在这个关系中,中介类是代理类,委托类就是委托类。同样地,代理类与中介类也构成 了一个静态代理关系,在这个关系中,中介类是委托类,代理类就是代理类。即,动态代理关系是由两组静态代理关系组成的

  • 上述的代码可以针对另外一个接口 MyInterface 和类 MyInterfaceImpl 做代理,可以对 LoggerHandler 这个类复用

1
2
3
4
5
MyInterface inter = new MyInterfaceImpl();
LoggerHandler handler = new LoggerHandler(inter);
MyInterface proxy = (MyInterface) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
inter.getClass().getInterfaces(), handler);
  • 若 MyInterface 和 IHelloWorld 实现自同一接口,那么上面的代码可以实现进一步抽象以应对更复杂的情况
  • 上述的 LoggerHandler 即相当于 Spring AOP 中的一个切面

具体实现

  • 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的 handler 对象的 invoke 方法来进行调用
  • Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)

    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
    @CallerSensitive   
    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);
    }
    //获取代理对象的构造方法(也就是$Proxy0(InvocationHandler h))
    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;
    }
    });
    }
    //生成代理类的实例并把InvocationHandlerImpl的实例传给它的构造方法
    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);
    }
    }
  • 查看 getProxyClass0 方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) {  
    if (interfaces.length > 65535) {
    throw new IllegalArgumentException("interface limit exceeded");
    }

    // If the proxy class defined by the given loader implementing
    // the given interfaces exists, this will simply return the cached copy;
    // otherwise, it will create the proxy class via the ProxyClassFactory
    return proxyClassCache.get(loader, interfaces);
    }
  • 查看 proClassCache

    1
    2
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>  
    proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
  • 使用了缓存,查看其对应的 get 方法

    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
    public V get(K key, P parameter) {  
    Objects.requireNonNull(parameter);

    expungeStaleEntries();

    Object cacheKey = CacheKey.valueOf(key, refQueue);

    // lazily install the 2nd level valuesMap for the particular cacheKey
    ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
    if (valuesMap == null) {
    //putIfAbsent这个方法在key不存在的时候加入一个值,如果key存在就不放入
    ConcurrentMap<Object, Supplier<V>> oldValuesMap
    = map.putIfAbsent(cacheKey,
    valuesMap = new ConcurrentHashMap<>());
    if (oldValuesMap != null) {
    valuesMap = oldValuesMap;
    }
    }

    // create subKey and retrieve the possible Supplier<V> stored by that
    // subKey from valuesMap
    Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
    Supplier<V> supplier = valuesMap.get(subKey);
    Factory factory = null;

    while (true) {
    if (supplier != null) {
    // supplier might be a Factory or a CacheValue<V> instance
    V value = supplier.get();
    if (value != null) {
    return value;
    }
    }
    // else no supplier in cache
    // or a supplier that returned null (could be a cleared CacheValue
    // or a Factory that wasn't successful in installing the CacheValue)

    // lazily construct a Factory
    if (factory == null) {
    factory = new Factory(key, parameter, subKey, valuesMap);
    }

    if (supplier == null) {
    supplier = valuesMap.putIfAbsent(subKey, factory);
    if (supplier == null) {
    // successfully installed Factory
    supplier = factory;
    }
    // else retry with winning supplier
    } else {
    if (valuesMap.replace(subKey, supplier, factory)) {
    // successfully replaced
    // cleared CacheEntry / unsuccessful Factory
    // with our Factory
    supplier = factory;
    } else {
    // retry with current supplier
    supplier = valuesMap.get(subKey);
    }
    }
    }
    }
  • 调用了supplier.get() 来获取动态代理类,其中 supplier 是 Factory,该类定义在 WeakCache 内部

    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
    public synchronized V get() { // serialize access  
    // re-check
    Supplier<V> supplier = valuesMap.get(subKey);
    if (supplier != this) {
    // something changed while we were waiting:
    // might be that we were replaced by a CacheValue
    // or were removed because of failure ->
    // return null to signal WeakCache.get() to retry
    // the loop
    return null;
    }
    // else still us (supplier == this)

    // create new value
    V value = null;
    try {
    value = Objects.requireNonNull(valueFactory.apply(key, parameter));
    } finally {
    if (value == null) { // remove us on failure
    valuesMap.remove(subKey, this);
    }
    }
    // the only path to reach here is with non-null value
    assert value != null;

    // wrap value with CacheValue (WeakReference)
    CacheValue<V> cacheValue = new CacheValue<>(value);

    // try replacing us with CacheValue (this should always succeed)
    if (valuesMap.replace(subKey, this, cacheValue)) {
    // put also in reverseMap
    reverseMap.put(cacheValue, Boolean.TRUE);
    } else {
    throw new AssertionError("Should not reach here");
    }

    // successfully replaced us with new CacheValue -> return the value
    // wrapped by it
    return value;
    }
    }
  • 其中调用了 valueFactory.apply(key, parameter) 方法

    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
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    private static final class ProxyClassFactory  
    implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
    // prefix for all proxy class names
    private static final String proxyClassNamePrefix = "$Proxy";

    // next number to use for generation of unique proxy class names
    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) {
    /*
    * Verify that the class loader resolves the name of this
    * interface to the same Class object.
    */
    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");
    }
    /*
    * Verify that the Class object actually represents an
    * interface.
    */
    if (!interfaceClass.isInterface()) {
    throw new IllegalArgumentException(
    interfaceClass.getName() + " is not an interface");
    }
    /*
    * Verify that this interface is not a duplicate.
    */
    if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
    throw new IllegalArgumentException(
    "repeated interface: " + interfaceClass.getName());
    }
    }

    String proxyPkg = null; // package to define proxy class in
    int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

    /*
    * Record the package of a non-public proxy interface so that the
    * proxy class will be defined in the same package. Verify that
    * all non-public proxy interfaces are in the same package.
    */
    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");
    }
    }
    }

    if (proxyPkg == null) {
    // if no non-public proxy interfaces, use com.sun.proxy package
    proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
    }

    /*
    * Choose a name for the proxy class to generate.
    */
    long num = nextUniqueNumber.getAndIncrement();
    String proxyName = proxyPkg + proxyClassNamePrefix + num;

    /*
    * Generate the specified proxy class.
    */
    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
    proxyName, interfaces, accessFlags);
    try {
    return defineClass0(loader, proxyName,
    proxyClassFile, 0, proxyClassFile.length);
    } catch (ClassFormatError e) {
    /*
    * A ClassFormatError here means that (barring bugs in the
    * proxy class generation code) there was some other
    * invalid aspect of the arguments supplied to the proxy
    * class creation (such as virtual machine limitations
    * exceeded).
    */
    throw new IllegalArgumentException(e.toString());
    }
    }
    }
  • 重点在于 byte[] proxyClassFile

    1
    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
  • 将该字节码保存到本地

    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
    public class DynamicProxyDemonstration  
    {
    public static void main(String[] args)
    {
    //代理的真实对象
    IHelloWorld hw = new HelloWorld();

    /**
    * InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
    * 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用.
    * 即:要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法
    */
    InvocationHandler handler = new LoggerHandler(hw);
    ClassLoader loader = handler.getClass().getContextClassLoader();
    Class[] interfaces = hw.getClass().getInterfaces();

    /**
    * 该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
    */
    IHelloWorld proxy = (IHelloWorld) Proxy.newProxyInstance(loader, interfaces, handler);

    proxy.sayHello();
    proxy.sayBye();

    // 将生成的字节码保存到本地,
    createProxyClassFile();
    }

    private static void createProxyClassFile(){
    String name = "ProxySubject";
    byte[] data = ProxyGenerator.generateProxyClass(name,new Class[]{IHelloWorld.class});
    FileOutputStream out =null;
    try {
    out = new FileOutputStream(name+".class");
    out.write(data);
    out.flush();
    } catch (FileNotFoundException e) {
    e.printStackTrace();
    } catch (IOException e) {
    e.printStackTrace();
    }finally {
    if(null!=out) try {
    out.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }

    }
  • 利用 jd-jui 工具可将生成的字节码反编译

    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
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    public final class ProxySubject  
    extends Proxy
    implements IHelloWorld
    {
    private static Method m1;
    private static Method m3;
    private static Method m4;
    private static Method m2;
    private static Method m0;

    public ProxySubject(InvocationHandler paramInvocationHandler)
    {
    super(paramInvocationHandler);
    }

    public final boolean equals(Object paramObject)
    {
    try
    {
    return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (Error|RuntimeException localError)
    {
    throw localError;
    }
    catch (Throwable localThrowable)
    {
    throw new UndeclaredThrowableException(localThrowable);
    }
    }

    public final String sayBye()
    {
    try
    {
    this.h.invoke(this, m3, null);
    return;
    }
    catch (Error|RuntimeException localError)
    {
    throw localError;
    }
    catch (Throwable localThrowable)
    {
    throw new UndeclaredThrowableException(localThrowable);
    }
    }

    public final String sayHello(String paramString)
    {
    try
    {
    this.h.invoke(this, m4, null);
    return;
    }
    catch (Error|RuntimeException localError)
    {
    throw localError;
    }
    catch (Throwable localThrowable)
    {
    throw new UndeclaredThrowableException(localThrowable);
    }
    }

    public final String toString()
    {
    try
    {
    return (String)this.h.invoke(this, m2, null);
    }
    catch (Error|RuntimeException localError)
    {
    throw localError;
    }
    catch (Throwable localThrowable)
    {
    throw new UndeclaredThrowableException(localThrowable);
    }
    }

    public final int hashCode()
    {
    try
    {
    return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (Error|RuntimeException localError)
    {
    throw localError;
    }
    catch (Throwable localThrowable)
    {
    throw new UndeclaredThrowableException(localThrowable);
    }
    }

    static
    {
    try
    {
    m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
    m3 = Class.forName("proxy.IHelloWorld").getMethod("sayBye", new Class[0]);
    m4 = Class.forName("proxy.IHelloWorld").getMethod("sayHello", new Class[0]);
    m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
    m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
    return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
    throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
    throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
    }
    }
  • 故执行 proxy.sayHello() 时实际上调用的是

    1
    this.h.invoke(this, m3, null);

总结

  • 实现 Java 动态代理的前提是委托类必须是实现了接口的类
  • Java 动态代理只能代理实现了接口的类的原因是由 Java 自动生成的代理类继承自 Proxy,而 Java 是不允许多继承
  • 实现 Java 动态代理的具体步骤:
    • 通过实现 InvocationHandler 接口创建自己的调用处理器
    • 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类
    • 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型
    • 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入

参考链接