经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » 设计模式 » 查看文章
搞懂设计模式——代理模式 + 原理分析
来源:cnblogs  作者:京东云开发者  时间:2023/2/8 8:59:11  对本文有异议

作者:京东零售 秦浩然

引子

举个栗子,众所周知,我们是可以在京东上购买机票的。 但机票是航司提供的,我们本质上是代理销售而已。

那为什么航司要让我们代理销售呢?

我们又是如帮他做代理的呢?

别急,本文将展开说说他们之间的关系。。。

一个有梦想的航司

从前有个航司打算开展线上销售机票业务,于是设计了如下系统。系统完成后,业务正常开展了。。。

航司销售机票的接口:

  1. public interface SellAirTicket {
  2. /**
  3. * 销售机票
  4. * @param price
  5. */
  6. void sellAirTicket(int price);
  7. }

航司销售机票的接口实现类:

  1. public class SellAirTicketImpl implements SellAirTicket {
  2. @Override
  3. public void sellAirTicket(int price) {
  4. System.out.println("航司销售机票,价格:" + price);
  5. }
  6. }

测试:

  1. public class MainClass {
  2. public static void main(String[] args) {
  3. SellAirTicket sellAirTicket = new SellAirTicketImpl();
  4. sellAirTicket.sellAirTicket(666);
  5. }
  6. }

测试结果:

  1. 航司销售机票,价格:666

业务蒸蒸日上,问题接踵而至

随着业务发展的越来越好,新的问题出现了。

黄牛天天爬接口,系统风险出现了;卖完票没有统计结果,卖成啥样也不知道。

于是航司想增加售前风控、售后统计。加上这些功能后,业务又继续稳步发展了。。。

航司销售机票的接口:

  1. public interface SellAirTicket {
  2. /**
  3. * 销售机票
  4. * @param price
  5. */
  6. void sellAirTicket(int price);
  7. }

航司销售机票的接口实现类:

  1. public class SellAirTicketImpl implements SellAirTicket {
  2. @Override
  3. public void sellAirTicket(int price) {
  4. System.out.println("航司售前风控。。。");
  5. System.out.println("航司销售机票,价格:" + price);
  6. System.out.println("航司售后统计。。。");
  7. }
  8. }

测试:

  1. public class MainClass {
  2. public static void main(String[] args) {
  3. SellAirTicket sellAirTicket = new SellAirTicketImpl();
  4. sellAirTicket.sellAirTicket(666);
  5. }
  6. }

测试结果:

  1. 航司售前风控。。。
  2. 航司销售机票,价格:666
  3. 航司售后统计。。。

人员捉襟见肘,业务被迫拆分

后来航司发现,就这么点人,又想做风控,又想卖机票,又想做统计,根本忙不过来。

那怎么解决呢? 航司只想专心卖票,不想做这些跟卖票无关的工作,那只能找个代理公司了。

于是,航司找到了JD代替自己做这些工作,自己就负责专心卖票。。。

航司销售机票的接口:

  1. public interface SellAirTicket {
  2. /**
  3. * 销售机票
  4. * @param price
  5. */
  6. void sellAirTicket(int price);
  7. }

航司销售机票的接口实现类:

  1. public class SellAirTicketImpl implements SellAirTicket {
  2. @Override
  3. public void sellAirTicket(int price) {
  4. System.out.println("航司销售机票,价格:" + price);
  5. }
  6. }

JD平台代理航司销售机票实现类:

  1. public class SellAirTicketProxy implements SellAirTicket {
  2. /**
  3. * 航司售票接口
  4. */
  5. private SellAirTicket sellAirTicket;
  6. @Override
  7. public void sellAirTicket(int price) {
  8. System.out.println("JD售前风控。。。");
  9. sellAirTicket.sellAirTicket(price);
  10. System.out.println("JD售后统计。。。");
  11. }
  12. public SellAirTicketProxy(SellAirTicket sellAirTicket) {
  13. this.sellAirTicket = sellAirTicket;
  14. }
  15. }

测试:

  1. public class MainClass {
  2. public static void main(String[] args) {
  3. SellAirTicket sellAirTicket = new SellAirTicketImpl();
  4. SellAirTicket sellAirTicketProxy = new SellAirTicketProxy(sellAirTicket);
  5. sellAirTicketProxy.sellAirTicket(666);
  6. }
  7. }

测试结果:

  1. JD售前风控。。。
  2. 航司销售机票,价格:666
  3. JD售后统计。。。

朴素的一对一合作方式,静态代理

以上流程对与航司而言,由JD帮助自己关注风控、统计,自已可以专心的卖票,看着很好的样子。

但是JD平台只能给航司卖票,其余的也干不了,航司与JD的关系属于静态绑定的关系,即:被代理类与代理类属于静态绑定的关系,称之为“静态代理”。

