变态传奇世界65535(超变态传奇世界65535)

admin 2021-11-01 手游排行榜 108 ℃ 请在这里放置你的在线分享代码
正文
代理模式

Proxy Pattern,代理模式,一个类可以代表另一个类的功能。

在生活中代理模式也是比比皆是,房产中介、委托律师、海外代购、同城闪送,他们都是代理了实际用户的一些行为,并且极大地提供了专业性、方便性和扩展性。

代理模式的类图如下。

静态代理

根据代理模式的思想,可以用以下这个例子来说明。

在古代皇帝下圣旨、口谕,给王公大臣升职加薪、给后宫嫔妃册封,都是大总管去送信儿,王公大臣也得老老实实磕头谢恩,大总管就是皇帝的代理。

/** * @author lyqiang */interface Sayer { void saySomeThing();}class Emperor implements Sayer { @Override public void saySomeThing() { System.out.println("民有所安,万邦咸服。朕心甚喜,赏你官升P8,月薪10W。"); }}class LiGongGong implements Sayer { Emperor emperor; public LiGongGong(Emperor emperor) { this.emperor = emperor; } @Override public void saySomeThing() { System.out.println("奉天承运,皇帝诏曰。"); emperor.saySomeThing(); System.out.println("钦此。"); }}public class ProxyTest { public static void main(String[] args) { //大总管代理了皇帝 Sayer sayer = new LiGongGong(new Emperor()); sayer.saySomeThing(); }}

执行结果如下。

奉天承运,皇帝诏曰。民有所安,万邦咸服。朕心甚喜,赏你官升P8,月薪10W。钦此。

可以看到,大总管对皇帝原话增加了开头和结尾,即代理类可以对被代理类进行功能的扩展。

从静态代理的实现方式可以看出,代理类也需要实现被代理类的接口,造成一定的代码冗余,并且代理类只能对某个固定接口的实现类进行代理,其灵活性也不强。

JDK 动态代理

JDK 提供了一套基于接口的动态代理方式。

具体实现案例如下代码。

/** * @author lyqiang */interface Sayer { void saySomeThing();}class Emperor implements Sayer { @Override public void saySomeThing() { System.out.println("民有所安,万邦咸服。朕心甚喜,今天翻爱妃的牌。"); }}interface Killer { void kill();}class MotherOfEmperor implements Killer { @Override public void kill() { System.out.println("今日珍妃气我之甚,赐死。"); }}class GongGongHandler implements InvocationHandler { private final Object obj; public GongGongHandler(Object obj) { this.obj = obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String title = "kill".equals(method.getName()) ? "太后懿旨。" : "奉天承运,皇帝诏曰。" System.out.println(title); Object result = method.invoke(obj, args); System.out.println("钦此。"); return result; }}public class DynamicProxyTest { public static void main(String[] args) { // ClassLoader loader, 被代理对象的类加载器 // Class?[] interfaces,被代理类实现的接口列表 // InvocationHandler h,动态处理器 // 代理皇帝报信翻牌 Sayer sayer = new Emperor(); Sayer proxy = (Sayer) Proxy.newProxyInstance(Sayer.class.getClassLoader(), new Class[]{Sayer.class}, new GongGongHandler(sayer)); proxy.saySomeThing(); System.out.println("——————————————————"); // 代理太后报信赐死 Killer killer = new MotherOfEmperor(); Killer killProxy = (Killer) Proxy.newProxyInstance(Killer.class.getClassLoader(), new Class[]{Killer.class}, new GongGongHandler(killer)); killProxy.kill(); }}

执行结果如下。

奉天承运,皇帝诏曰。民有所安,万邦咸服。朕心甚喜,今天翻爱妃的牌。钦此。——————————————————太后懿旨。今日珍妃气我之甚,赐死。钦此。

上述代码可以再改动优化一下,将创建代理类放到 GongGongHandler 类中。

class GongGongHandlerT implements InvocationHandler { private final T obj; public GongGongHandler(T obj) { this.obj = obj; } public T getProxy() { return (T) Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String title = "kill".equals(method.getName()) ? "太后懿旨。" : "奉天承运,皇帝诏曰。" System.out.println(title); Object result = method.invoke(obj, args); System.out.println("钦此。"); return result; }}public class DynamicProxyTest { public static void main(String[] args) { // 代理皇帝报信翻牌 Sayer sayerProxy = new GongGongHandlerSayer(new Emperor()).getProxy(); sayerProxy.saySomeThing(); System.out.println("——————————————————"); // 代理太后赐死 Killer killProxy = new GongGongHandlerKiller(new MotherOfEmperor()).getProxy(); killProxy.kill(); }}

可以看到,大总管文能代理皇帝颁布圣旨,武能代理皇太后弄死皇妃。

相比静态代理来说,JDK 动态代理做到了代理类不必实现被代理类的接口,并且可以代理多种类型的接口。

JDK 的动态代理关键点如下。

1、核心执行逻辑处理器实现 InvocationHandler 接口,并实现 invoke() 方法。2、使用 Proxy.newProxyInstance() 来创建代理类。ClassLoader loader, 标识被代理类的类加载器Class?[] interfaces,标识被代理类实现的接口InvocationHandler h,代理的处理器3、将代理类转为被代理类的类型,并调用方法。JDK 动态代理的原理

静态代理是在编译时已经确定了代理关系,而动态代理是在程序运行时通过反射机制进行创建代理。 Proxy.newProxyInstance() 源码如下。

public static Object newProxyInstance(ClassLoader loader, Class?[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); final Class?[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * Look up or generate the designated proxy class. */ // 生成代理类 class Class? cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ // 实例化代理类对象 try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } final Constructor? cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedActionVoid() { public Void run() { cons.setAccessible(true); return null; } }); } return cons.newInstance(new Object[]{h}); } catch ...... }

重点关注 getProxyClass0 。

private static Class? getProxyClass0(ClassLoader loader, Class?... interfaces) { // 变态狂也不会这么干吧 65535个接口 if (interfaces.length 65535) { throw new IllegalArgumentException("interface limit exceeded"); } // If the proxy class defined by the given loader implementing // the given interfaces exists, this will simply return the cached copy; // otherwise, it will create the proxy class via the ProxyClassFactory // 从缓存中获取 return proxyClassCache.get(loader, interfaces); }

proxyClassCache 结构如下。

private static final WeakCacheClassLoader, Class?[], Class? proxyClassCache = new WeakCache(new KeyFactory(), new ProxyClassFactory());

缓存是通过 ProxyClassFactory 的 apply() 方法放入的。

private static final class ProxyClassFactory implements BiFunctionClassLoader, Class?[], Class? { // prefix for all proxy class names // 代理类的前缀 private static final String proxyClassNamePrefix = "$Proxy" // next number to use for generation of unique proxy class names // 代理类的顺序号 private static final AtomicLong nextUniqueNumber = new AtomicLong(); @Override public Class? apply(ClassLoader loader, Class?[] interfaces) { MapClass?, Boolean interfaceSet = new IdentityHashMap(interfaces.length); for (Class? intf : interfaces) { // ...... 省略一些不重要的代码 String proxyPkg = null; // package to define proxy class in int accessFlags = Modifier.PUBLIC | Modifier.FINAL; /* * Record the package of a non-public proxy interface so that the * proxy class will be defined in the same package. Verify that * all non-public proxy interfaces are in the same package. */ for (Class? intf : interfaces) { int flags = intf.getModifiers(); if (!Modifier.isPublic(flags)) { accessFlags = Modifier.FINAL; String name = intf.getName(); int n = name.lastIndexOf('.'); String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); if (proxyPkg == null) { proxyPkg = pkg; } else if (!pkg.equals(proxyPkg)) { throw new IllegalArgumentException( "non-public interfaces from different packages"); } } } if (proxyPkg == null) { // if no non-public proxy interfaces, use com.sun.proxy package // 默认 com.sun.proxy. proxyPkg = ReflectUtil.PROXY_PACKAGE + "." } /* * Choose a name for the proxy class to generate. */ long num = nextUniqueNumber.getAndIncrement(); // 代理类的完整名称 String proxyName = proxyPkg + proxyClassNamePrefix + num; /* * Generate the specified proxy class. */ // 生成 class 字节码文件的字节数组 byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { // 根据字节码返回 class return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { /* * A ClassFormatError here means that (barring bugs in the * proxy class generation code) there was some other * invalid aspect of the arguments supplied to the proxy * class creation (such as virtual machine limitations * exceeded). */ throw new IllegalArgumentException(e.toString()); } } }

通过代码可以看出,根据代理类指定的信息生成了 class 字节码文件,再通过 class 文件动态创建实例,来生成代理对象。

我们把字节码文件写到一个 MyProxy.class 文件中,来看下它的结构。

public class DynamicProxyTest { public static void main(String[] args) throws IOException { // 代理皇帝报信翻牌 Sayer sayerProxy = new GongGongHandlerSayer(new Emperor()).getProxy(); sayerProxy.saySomeThing(); // 生成 class 字节码文件的字节数组 int accessFlags = Modifier.PUBLIC | Modifier.FINAL; byte[] proxyClassFile = ProxyGenerator.generateProxyClass( sayerProxy.getClass().getName(), new Class[]{Sayer.class}, accessFlags); // 写入文件 OutputStream outputStream = new FileOutputStream("./MyProxy.class"); outputStream.write(proxyClassFile); outputStream.close(); }}

将 MyProxy.class 反编译查看代码。

public final class $Proxy0 extends Proxy implements Sayer { 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}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final void saySomeThing() throws { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } 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 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")); m3 = Class.forName("com.lyqiang.proxy.jdkproxy.dynamic.Sayer").getMethod("saySomeThing"); m2 = Class.forName("java.lang.Object").getMethod("toString"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } }}

可见动态代理创建了一个继承 Proxy 、实现 Sayer 接口的类,其中 saySomeThing() 方法直接调用了 InvocationHandler 的 invoke() 方法。

总结

静态代理遵循代理模式,在一定程度上降低了系统耦合度,并实现了功能的扩展增强,但是不够灵活。

基于上述示例和源码分析,我们了解到动态代理的执行原理,总结如下。

1、实现 InvocationHandler 接口,在 invoke 方法中增强扩展被代理类的行为。2、使用 Proxy.newProxyInstance 获得代理类的实例。a. 根据传入参数,动态生成一个类b. 该类继承 Proxy,实现被代理类的接口c. 该类中实现的被代理类接口方法,实际是调用 InvocationHandler 的 invoke 方法3、调用代理类的方法。

动态代理虽然相对灵活,但有个缺点是被代理类必须实现某个接口。它是基于接口的代理,因为生成的代理类已经继承了 Proxy 类,而 Java 是单继承,所以只能采用实现接口的方式来实现动态扩展。

原文链接:https://mp.weixin.qq /s/qqOudiFfqr5TNljrdT9gJA

本文TAG:

网站分类
最近发表
标签列表
传奇手游发布网站