问题:对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这个就上场了
建立个买房子的类
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注册