这一次彻底搞懂JDK动态代理

开发 前端
静态代理这个模式本身有个大问题,若类方法数量越来越多的时候,代理类的代码量十分庞大的。所以引入动态代理.

[[409031]]

动态代理 V.S 静态代理

Proxy类的代码被固定下来,不会因为业务的逐渐庞大而庞大

可以实现AOP编程,这是静态代理无法实现的

解耦,如果用在web业务下,可以实现数据层和业务层的分离

动态代理的优势就是实现无侵入式的代码扩展。

静态代理这个模式本身有个大问题,若类方法数量越来越多的时候,代理类的代码量十分庞大的。所以引入动态代理

动态代理

Java中动态代理的实现的关键:

  • Proxy
  • InvocationHandler

InvocationHandler#invoke

  • method 调用的方法,即需要执行的方法
  • args 方法的参数
  • proxy 代理类的实例 图片

JDK动态代理

JDK动态代理模式里有个拦截器,在JDK中,只要实现了InvocationHandler接口的类就是一个拦截器类。拦截器的作用:控制目标对象的目标方法的执行。

拦截器的具体操作步骤:

1.引入类

目标类和一些扩展方法相关的类

2.赋值

调用构造器,给相关对象赋值

3.合并逻辑处理

在invoke方法中把所有的逻辑结合在一起。最终决定目标方法是否被调用

示例

思考如下问题:

代理对象由谁产生

JVM,不像静态代理,我们得自己new个代理对象。

代理对象实现了什么接口

实现的接口是目标对象实现的接口。同静态代理中代理对象实现的接口。那个继承关系图还是相同的。代理对象和目标对象都实现一个共同的接口。就是这个接口。所以Proxy.newProxyInstance()方法返回的类型就是这个接口类型。

代理对象的方法体是什么

代理对象的方法体中的内容就是拦截器中invoke方法中的内容。

所有代理对象的处理逻辑,控制是否执行目标对象的目标方法。都是在这个方法里面处理的。

拦截器中的invoke方法中的method参数是在什么时候赋值的

在客户端,代理对象调用目标方法的时候,此实例中为:

proxyObj.business(); 
  • 1.

实际上进入的是拦截器中的invoke方法,这时拦截器中的invoke方法中的method参数会被赋值。

为啥这叫JDK动态代理

因为该动态代理对象是用JDK相关代码生成。

很多同学对动态代理一直很迷糊,在于理解错了

proxyObj.business(); 
$Proxy0 
  • 1.
  • 2.

没有发现这个 proxyObj 和 Proxy 类之间的联系,一直好奇

最后调用的business()是怎么和 invoke() 联系上的?

invoke又怎么知道business的存在?

因为大多同学不知道 $Proxy0 类,看看下面的 $Proxy0 源码,相信你完全可以理解动态代理了。

我们虽然没有显式调用invoke,但该方法确实被执行了。

可以从newProxyInstance方法作为突破口,我们先来看一下Proxy类中newProxyInstance方法的源代码:

public static Object newProxyInstance(ClassLoader loader, 
                                      Class<?>[] interfaces, 
                                      InvocationHandler h) { 
    final Class<?>[] intfs = interfaces.clone(); 
    final SecurityManager sm = System.getSecurityManager(); 
    if (sm != null) { 
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs); 
    } 
 
    /* 
     * 查找或生成指定的代理类 
     * 创建代理类$Proxy0 
     * $Proxy0类实现了interfaces的接口,并继承了Proxy类 
     */ 
    Class<?> cl = getProxyClass0(loader, intfs); 
 
    /* 
     * 使用指定的调用处理程序调用其构造器 
     */ 
    try { 
        if (sm != null) { 
            checkNewProxyPermission(Reflection.getCallerClass(), cl); 
        } 
        // 形参为InvocationHandler类型的构造器 
        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
                } 
            }); 
        } 
        return cons.newInstance(new Object[]{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.

Proxy.newProxyInstance 做了什么呢?

  • 根据参数loader和interfaces调用方法 getProxyClass(loader, interfaces)创建代理$Proxy0类。$Proxy0类 实现了interfaces的接口,并继承了Proxy类
  • 实例化$Proxy0,并在构造器把DynamicSubject传过去,接着$Proxy00调用父类Proxy的构造器,为h赋值

$Proxy0的源码:

package com.sun.proxy; 
 
public final class $Proxy0 extends Proxy implements TargetInterface { 
    private static Method m1; 
    private static Method m3; 
    private static Method m2; 
    private static Method m0; 
 
    public $Proxy0(InvocationHandler var1) throws  { 
        super(var1); 
    } 
 
    public final boolean equals(Object var1) throws  { 
        try { 
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); 
        }... 
    } 
 
    public final void business() throws  { 
        try { 
            super.h.invoke(this, m3, (Object[])null); 
        }... 
    } 
 
    public final String toString() throws  { 
        try { 
            return (String)super.h.invoke(this, m2, (Object[])null); 
        }... 
    } 
 
    public final int hashCode() throws  { 
        try { 
            return (Integer)super.h.invoke(this, m0, (Object[])null); 
        }... 
    } 
 
    static { 
        try { 
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); 
            m3 = Class.forName("com.javaedge.design.pattern.structural.proxy.dynamicproxy.jdkdynamicproxy.TargetInterface").getMethod("business"); 
            m2 = Class.forName("java.lang.Object").getMethod("toString"); 
            m0 = Class.forName("java.lang.Object").getMethod("hashCode"); 
        }... 
    } 

  • 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.

接着把得到的$Proxy0实例强转成TargetInterface,并将引用赋给TargetInterface。当执行proxyObj.business(),就调用了$Proxy0类中的business()方法,进而调用父类Proxy中的h的invoke()方法。即InvocationHandler.invoke()。

最后提醒Proxy#getProxyClass返回的是Proxy的Class类,而非很同学想当然认为的“被代理类的Class类”! 

本文转载自微信公众号「JavaEdge」,可以通过以下二维码关注。转载本文请联系JavaEdge公众号。

 

责任编辑:武晓燕 来源: JavaEdge
相关推荐

2021-08-29 08:14:30

GPU CSS gpu

2024-05-15 10:14:00

CRDT数据类型协同编辑

2019-11-08 16:05:54

Promise前端链式调用

2024-03-11 08:47:30

CRDT数据类型协同编辑

2019-09-12 09:40:34

秒杀系统高并发

2018-08-07 14:45:52

编程语言JavaScripthtml

2021-03-11 12:15:37

Kubernetes云原生容器

2021-04-28 09:55:52

JavaLock接口并发编程

2020-08-13 07:04:45

跨域CORS浏览器

2019-06-05 13:00:00

2016-03-31 17:01:26

桂林甲天下

2018-07-23 16:13:27

Google欧盟Android

2024-05-20 00:00:00

代码主线程

2024-10-09 12:05:27

2025-04-09 10:36:32

2019-04-12 11:25:24

华为

2014-07-18 17:14:16

小米苹果雷军

2016-11-08 07:58:02

乐视难关科技新闻早报

2016-01-06 11:15:03

VR

2019-11-05 11:17:11

Java虚拟机技术Java 堆
点赞
收藏

51CTO技术栈公众号