据此,我们可以给代理模式下个定义:

  1. 【代理模式】 就是在不改变原有类(被代理类)的情况下,为原有类创建代理对象,对原有类的功能做增强的一种模式
  2. 代理模式的优点:
  3. 1. 满足单一原则,业务类可以只关心自己的核心逻辑,非核心逻辑由代理类完成;
  4. 2. 易于维护,核心逻辑、非核心逻辑的修改不会互相影响;
  5. 3. 对于用户(调用者)而言,使用的方式没有区别,有和可以做到低成本替换;
  6. 代理模式的缺点:
  7. 1. 每个被代理类都要有一个代理类,大大增加了代码量;

保险出现,代理公司高瞻远瞩

某天,保险公司也被风控、统计逻辑搞的焦头烂额,听说航司找了个代理,于是也找到了JD,让JD给他们做代理。

JD想:总不能谁来找我,我就给谁做一套代理系统吧,那我得做多少套,反正他们都是找我做风控、统计的,那我能不能做一套系统,给他们所有的人用呢,说干就干。。。

保险公司销售保险的接口:

  1. public interface SellInsurance {
  2. /**
  3. * 销售保险
  4. * @param price
  5. */
  6. void sellInsurance(int price);
  7. }

保险公司销售保险的接口实现类:

  1. public class SellInsuranceImpl implements SellInsurance {
  2. @Override
  3. public void sellInsurance(int price) {
  4. System.out.println("保险公司销售保险,价格:" + price);
  5. }
  6. }

JD平台代理的风控、统计实现类:

  1. public class SellDynamicProxy {
  2. /**
  3. * 获取传入目标对象的代理对象
  4. * @param target
  5. * @return
  6. */
  7. public Object createProxy(Object target) {
  8. return Proxy.newProxyInstance(
  9. target.getClass().getClassLoader(), //目标对象使用类加载器
  10. target.getClass().getInterfaces(), //目标对象实现的接口的类型
  11. new DynamicProxyHandler(target)); //目标对象事件处理器
  12. }
  13. /**
  14. * 目标对象的事件处理器
  15. */
  16. private class DynamicProxyHandler implements InvocationHandler {
  17. //被代理对象
  18. private Object target;
  19. @Override
  20. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  21. System.out.println("JD售前风控。。。");
  22. method.invoke(target, args);
  23. System.out.println("JD售后统计。。。");
  24. return null;
  25. }
  26. public DynamicProxyHandler(Object object) {
  27. this.target = object;
  28. }
  29. }
  30. }

测试:

  1. public class MainClass {
  2. public static void main(String[] args) {
  3. //创建动态代理平台
  4. SellDynamicProxy dynamicProxy = new SellDynamicProxy();
  5. //代理销售机票
  6. SellAirTicket airTicketProxy = (SellAirTicket) dynamicProxy.createProxy(new SellAirTicketImpl());
  7. airTicketProxy.sellAirTicket(600);
  8. //代理销售保险
  9. SellInsurance insuranceProxy = (SellInsurance) dynamicProxy.createProxy(new SellInsuranceImpl());
  10. insuranceProxy.sellInsurance(30);
  11. }
  12. }

测试结果:

  1. JD售前风控。。。
  2. 航司销售机票,价格:600
  3. JD售后统计。。。
  4. JD售前风控。。。
  5. 保险公司销售保险,价格:30
  6. JD售后统计。。。

进阶的一对多合作方式,动态代理

到这里航司、保险公司都找到了自己的代理,JD平台也完成了风控、统计代理平台的搭建。再有人来找自己,JD平台都可以满足代理需求,现在看来,已经很完美了。

被代理的商家与JD属于动态绑定的关系,即:被代理类与代理类属于动态绑定的关系,称之为“动态代理”,由于此代理功能依赖JDK提供的Proxy、InvocationHandler类,也成为“JDK动态代理”。

据此,我们可以补充代理模式的定义:

  1. 【代理模式】 就是在不改变原有类(被代理类)的情况下,为原有类创建代理对象,对原有类的功能做增强的一种模式
  2. 代理模式的优点:
  3. 1. 满足单一原则,业务类可以只关心自己的核心逻辑,非核心逻辑由代理类完成;
  4. 2. 易于维护,核心逻辑、非核心逻辑的修改不会互相影响;
  5. 3. 对于用户(调用者)而言,使用的方式没有区别,可以做到低成本替换;
  6. 4. JDK动态代理可以动态的绑定目标类,可以减少代码量,提高代码的复用;
  7. 代理模式的缺点:
  8. 1. 静态代理每个被代理类都要有一个代理类,大大增加了代码量;
  9. 2. JDK动态代理基于JDK的反射原理实现,降低了执行效率;

酒店出现,代理公司意外降临

