【Java】jdk1.8 Java代理模式,Jdk动态代理讲解(非常详细,附带class文件)

阅读: 12 发表于 2024-07-19 10:04

 

427ef4152dbf4b6c92618a198935cb6c.png

  📝个人主页:

期待您的关注 

1b7335aca73b41609b7f05d1d366f476.gif

目录

 

 

一、什么是代理模式

想要学代理模式,我们就要先弄清一个概念“什么是代理”?

在我们的现实生活中,你或许不少听过关于代理的名词,如:代理商。那什么又叫做代理商?让我一个词来形容就是中间商。

举个例子,在你买二手房的时候,你一般不会直接和业主去交谈,你或许会找一家二手房出租出售企业来寻找房源,你看上哪个房子后,由这位中间商来沟通房源的业主完成购买流程。这就是一个代理的例子。

再举个例子,在青青草原上举办了一场选美大赛,在喜羊羊的劝导下,美羊羊参加了这场比赛。当然结果对于美羊羊来说是十分完美的,美羊羊以158票的优势超越第二名的红太狼位居榜首。从此美羊羊成为了青青草原的大明星。

红太狼自然是不服,于是灰太狼和红太狼商量了一个计划,他们想要请美羊羊来参加一场由他们举办的模特比赛,美羊羊在接收到邀请后非常开心,直接就同意了他们的邀请。但这时作为经纪人的喜羊羊就不同意了,这明显是请君入瓮啊,于是喜羊羊就代替美羊羊推掉了他们的邀请。

如果没有喜羊羊,美羊羊去了之后的后果可想而知,这时就体现出了喜羊羊的重要性了。

b22151001edd4be7952be98dd0c74c1f.png

二、Java中的静态代理 

 1.创建我们的美羊羊同意邀请的service层。go()方法也叫做目标方法。

public interface YangService { void go(); } public class YangServiceImpl implements YangService { @Override public void go() { System.out.println("美羊羊想要同意邀请"); } }

2.创建一个喜羊羊检查意图和拒绝他们的邀请的类(喜羊羊可能是多个人的经纪人,所以把他的功能单独抽取)。这些方法我也会称作增强方法。

public class DaoTransaction{ public void beforeCheck(){ System.out.println("喜羊羊检查灰太狼的意图"); } public void afterCheck(){ System.out.println("喜羊羊拒绝他们的邀请"); } }

3.我们创建一个代理类,这个类就相当于灰太狼和红太狼直接向喜羊羊发出了邀请,美羊羊太火,他们无法直接接触到美羊羊。这里要实现美羊羊功能的接口,因为喜羊羊要对美羊羊的一切行动做出处理。看下边的代码,美羊羊在同意之前要经过喜羊羊的检查,同意之后还要被喜羊羊拒绝。

public class XiYyProxy implements YangService { DaoTransaction daoTransaction = new DaoTransaction(); YangServiceImpl yangService = new YangServiceImpl(); @Override public void go() { daoTransaction.beforeCheck(); yangService.go(); daoTransaction.afterCheck(); } }

4.主程序。

public static void main(String[] args) { XiYyProxy xiYyProxy = new XiYyProxy(); xiYyProxy.go(); }

be5bf77645464f0e91a81927339b95e1.png

三、Jdk动态代理 

我们上边写的是静态代理,你可以看到,我们的代码只能让喜羊羊作为美羊羊一个人的经纪人,静态代理有着这么几个特点。

目标角色固定

在应用程序执行前就得到目标角色

代理对象会增强目标对象的行为

有可能存在多个代理 引起"类爆炸"(缺点)

如果灰太狼邀请的不是美羊羊,那我们的代码就毫无用处了,所以我们使用动态代理来实现功能。

我们创建这样的一个service,我们上边写的是YangService,为了区分我们创建一个YangTwoService,这里我弄得不太规范,重在让大家理解。

public interface YangTwoService { void go(); } public class YangTwoServiceImpl implements YangTwoService { @Override public void go() { System.out.println("沸羊羊同意参加健美比赛"); } }

那如果我们想要喜羊羊作为沸羊羊的经纪人拒绝掉这个比赛呢?还按照上边的代理方式吗?这就太费事费力了。我们这样想,我们把喜羊羊作为代理的过程抽取出来,到底要代理谁我们后期决定,只要传入一个参数确认是谁,这样我们的代码就简化很多了。

创建JdkProxyHandler。

