java多线程    Java入门    vsftp    ftp    linux配置    centos    FRP教程    HBase    Html5缓存    webp    zabbix    分布式    neo4j图数据库    

Java的动态代理和静态代理

问题:对Java接口代理模式的实现原理的理解

Java 代理分为静态代理和动态代理

静态代理

场景,我们买房子不是自己去买,而是中介帮忙处理

1.买房接口

package com.javaer.proxy;

public interface House {
    void buy();
}

2.买个一居室

package com.javaer.proxy;

public class House1 implements House{
    public void buy() {
        System.out.println("我成功购买了个一居室");
    }
}

3.代理帮忙处理

package com.javaer.proxy;

public class HouseProxy implements House{
    House house;
    public HouseProxy(House house){
        this.house = house;
    }
    public void buy() {
        System.out.println("------买房前申请房产审批");
        this.house.buy();
        System.out.println("------买房后拿到房本");
    }
    public static void main(String[] args) {
        House1 house1 = new House1();
        House house = new HouseProxy(house1);
        house.buy();
    }
}

代理买房子会帮忙处理买房前的事情和买房后的事情

代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。

如果要买个两居室搞个house2的类实现House接口就好了。

动态代理

为了避免混淆,我把动态代理的类放在com.javaer.dynimic下

买房

package com.javaer.dynamic;

public interface House {
    void buy();
}

买个一居室

package com.javaer.dynamic;

import com.javaer.dynamic.House;

public class House1 implements House {
    public void buy() {
        System.out.println("[动态代理]我成功购买了个一居室");
    }
}

动态代理

package com.javaer.dynamic;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MyInvocationHandler implements InvocationHandler {
    private Object object;

    public MyInvocationHandler(Object object) {
        this.object = object;
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("------买房前申请房产审批");
        Object invoke = method.invoke(object, args);
        System.out.println("------买房后拿到房本");
        return invoke;
    }
}
 public static void main(String[] args) {
        House1 house1 = new House1();
        InvocationHandler invocationHandler = new MyInvocationHandler(house1);
        House dynamicProxy = (House) Proxy.newProxyInstance(House1.class.getClassLoader(),
                House1.class.getInterfaces(), invocationHandler);
        dynamicProxy.buy();

    }
------买房前申请房产审批
我成功购买了个一居室
------买房后拿到房本

动态代理里不需要和房子发生什么关系,但是利用Proxy.newProxyInstance可以生成House1这个一居室类

静态代理需要实现House房子接口,要实现buy方法,如果我们为House这个接口增加一个方法叫交税 tax(),那么静态代理需要修改源代码增加tax()的实现方法。

现在扩展下,我想买个商铺

package com.javaer.dynamic;

public interface Shop {
    public void buy();
}
package com.javaer.dynamic;

public class Shop100 implements Shop{
    public void buy() {
        System.out.println("[动态代理]我购买了100平米商铺");
    }
}

动态代理什么也不用改直接调用

public static void main(String[] args) {
        House house1 = new House1();
        InvocationHandler invocationHandler = new MyInvocationHandler(house1);
        House dynamicProxy = (House) Proxy.newProxyInstance(House1.class.getClassLoader(),
                House1.class.getInterfaces(), invocationHandler);
        dynamicProxy.buy();

        Shop shop100 = new Shop100();
        invocationHandler = new MyInvocationHandler(shop100);
        Shop dynamicProxyShop = (Shop) Proxy.newProxyInstance(Shop100.class.getClassLoader(),
                Shop100.class.getInterfaces(), invocationHandler);
        dynamicProxyShop.buy();

    }

在这个场景下,如果要静态代理实现,要再写一个商铺的代理实现商铺的购买。我们还可以这么理解,静态代理像链家地产只能买卖房子,动态代理像个仆人,还能买金银珠宝。

动态代理原理

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
//此方法的参数含义如下
//proxy:代表动态代理对象
//method:代表正在执行的方法
//args:代表当前执行方法传入的实参
//返回值:表示当前执行方法的返回值

深入研究下代理帮忙生成了什么

//System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); 