平静的日子没过多久,一天某酒店找来了,要求给他们做代理,做就做呗。轻车又熟路。。。

酒店销售房间:

  1. public class SellHotel {
  2. /**
  3. * 销售酒店
  4. * @param price
  5. */
  6. public void sellHotel(int price) {
  7. System.out.println("酒店销售房间,价格:" + price);
  8. }
  9. }

测试:

  1. public class MainClass {
  2. public static void main(String[] args) {
  3. //创建动态代理平台
  4. SellDynamicProxy dynamicProxy = new SellDynamicProxy();
  5. //代理销售酒店
  6. SellHotel sellHotel = (SellHotel) dynamicProxy.createProxy(new SellHotel());
  7. sellHotel.sellHotel(300);
  8. }
  9. }

测试结果:

  1. Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be
  2. cast to demo.pattern.proxy.SellHotel at demo.pattern.proxy.MainClass.main(MainClass.java:14)

怎么回事,平台不好用了!代理公司闭关修炼,查一下问题。。。

优秀代理公司的自我修养

以前给别人代理都好使,这次给酒店代理为何就不行了呢? 一个优秀的代理,有问题就要解决问题。。。

先看异常,代理类不能被强转为目标类型,但是为何之前的都好使?

思考:只有生成的代理类属于目标类型,才能强转,那就需要代理类实现目标类的接口,那问题就可能是这样了,验证一下。

原理分析:JDK代理对象是如何实现的?