public class JdkProxyHandler implements InvocationHandler { //这是我们要代理的目标对象 到底是谁 Object object; // 这是从喜羊羊那里抽取出来的方法 DaoTransaction daoTransaction; public JdkProxyHandler(Object o,DaoTransaction daoTransaction){ this.object = o; this.daoTransaction = daoTransaction; } @Override //proxy--代理类 method--目标方法 args--目标方法的参数 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object o = new Object(); //我们只对go方法进行处理,喜羊羊不能管得太多 if(method.getName().equals("go")){ daoTransaction.beforeCheck(); o = method.invoke(object,args); daoTransaction.afterCheck(); }else{ o = method.invoke(object,args); } return o; } }

主方法。

public class Main { public static void main(String[] args) { DaoTransaction daoTransaction = new DaoTransaction(); YangServiceImpl yangService = new YangServiceImpl(); YangTwoServiceImpl yangTwoService = new YangTwoServiceImpl(); JdkProxyHandler proxy1 = new JdkProxyHandler(yangService,daoTransaction); JdkProxyHandler proxy2 = new JdkProxyHandler(yangTwoService,daoTransaction); YangService proxyYang = (YangService) Proxy.newProxyInstance(yangService.getClass().getClassLoader(), yangService.getClass().getInterfaces(), proxy1); YangTwoService proxyYangTwo = (YangTwoService) Proxy.newProxyInstance(yangTwoService.getClass().getClassLoader(), yangTwoService.getClass().getInterfaces(), proxy2); proxyYang.go(); proxyYangTwo.go(); } }

看不懂没关系,我接下来会将Jdk动态代理的过程。我先来解释一下上边的代码。

创建我们的Handler,在上边我们能够看到,我们的逻辑处理都是在这个对象里的,这个对象很重要,你到底是如何让喜羊羊去处理的,都是靠这个JdkProxyHandler类(自定义)实现。每只不同的羊都有自己的对象。

JdkProxyHandler proxy1 = new JdkProxyHandler(yangService,daoTransaction);

这行代码才是真正创建我们的代理对象,也就是相当于我们上边静态代理创建的XiYyProxy对象,我们调用美羊羊和沸羊羊的go方法,都通过这个proxyYang对象 。

YangService proxyYang = (YangService) Proxy.newProxyInstance(yangService.getClass().getClassLoader(), yangService.getClass().getInterfaces(), proxy1);

 下边这行代码就是调用我们创建出来的代理对象来处理我们的逻辑。

proxyYang.go();

 

4f8fbca8f78747e6b93b8a399a27a5b9.png

四、Jdk动态代理解析 

我们生成一下Jdk动态代理在编译后生成的class文件的反编译结果来查看一下。下边的代码就是为了生成我们的class文件的。

public static void saveProxyClass(String path) throws IOException { byte[] $proxy1s = ProxyGenerator.generateProxyClass("$Proxy1", YangServiceImpl.class.getInterfaces()); FileOutputStream out = null; try{ out = new FileOutputStream(new File(path+"$Proxy1.class")); out.write($proxy1s); }catch (Exception e){ e.printStackTrace(); }finally { if(out == null){ try { out.flush(); out.close(); }catch (Exception e){ e.printStackTrace(); } } } }

在main方法中调用这个方法。注意:一定要修改saveProxyClass的路径,我直接生成到根目录下了!!!!!!

public static void main(String[] args) throws IOException { DaoTransaction daoTransaction = new DaoTransaction(); YangServiceImpl yangService = new YangServiceImpl(); YangTwoServiceImpl yangTwoService = new YangTwoServiceImpl(); JdkProxyHandler proxy1 = new JdkProxyHandler(yangService,daoTransaction); JdkProxyHandler proxy2 = new JdkProxyHandler(yangTwoService,daoTransaction); YangService proxyYang = (YangService) Proxy.newProxyInstance(yangService.getClass().getClassLoader(), yangService.getClass().getInterfaces(), proxy1); YangTwoService proxyYangTwo = (YangTwoService) Proxy.newProxyInstance(yangTwoService.getClass().getClassLoader(), yangTwoService.getClass().getInterfaces(), proxy2); proxyYang.go(); proxyYangTwo.go(); saveProxyClass("你的真实路径"); }

f604122c97984d21b4df8ae307b9e005.png

查看文件。

// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // import com.my.proxy.service.YangService; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class $Proxy1 extends Proxy implements YangService { private static Method m1; private static Method m2; private static Method m3; private static Method m0; public $Proxy1(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 go() 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.my.proxy.service.YangService").getMethod("go"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }

我们来分块讲解一下,先看下边的static静态块,这个块在类被加载的时候自动执行里边的代码。

我去,这不就是反射吗把我的YangServiceImpl类中继承的Object类中的方法都给我反射出来了,同时还把我的YangService接口中的方法go()也给我反射出来了。

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.my.proxy.service.YangService").getMethod("go"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } }

 在看看我们的go()方法调用。我去,这个super.h.invoke(this,m3,(Object[])null)是啥啊,我带你先弄清h是啥。

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

我们往上看看这个代理类的构造函数。你明白了吗?h就是我们传给父类的InvocationHandler类型的对象,也就是我们上边自己创建的JdkProxyHandler类。

public $Proxy1(InvocationHandler var1) throws { super(var1); }

那这个go方法还用点进去吗?我们上边的JdkProxyHandler已经写清楚了。

现在你是否了解了Jdk动态代理?它是通过反射实现的,将目标接口中的方法进行反射,然后我们的代理类拿到这些方法,每当我们执行目标接口中的方法的时候,都会通过我们的代理类进行反射调用,具体如何调用,就是通过我们创建的InvocationHandler类型的对象。

 

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=32sln7v483acs

热点推荐

最新发布

友情链接