以上两句话加上能看出生成的代理文件,在工程目录下的com文件夹下(有的说新版本JDK用上面的,但是我是jdk1.8 用了下面的才好用)

    public static void main(String[] args) {
        //System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

        House house1 = new House1();
        InvocationHandler invocationHandler = new MyInvocationHandler(house1);
        House dynamicProxy = (House) Proxy.newProxyInstance(House1.class.getClassLoader(),
                House1.class.getInterfaces(), invocationHandler);
        dynamicProxy.buy();

        Shop shop100 = new Shop100();
        invocationHandler = new MyInvocationHandler(shop100);
        Shop dynamicProxyShop = (Shop) Proxy.newProxyInstance(Shop100.class.getClassLoader(),
                Shop100.class.getInterfaces(), invocationHandler);
        dynamicProxyShop.buy();
        System.out.println("房子代理" + dynamicProxy.getClass());
        System.out.println("商铺代理" + dynamicProxyShop.getClass());

    }
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.sun.proxy;

import com.javaer.dynamic.House;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements House {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    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});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

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

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

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

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.javaer.dynamic.House").getMethod("buy");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

我们可以看到房子的代理的文件里,实现了房子House的接口,并完成了buy的使用 m3 = Class.forName("com.javaer.dynamic.House").getMethod("buy");

而店铺打开发现实现了店铺Shop的接口

public final class $Proxy1 extends Proxy implements Shop {

cglib 动态代理

以上代理很明显依赖接口,如果类如何被动态代理,这时候cglib这个就上场了

建立个买房子的类

package com.javaer.cglib;

public class House {
    public void buy(){
        System.out.println("购买一个房子");
    }
}

引入cglib

  <dependencies>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>
    </dependencies>

实现动态代理

package com.javaer.cglib;

import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibProxyInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("买房前申请房产证");
        Object object = methodProxy.invokeSuper(o, objects);
        System.out.println("买房后得到房产证");
        return object;
    }
    public static void main(String[] args) {
        // //在指定目录下生成动态代理类
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/yuexiaosheng/my_java/javaer/proxyTest/cglib/");
        //创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数
        Enhancer enhancer = new Enhancer();
        //设置目标类的字节码文件
        enhancer.setSuperclass(House.class);
        //设置回调函数
        enhancer.setCallback(new CglibProxyInterceptor());
        //这里的creat方法就是正式创建代理类
        House house = (House)enhancer.create();
        //调用代理类的play方法
        house.buy();
        System.out.println("cglib动态代理House:"+house.getClass());
    }

}

看看最终生成的类

public class House$$EnhancerByCGLIB$$176cfcaf extends House implements Factory {

这个类采用继承House的方式实现了动态代理,拦截器调用intercept()方法,intercept()方法由自定义CglibProxyInterceptor实现,所以,最后调用CglibProxyInterceptor中的intercept()方法,从而完成了由代理对象访问到目标对象的动态代理实现。

  • CGlib 是一个强大的,高性能,高质量的 Code 生成类库。它可以在运行期扩展 Java 类与实现 Java 接口。

  • 用 CGlib 生成代理类是目标类的子类。

  • 用 CGlib 生成 代理类不需要接口。

  • 用 CGLib 生成的代理类重写了父类的各个方法。

  • 拦截器中的 intercept 方法内容正好就是代理类中的方法体。

代理模式本质上的目的是为了增强现有代码的功能, 而不用我们到处写代码。就如我们有了个仆人帮助我们干任何事儿。

https://juejin.cn/post/6874916520179269639

https://www.liaoxuefeng.com/wiki/1252599548343744/1264804593397984

https://mp.weixin.qq.com/s/LWToCjGGP52_0cy9xkiHlQ

jdk动态代理生成的代理类实现了我们业务定义的接口,并重写了我们接口的方法,如此才实现代理的功能,所以我们的目标类需要实现接口。

jdk代理的关键在代理类的生成

https://xie.infoq.cn/article/9a9387805a496e1485dc8430f 静态代理和动态代理 说的很清楚。

https://shusheng007.top/2021/09/08/proxy-pattern/ 秒懂秒懂

https://shusheng007.top/2023/02/10/chatgpt-register/ openAI注册


This entry was posted in JAVA. Bookmark the permalink.
月小升QQ 2651044202, 技术交流QQ群 178491360
首发地址:月小升博客https://java-er.com/blog/java-proxy/
无特殊说明,文章均为月小升原创,欢迎转载,转载请注明本文地址,谢谢
您的评论是我写作的动力.

Leave a Reply