让我们先看下源码:

  1. /**
  2. * 获取传入目标对象的代理对象
  3. * @param target
  4. * @return
  5. */
  6. public Object createProxy(Object target) {
  7. return Proxy.newProxyInstance(
  8. target.getClass().getClassLoader(), //目标对象使用类加载器
  9. target.getClass().getInterfaces(), //目标对象实现的接口的类型
  10. new DynamicProxyHandler(target)); //目标对象事件处理器
  11. }
  12. /**
  13. * 创建代理类源码
  14. */
  15. public static Object newProxyInstance(ClassLoader loader,
  16. Class<?>[] interfaces,
  17. InvocationHandler h)
  18. throws IllegalArgumentException
  19. {
  20. Objects.requireNonNull(h);
  21. final Class<?>[] intfs = interfaces.clone();
  22. final SecurityManager sm = System.getSecurityManager();
  23. if (sm != null) {
  24. checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
  25. }
  26. /*
  27. * Look up or generate the designated proxy class.
  28. */
  29. Class<?> cl = getProxyClass0(loader, intfs); //生成代理类的字节码对象
  30. /*
  31. * Invoke its constructor with the designated invocation handler.
  32. */
  33. try {
  34. if (sm != null) {
  35. checkNewProxyPermission(Reflection.getCallerClass(), cl);
  36. }
  37. final Constructor<?> cons = cl.getConstructor(constructorParams); //获取参数为事件处理器的构造器
  38. final InvocationHandler ih = h;
  39. if (!Modifier.isPublic(cl.getModifiers())) {
  40. AccessController.doPrivileged(new PrivilegedAction<Void>() {
  41. public Void run() {
  42. cons.setAccessible(true);
  43. return null;
  44. }
  45. });
  46. }
  47. return cons.newInstance(new Object[]{h}); //用词构造器,传入的事件处理器,构造代理类
  48. } catch (IllegalAccessException|InstantiationException e) {
  49. throw new InternalError(e.toString(), e);
  50. } catch (InvocationTargetException e) {
  51. Throwable t = e.getCause();
  52. if (t instanceof RuntimeException) {
  53. throw (RuntimeException) t;
  54. } else {
  55. throw new InternalError(t.toString(), t);
  56. }
  57. } catch (NoSuchMethodException e) {
  58. throw new InternalError(e.toString(), e);
  59. }
  60. }
  61. /**
  62. * Generate a proxy class. Must call the checkProxyAccess method
  63. * to perform permission checks before calling this.
  64. * 生成代理类的字节码对象
  65. */
  66. private static Class<?> getProxyClass0(ClassLoader loader,
  67. Class<?>... interfaces) {
  68. if (interfaces.length > 65535) {
  69. throw new IllegalArgumentException("interface limit exceeded");
  70. }
  71. // If the proxy class defined by the given loader implementing
  72. // the given interfaces exists, this will simply return the cached copy;
  73. // otherwise, it will create the proxy class via the ProxyClassFactory
  74. return proxyClassCache.get(loader, interfaces); //这里获取字节码对象
  75. }
  76. /**
  77. * 这里获取字节码对象
  78. */
  79. public V get(K key, P parameter) {
  80. Objects.requireNonNull(parameter);
  81. expungeStaleEntries();
  82. Object cacheKey = CacheKey.valueOf(key, refQueue);
  83. // lazily install the 2nd level valuesMap for the particular cacheKey
  84. ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
  85. if (valuesMap == null) {
  86. ConcurrentMap<Object, Supplier<V>> oldValuesMap
  87. = map.putIfAbsent(cacheKey,
  88. valuesMap = new ConcurrentHashMap<>());
  89. if (oldValuesMap != null) {
  90. valuesMap = oldValuesMap;
  91. }
  92. }
  93. // create subKey and retrieve the possible Supplier<V> stored by that
  94. // subKey from valuesMap
  95. Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter)); //这里创建字解码对象
  96. Supplier<V> supplier = valuesMap.get(subKey);
  97. Factory factory = null;
  98. while (true) {
  99. if (supplier != null) {
  100. // supplier might be a Factory or a CacheValue<V> instance
  101. V value = supplier.get();
  102. if (value != null) {
  103. return value;
  104. }
  105. }
  106. // else no supplier in cache
  107. // or a supplier that returned null (could be a cleared CacheValue
  108. // or a Factory that wasn't successful in installing the CacheValue)
  109. // lazily construct a Factory
  110. if (factory == null) {
  111. factory = new Factory(key, parameter, subKey, valuesMap);
  112. }
  113. if (supplier == null) {
  114. supplier = valuesMap.putIfAbsent(subKey, factory);
  115. if (supplier == null) {
  116. // successfully installed Factory
  117. supplier = factory;
  118. }
  119. // else retry with winning supplier
  120. } else {
  121. if (valuesMap.replace(subKey, supplier, factory)) {
  122. // successfully replaced
  123. // cleared CacheEntry / unsuccessful Factory
  124. // with our Factory
  125. supplier = factory;
  126. } else {
  127. // retry with current supplier
  128. supplier = valuesMap.get(subKey);
  129. }
  130. }
  131. }
  132. }
  133. /**
  134. * A factory function that generates, defines and returns the proxy class given
  135. * the ClassLoader and array of interfaces.
  136. *
  137. * Proxy类的内部类,就是为了创建代理对象的字节码对象
  138. */
  139. private static final class ProxyClassFactory
  140. implements BiFunction<ClassLoader, Class<?>[], Class<?>>
  141. {
  142. // prefix for all proxy class names
  143. private static final String proxyClassNamePrefix = "$Proxy";
  144. // next number to use for generation of unique proxy class names
  145. private static final AtomicLong nextUniqueNumber = new AtomicLong();
  146. @Override
  147. public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
  148. Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
  149. for (Class<?> intf : interfaces) {
  150. /*
  151. * Verify that the class loader resolves the name of this
  152. * interface to the same Class object.
  153. */
  154. Class<?> interfaceClass = null;
  155. try {
  156. interfaceClass = Class.forName(intf.getName(), false, loader);
  157. } catch (ClassNotFoundException e) {
  158. }
  159. if (interfaceClass != intf) {
  160. throw new IllegalArgumentException(
  161. intf + " is not visible from class loader");
  162. }
  163. /*
  164. * Verify that the Class object actually represents an
  165. * interface.
  166. */
  167. if (!interfaceClass.isInterface()) {
  168. throw new IllegalArgumentException(
  169. interfaceClass.getName() + " is not an interface");
  170. }
  171. /*
  172. * Verify that this interface is not a duplicate.
  173. */
  174. if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
  175. throw new IllegalArgumentException(
  176. "repeated interface: " + interfaceClass.getName());
  177. }
  178. }
  179. String proxyPkg = null; // package to define proxy class in
  180. int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
  181. /*
  182. * Record the package of a non-public proxy interface so that the
  183. * proxy class will be defined in the same package. Verify that
  184. * all non-public proxy interfaces are in the same package.
  185. */
  186. for (Class<?> intf : interfaces) {
  187. int flags = intf.getModifiers();
  188. if (!Modifier.isPublic(flags)) {
  189. accessFlags = Modifier.FINAL;
  190. String name = intf.getName();
  191. int n = name.lastIndexOf('.');
  192. String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
  193. if (proxyPkg == null) {
  194. proxyPkg = pkg;
  195. } else if (!pkg.equals(proxyPkg)) {
  196. throw new IllegalArgumentException(
  197. "non-public interfaces from different packages");
  198. }
  199. }
  200. }
  201. if (proxyPkg == null) {
  202. // if no non-public proxy interfaces, use com.sun.proxy package
  203. proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
  204. }
  205. /*
  206. * Choose a name for the proxy class to generate.
  207. */
  208. long num = nextUniqueNumber.getAndIncrement();
  209. String proxyName = proxyPkg + proxyClassNamePrefix + num;
  210. /*
  211. * Generate the specified proxy class.
  212. */
  213. byte[] proxyClassFile = ProxyGenerator.generateProxyClass( //这里生成需要的字节码对象
  214. proxyName, interfaces, accessFlags);
  215. try {
  216. return defineClass0(loader, proxyName,
  217. proxyClassFile, 0, proxyClassFile.length);
  218. } catch (ClassFormatError e) {
  219. /*
  220. * A ClassFormatError here means that (barring bugs in the
  221. * proxy class generation code) there was some other
  222. * invalid aspect of the arguments supplied to the proxy
  223. * class creation (such as virtual machine limitations
  224. * exceeded).
  225. */
  226. throw new IllegalArgumentException(e.toString());
  227. }
  228. }
  229. }

