问题:对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注册