上面分析一堆,那我们来了看看得到的代理类到底是啥,为啥他就能执行那个我们的目标类的方法。同时,还得目标类实现接口?

  1. /**
  2. * 我们自己生成一份目标类字节码文件
  3. * @throws IOException
  4. */
  5. public static void transClass() throws IOException {
  6. SellAirTicketImpl sellAirTicket = new SellAirTicketImpl();
  7. byte[] bts = ProxyGenerator.generateProxyClass("$Proxy0", sellAirTicket.getClass().getInterfaces());
  8. File file = new File("E:\test","$Proxy0.class");
  9. if (!file.exists()){
  10. file.createNewFile();
  11. }
  12. FileOutputStream fos = new FileOutputStream(file);
  13. fos.write(bts);
  14. fos.flush();
  15. fos.close();
  16. }

将我们的字节码文件在此反编译:http://javare.cn,得到我们的代理类:

  1. import demo.pattern.proxy.SellAirTicket;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Method;
  4. import java.lang.reflect.Proxy;
  5. import java.lang.reflect.UndeclaredThrowableException;
  6. public final class $Proxy0 extends Proxy implements SellAirTicket {
  7. private static Method m1;
  8. private static Method m2;
  9. private static Method m3;
  10. private static Method m0;
  11. public $Proxy0(InvocationHandler var1) throws { //代理类的构造器,将事件处理器传入,交给父类Proxy
  12. super(var1);
  13. }
  14. public final boolean equals(Object var1) throws {
  15. try {
  16. return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
  17. } catch (RuntimeException | Error var3) {
  18. throw var3;
  19. } catch (Throwable var4) {
  20. throw new UndeclaredThrowableException(var4);
  21. }
  22. }
  23. public final String toString() throws {
  24. try {
  25. return (String)super.h.invoke(this, m2, (Object[])null);
  26. } catch (RuntimeException | Error var2) {
  27. throw var2;
  28. } catch (Throwable var3) {
  29. throw new UndeclaredThrowableException(var3);
  30. }
  31. }
  32. public final void sellAirTicket(int var1) throws {
  33. try {
  34. super.h.invoke(this, m3, new Object[]{Integer.valueOf(var1)}); //执行目标方法时,调用父类的事件处理器
  35. } catch (RuntimeException | Error var3) {
  36. throw var3;
  37. } catch (Throwable var4) {
  38. throw new UndeclaredThrowableException(var4);
  39. }
  40. }
  41. public final int hashCode() throws {
  42. try {
  43. return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
  44. } catch (RuntimeException | Error var2) {
  45. throw var2;
  46. } catch (Throwable var3) {
  47. throw new UndeclaredThrowableException(var3);
  48. }
  49. }
  50. static {
  51. try {
  52. m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
  53. m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
  54. m3 = Class.forName("demo.pattern.proxy.SellAirTicket").getMethod("sellAirTicket", new Class[]{Integer.TYPE}); //获取接口类型的目标方法
  55. m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
  56. } catch (NoSuchMethodException var2) {
  57. throw new NoSuchMethodError(var2.getMessage());
  58. } catch (ClassNotFoundException var3) {
  59. throw new NoClassDefFoundError(var3.getMessage());
  60. }
  61. }
  62. }

到此为止真相大白了,原来代理类继承了Proxy父类,同时实现了目标类的接口,这就将我们的目标方法与定义的事件处理器联系起来了。

同时,由于java的单继承模式,导致了代理类只能继承Proxy类,那这样的话,就只好通过目标类的接口来关联目标类了。

小结:JDK动态代理

据此,我们可以再次补充代理模式的定义:

  1. 【代理模式】 就是在不改变原有类(被代理类)的情况下,为原有类创建代理对象,对原有类的功能做增强的一种模式
  2. 代理模式的优点:
  3. 1. 满足单一原则,业务类可以只关心自己的核心逻辑,非核心逻辑由代理类完成;
  4. 2. 易于维护,核心逻辑、非核心逻辑的修改不会互相影响;
  5. 3. 对于用户(调用者)而言,使用的方式没有区别,可以做到低成本替换;
  6. 4. JDK动态代理可以动态的绑定目标类,可以减少代码量,提高代码的复用;
  7. 代理模式的缺点:
  8. 1. 静态代理每个被代理类都要有一个代理类,大大增加了代码量;
  9. 2. JDK动态代理基于JDK的反射原理实现,降低了执行效率;
  10. 3. JDK动态代理是基于接口的代理,要求目标类必须实现目标接口;

好的合作伙伴就是不抛弃不放弃

到这里,问题是搞明白了,就是酒店的问题,但是好的合作伙伴就是应该不抛弃,不放弃。

酒店跟我们合作,我们就要帮助他们解决困难。那怎么办呢?酒店没接口,JDK代理又非要接口,那我们就不用JDK代理了!

这时,基于类的代理方式就应运而生了—— cglib为我们提供了基于类的动态代理模式。

导Jar包:cglib-3.2.5.jar(cglib核心包)、asm-3.3.1.jar(字节码处理框架)

  1. public class CglibDynamicProxy implements MethodInterceptor {
  2. //目标对象
  3. private Object target;
  4. /**
  5. *给目标对象创建一个代理对象
  6. */
  7. public Object getProxyInstance(){
  8. //工具类
  9. Enhancer en = new Enhancer();
  10. //设置父类
  11. en.setSuperclass(target.getClass());
  12. //设置回调函数
  13. en.setCallback(this);
  14. //创建子类代理对象
  15. return en.create();
  16. }
  17. @Override
  18. public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
  19. System.out.println("cglib售前风控。。。");
  20. final Object invoke = method.invoke(target, objects);
  21. System.out.println("cglib售后统计。。。");
  22. return invoke;
  23. }
  24. public CglibDynamicProxy(Object target) {
  25. this.target = target;
  26. }
  27. }

测试

  1. public static void main(String[] args) {
  2. //创建销售酒店代理
  3. CglibDynamicProxy cglibDynamicProxy = new CglibDynamicProxy(new SellHotel());
  4. SellHotel sellHotel = (SellHotel) cglibDynamicProxy.getProxyInstance();
  5. sellHotel.sellHotel(300);
  6. }

测试结果

  1. cglib售前风控。。。
  2. 酒店销售房间,价格:300
  3. cglib售后统计。。。

原理分析:cglib代理对象是如何实现的?

那为啥cglib就不用目标类实现接口了呢?让我们看看代理类。

  1. public static void main(String[] args) {
  2. //代理类class文件存入本地磁盘
  3. System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\testCglib");
  4. //创建销售酒店代理
  5. CglibDynamicProxy cglibDynamicProxy = new CglibDynamicProxy(new SellHotel());
  6. SellHotel sellHotel = (SellHotel) cglibDynamicProxy.getProxyInstance();
  7. sellHotel.sellHotel(300);
  8. }

反编译结果

  1. /**
  2. * 代理类反编译结果
  3. */
  4. public class SellHotel$$EnhancerByCGLIB$$2624d6e3 extends SellHotel implements Factory {
  5. private boolean CGLIB$BOUND;
  6. public static Object CGLIB$FACTORY_DATA;
  7. private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
  8. private static final Callback[] CGLIB$STATIC_CALLBACKS;
  9. private MethodInterceptor CGLIB$CALLBACK_0; //方法拦截器
  10. private static final Method CGLIB$sellHotel$0$Method; //被代理方法
  11. private static final MethodProxy CGLIB$sellHotel$0$Proxy; //代理方法
  12. private static final Object[] CGLIB$emptyArgs;
  13. private static final Method CGLIB$equals$1$Method;
  14. private static final MethodProxy CGLIB$equals$1$Proxy;
  15. private static final Method CGLIB$toString$2$Method;
  16. private static final MethodProxy CGLIB$toString$2$Proxy;
  17. private static final Method CGLIB$hashCode$3$Method;
  18. private static final MethodProxy CGLIB$hashCode$3$Proxy;
  19. private static final Method CGLIB$clone$4$Method;
  20. private static final MethodProxy CGLIB$clone$4$Proxy;
  21. static void CGLIB$STATICHOOK1() {
  22. CGLIB$THREAD_CALLBACKS = new ThreadLocal();
  23. CGLIB$emptyArgs = new Object[0];
  24. Class var0 = Class.forName("demo.pattern.proxy.SellHotel$$EnhancerByCGLIB$$2624d6e3"); //代理类
  25. Class var1; //被代理类
  26. CGLIB$sellHotel$0$Method = ReflectUtils.findMethods(new String[]{"sellHotel", "(I)V"}, (var1 = Class.forName("demo.pattern.proxy.SellHotel")).getDeclaredMethods())[0];
  27. CGLIB$sellHotel$0$Proxy = MethodProxy.create(var1, var0, "(I)V", "sellHotel", "CGLIB$sellHotel$0");
  28. Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
  29. CGLIB$equals$1$Method = var10000[0];
  30. CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
  31. CGLIB$toString$2$Method = var10000[1];
  32. CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
  33. CGLIB$hashCode$3$Method = var10000[2];
  34. CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$3");
  35. CGLIB$clone$4$Method = var10000[3];
  36. CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
  37. }
  38. final void CGLIB$sellHotel$0(int var1) {
  39. super.sellHotel(var1);
  40. }
  41. public final void sellHotel(int var1) { //代理类重写的方法
  42. MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; //方法拦截器
  43. if(this.CGLIB$CALLBACK_0 == null) {
  44. CGLIB$BIND_CALLBACKS(this);
  45. var10000 = this.CGLIB$CALLBACK_0;
  46. }
  47. if(var10000 != null) { //执行方法拦截器
  48. var10000.intercept(this, CGLIB$sellHotel$0$Method, new Object[]{new Integer(var1)}, CGLIB$sellHotel$0$Proxy);
  49. } else {
  50. super.sellHotel(var1);
  51. }
  52. }
  53. final boolean CGLIB$equals$1(Object var1) {
  54. return super.equals(var1);
  55. }
  56. public final boolean equals(Object var1) {
  57. MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
  58. if(this.CGLIB$CALLBACK_0 == null) {
  59. CGLIB$BIND_CALLBACKS(this);
  60. var10000 = this.CGLIB$CALLBACK_0;
  61. }
  62. if(var10000 != null) {
  63. Object var2 = var10000.intercept(this, CGLIB$equals$1$Method, new Object[]{var1}, CGLIB$equals$1$Proxy);
  64. return var2 == null?false:((Boolean)var2).booleanValue();
  65. } else {
  66. return super.equals(var1);
  67. }
  68. }
  69. final String CGLIB$toString$2() {
  70. return super.toString();
  71. }
  72. public final String toString() {
  73. MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
  74. if(this.CGLIB$CALLBACK_0 == null) {
  75. CGLIB$BIND_CALLBACKS(this);
  76. var10000 = this.CGLIB$CALLBACK_0;
  77. }
  78. return var10000 != null?(String)var10000.intercept(this, CGLIB$toString$2$Method, CGLIB$emptyArgs, CGLIB$toString$2$Proxy):super.toString();
  79. }
  80. final int CGLIB$hashCode$3() {
  81. return super.hashCode();
  82. }
  83. public final int hashCode() {
  84. MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
  85. if(this.CGLIB$CALLBACK_0 == null) {
  86. CGLIB$BIND_CALLBACKS(this);
  87. var10000 = this.CGLIB$CALLBACK_0;
  88. }
  89. if(var10000 != null) {
  90. Object var1 = var10000.intercept(this, CGLIB$hashCode$3$Method, CGLIB$emptyArgs, CGLIB$hashCode$3$Proxy);
  91. return var1 == null?0:((Number)var1).intValue();
  92. } else {
  93. return super.hashCode();
  94. }
  95. }
  96. final Object CGLIB$clone$4() throws CloneNotSupportedException {
  97. return super.clone();
  98. }
  99. protected final Object clone() throws CloneNotSupportedException {
  100. MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
  101. if(this.CGLIB$CALLBACK_0 == null) {
  102. CGLIB$BIND_CALLBACKS(this);
  103. var10000 = this.CGLIB$CALLBACK_0;
  104. }
  105. return var10000 != null?var10000.intercept(this, CGLIB$clone$4$Method, CGLIB$emptyArgs, CGLIB$clone$4$Proxy):super.clone();
  106. }
  107. public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
  108. String var10000 = var0.toString();
  109. switch(var10000.hashCode()) {
  110. case -508378822:
  111. if(var10000.equals("clone()Ljava/lang/Object;")) {
  112. return CGLIB$clone$4$Proxy;
  113. }
  114. break;
  115. case 1826985398:
  116. if(var10000.equals("equals(Ljava/lang/Object;)Z")) {
  117. return CGLIB$equals$1$Proxy;
  118. }
  119. break;
  120. case 1913648695:
  121. if(var10000.equals("toString()Ljava/lang/String;")) {
  122. return CGLIB$toString$2$Proxy;
  123. }
  124. break;
  125. case 1979480752:
  126. if(var10000.equals("sellHotel(I)V")) {
  127. return CGLIB$sellHotel$0$Proxy;
  128. }
  129. break;
  130. case 1984935277:
  131. if(var10000.equals("hashCode()I")) {
  132. return CGLIB$hashCode$3$Proxy;
  133. }
  134. }
  135. return null;
  136. }
  137. public SellHotel$$EnhancerByCGLIB$$2624d6e3() {
  138. CGLIB$BIND_CALLBACKS(this);
  139. }
  140. public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) {
  141. CGLIB$THREAD_CALLBACKS.set(var0);
  142. }
  143. public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) {
  144. CGLIB$STATIC_CALLBACKS = var0;
  145. }
  146. private static final void CGLIB$BIND_CALLBACKS(Object var0) {
  147. SellHotel$$EnhancerByCGLIB$$2624d6e3 var1 = (SellHotel$$EnhancerByCGLIB$$2624d6e3)var0;
  148. if(!var1.CGLIB$BOUND) {
  149. var1.CGLIB$BOUND = true;
  150. Object var10000 = CGLIB$THREAD_CALLBACKS.get();
  151. if(var10000 == null) {
  152. var10000 = CGLIB$STATIC_CALLBACKS;
  153. if(CGLIB$STATIC_CALLBACKS == null) {
  154. return;
  155. }
  156. }
  157. var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
  158. }
  159. }
  160. public Object newInstance(Callback[] var1) {
  161. CGLIB$SET_THREAD_CALLBACKS(var1);
  162. SellHotel$$EnhancerByCGLIB$$2624d6e3 var10000 = new SellHotel$$EnhancerByCGLIB$$2624d6e3();
  163. CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
  164. return var10000;
  165. }
  166. public Object newInstance(Callback var1) {
  167. CGLIB$SET_THREAD_CALLBACKS(new Callback[]{var1});
  168. SellHotel$$EnhancerByCGLIB$$2624d6e3 var10000 = new SellHotel$$EnhancerByCGLIB$$2624d6e3();
  169. CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
  170. return var10000;
  171. }
  172. public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) {
  173. CGLIB$SET_THREAD_CALLBACKS(var3);
  174. SellHotel$$EnhancerByCGLIB$$2624d6e3 var10000 = new SellHotel$$EnhancerByCGLIB$$2624d6e3;
  175. switch(var1.length) {
  176. case 0:
  177. var10000.<init>();
  178. CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
  179. return var10000;
  180. default:
  181. throw new IllegalArgumentException("Constructor not found");
  182. }
  183. }
  184. public Callback getCallback(int var1) {
  185. CGLIB$BIND_CALLBACKS(this);
  186. MethodInterceptor var10000;
  187. switch(var1) {
  188. case 0:
  189. var10000 = this.CGLIB$CALLBACK_0;
  190. break;
  191. default:
  192. var10000 = null;
  193. }
  194. return var10000;
  195. }
  196. public void setCallback(int var1, Callback var2) {
  197. switch(var1) {
  198. case 0:
  199. this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2;
  200. default:
  201. }
  202. }
  203. public Callback[] getCallbacks() {
  204. CGLIB$BIND_CALLBACKS(this);
  205. return new Callback[]{this.CGLIB$CALLBACK_0};
  206. }
  207. public void setCallbacks(Callback[] var1) {
  208. this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0];
  209. }
  210. static {
  211. CGLIB$STATICHOOK1();
  212. }
  213. }

到此,我们知道cglib代理是帮我们新建了一个代理类,此代理类继承自目标类获取目标方法,同时重写了目标方法。

再通过我们定义的拦截器调用我们的目标方法,以此来达到代理目标方法的目的。

总结:JDK、cglib动态代理

据此,我们可以总结代理模式的定义:

  1. 【代理模式】 就是在不改变原有类(被代理类)的情况下,为原有类创建代理对象,对原有类的功能做增强的一种模式。
  2. 代理模式的优点:
  3. 1. 满足单一原则,业务类可以只关心自己的核心逻辑,非核心逻辑由代理类完成;
  4. 2. 易于维护,核心逻辑、非核心逻辑的修改不会互相影响;
  5. 3. 对于用户(调用者)而言,使用的方式没有区别,可以做到低成本替换;
  6. 4. JDK动态代理可以动态的绑定目标类,可以减少代码量,提高代码的复用;
  7. 5. cglib动态代理可基于实现类做代理,可以解决JDK代理依赖接口的问题;
  8. 代理模式的缺点:
  9. 1. 静态代理每个被代理类都要有一个代理类,大大增加了代码量;
  10. 2. JDK动态代理基于JDK的反射原理实现,降低了执行效率;
  11. 3. JDK动态代理是基于接口的代理,要求目标类必须实现目标接口;
  12. 代理模式分类:
  13. 1. 静态代理;
  14. 2. JDK动态代理(基于目标类的接口生成代理类做代理);
  15. 3. cglib动态代理(基于目标类生成子类做代理,同时也支持基于接口的代理);

代理模式的使用场景

我们知道,Spring的AOP就是依赖于动态代理模式实现的,那我们在日常的开发中有哪些地方能用到代理呢?

?事物

?日志

?监控

?统计

?鉴权

?限流

?缓存

?环境隔离

原文链接:https://www.cnblogs.com/Jcloud/p/17094633.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号