动态代理机制

所谓动态代理,即通过代理类Proxy的代理,接口和实现类之间可以不直接发生联系,而可以在运行期(Runtime)实现动态关联。

Java动态代理类位于Java.lang.reflect包下,主要涉及到两个类。

(1)接口InvocationHandler:该接口中仅定义了一个方法。

Object invoke(Object obj, Method method, Object[] args);

在实际使用时,第一个参数obj一般指代理类,method是被代理的方法,args为该方法的参数数组。

(2)proxy:该类即为动态代理类,作用类实现了InvocationHandler接口的代理类,其中主要方法有:

protected Proxy(InvocationHandler h):构造方法,用于给内部的h赋值。

static Class getProxyClass(ClassLoader loader, Class[] interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。

static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用。

动态代理应用

Java 1.3中引入的动态代理类可以为已知接口的实现动态地创建包装器(wrapper)类。

与动态代理对应的是非动态代理,先来看看非动态代理的实现。

1. 定义接口和实现类并直接调用

接口的定义

  1. 1 package com.tuhooo.proxy.demo;
  2. 2
  3. 3 public interface Hello {
  4. 4
  5. 5 public void sayHello();
  6. 6
  7. 7 }

接口的实现类

  1. 1 package com.tuhooo.proxy.demo;
  2. 2
  3. 3 public class HelloEarth implements Hello {
  4. 4
  5. 5 @Override
  6. 6 public void sayHello() {
  7. 7 System.out.println("Hello earth!");
  8. 8 }
  9. 9
  10. 10 }

通过实现接口增强

  1. 1 package com.tuhooo.proxy.demo;
  2. 2
  3. 3 public class HelloEarthWrapper1 implements Hello {
  4. 4
  5. 5 private Hello helloImpl;
  6. 6
  7. 7 public HelloEarthWrapper1(Hello helloImpl) {
  8. 8 this.helloImpl = helloImpl;
  9. 9 }
  10. 10
  11. 11 @Override
  12. 12 public void sayHello() {
  13. 13 System.out.println("在sayHello之前执行了这条语句......");
  14. 14 helloImpl.sayHello();
  15. 15
  16. 16 }
  17. 17 }

通过继承对类进行增强

  1. 1 package com.tuhooo.proxy.demo;
  2. 2
  3. 3 public class HelloEarthWrapper2 extends HelloEarth {
  4. 4
  5. 5 private Hello helloImpl;
  6. 6
  7. 7 public HelloEarthWrapper2(Hello helloImpl) {
  8. 8 this.helloImpl = helloImpl;
  9. 9 }
  10. 10
  11. 11 @Override
  12. 12 public void sayHello() {
  13. 13 System.out.println("在sayHello之前执行了这条语句......");
  14. 14 helloImpl.sayHello();
  15. 15 }
  16. 16 }

实现InvocationHandler接口

  1. 1 package com.tuhooo.proxy.demo;
  2. 2
  3. 3 import java.lang.reflect.InvocationHandler;
  4. 4 import java.lang.reflect.Method;
  5. 5
  6. 6 public class HelloHandler implements InvocationHandler {
  7. 7
  8. 8 private Object proxyed; // 被代理的类
  9. 9
  10. 10 // 被代理的对象
  11. 11 public HelloHandler(Object obj) {
  12. 12
  13. 13 this.proxyed = obj;
  14. 14 }
  15. 15
  16. 16 @Override
  17. 17 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  18. 18
  19. 19 Object result;
  20. 20 System.out.println("在sayHello()之前可以执行的语句");
  21. 21 // 调用原始对象的方法
  22. 22 result = method.invoke(this.proxyed, args);
  23. 23 System.out.println("在调用sayHello()之后");
  24. 24 return result;
  25. 25 }
  26. 26 }

测试代理类

  1. 1 package com.tuhooo.proxy.demo;
  2. 2
  3. 3 import java.lang.reflect.InvocationHandler;
  4. 4 import java.lang.reflect.Proxy;
  5. 5 import org.junit.Test;
  6. 6
  7. 7 public class TestProxy {
  8. 8
  9. 9 @Test
  10. 10 public void test1() {
  11. 11 Hello helloImpl = new HelloEarth();
  12. 12 Hello helloWrapper = new HelloEarthWrapper2(helloImpl);
  13. 13 helloWrapper.sayHello();
  14. 14 }
  15. 15
  16. 16 @Test
  17. 17 public void test2() {
  18. 18 // 被代理类的实例
  19. 19 Hello hello = new HelloEarth();
  20. 20 // InvocationHandler接口,传入被代理的类
  21. 21
  22. 22 InvocationHandler handler = new HelloHandler(hello);
  23. 23
  24. 24 // 生成代理类
  25. 25 // 1. 给我被代理类的类加载器加载被代理的类,为啥要类加载器呢
  26. 26 // 2. 给我被代理类的接口信息
  27. 27 // 3. 给我如何增强接口中的方法信息
  28. 28 Hello proxy = (Hello) Proxy.newProxyInstance(hello.getClass()
  29. 29 .getClassLoader(), hello.getClass().getInterfaces(), handler);
  30. 30 // 调用代理的方法
  31. 31 proxy.sayHello();
  32. 32 }
  33. 33 }

2. 使用包装类进行包装

包装类中有一个被包装的类的引用作为属性,和被包装类实现相同的接口,然后在实现接口的方法中调用和被包装类同名的接口中的方法实现方法的增强。这种解决方案的缺点就是当接口中的方法过多的时候就需要为接口中的每个方法都写一遍,即时不需要增强的方法也需要实现。

另一种做法是继承被包装的类,然后在包装类中重写需要增强的方法,但是缺点是只有在被包装类出现的地方才能使用被包装类,而实现接口的方法是在接口出现的地方都可以使用包装类。继承了被包装类之后的类,就是去了接口的好处,在包装类作为引用类型的地方必须的是被包装类及其子类,而不是接口出现。

3. 使用动态代理

使用动态代理则可以不需要实现接口中所有的方法,又可以获得接口的好处。动态代理要求要包装的对象必须实现一个接口,该接口定义了准备在包装器中使用的所有方法,这一限制是鼓励面向接口的编程,而不是限制编程,根据经验,每个类至少应该实现一个接口。良好的接口用法不仅使动态代理成为可能,还有利于程序的模块化。

创建动态代理类

调用动态代理类

主要的步骤有:

首先根据被代理对象创建一个代理类

创建动态代理对象的proxy1,第一个参数为被代理类的类加载器,第二个参数为该类的接口,第三个对象为代理对象的实例。

通过调用代理对象中增强的方法

上述过程就是告诉Proxy类用一个指定的类加载器来动态创建一个对象,该对象要实现指定的接口,并用提供的InvocationHandler来代替传统的方法主体。在实现InvocationHandler接口的类中invoke()方法中,完全不存在对Hello接口的引用,在上述的例子中,以构造方法的传参的形式,为InvocationHandler的实现类提供了被代理接口的实例。代理接口实例上的任何方法调用最终都由InvocationHandler的实例委托给代理接口实例,这是最常见的设计。但是,InvocationHandler实例不一定非要委托给被代理的接口的另一个实例。事实上,InvocationHandler可以自行提供方法主体,完全不必委托给被代理类来实现。如果被代理接口发生变化,InvocationHandler中的实例仍然是可用的。

动态代理可以实现许多AOP方面的功能:安全、事务、日志、过滤、编码、解码等功能,而且纯粹是热插拔的。AOP的好处是可以对类的实例统一实现拦截操作,通常应用在日志、权限、缓存、连接池中等。

基于动态代理的字节码库

了解动态代理的原理之后,我们完全可以自己实现这样一个动态代理,只要生成该类的class文件的内存映像即可。有很多这样的工具。

BCEL:

SERP:

ASM:Java字节码汇编语言。ASM是轻量级的Java字节码处理框架,可以动态生成二进制格式的stub类或其他代理类,或者在类被Java虚拟机装载之前动态修改类。ASM设计的目的就是在运行时使用,因此要尽量体积小速度快。

cglib:Code Generation Library的缩写,依赖与ASM库。Spring和Hibernate选择了同样的cglib包。Hibernate主要利用cglib生成pojo的子类并用override get方法来实现lazy loading机制,Spring则是利用cglib来实现动态代理。JDK的动态代理必须得实现接口才行。

使用CGLib进行动态代理

待增强的类

  1. 1 package com.tuhooo.cglib.demo;
  2. 2
  3. 3 public class HelloWorld{
  4. 4 public void savePerson() {
  5. 5 System.out.println("hello, world!");
  6. 6 }
  7. 7 }

增强类

  1. 1 package com.tuhooo.cglib.demo;
  2. 2
  3. 3 import java.lang.reflect.Method;
  4. 4 import net.sf.cglib.proxy.Enhancer;
  5. 5 import net.sf.cglib.proxy.MethodInterceptor;
  6. 6 import net.sf.cglib.proxy.MethodProxy;
  7. 7
  8. 8 public class HelloWorldInterceptor implements MethodInterceptor{
  9. 9
  10. 10 // 被增强的类
  11. 11 private Object target;
  12. 12 // 如何增强方法的信息
  13. 13 private Strength strength;
  14. 14
  15. 15 public HelloWorldInterceptor(Object target, Strength strength) {
  16. 16 super();
  17. 17 this.target = target;
  18. 18 this.strength = strength;
  19. 19 }
  20. 20
  21. 21 /**
  22. 22 * 用来产生代理对象
  23. 23 */
  24. 24 public Object createProxy(){
  25. 25 // 增强类
  26. 26 Enhancer enhancer = new Enhancer();
  27. 27 // 设置增强类的回调方法
  28. 28 enhancer.setCallback(this);
  29. 29 //设置代理类的父类
  30. 30 enhancer.setSuperclass(HelloWorld.class);
  31. 31 return enhancer.create();
  32. 32 }
  33. 33
  34. 34 // 这里相当于InvocationHandler接口中的invoke方法
  35. 35 public Object intercept(Object arg0, Method method, Object[] arg2,
  36. 36 MethodProxy arg3) throws Throwable {
  37. 37 this.strength.begin();
  38. 38 method.invoke(target, arg2);
  39. 39 this.strength.end();
  40. 40 return null;
  41. 41 }
  42. 42 }

增强的方法

  1. 1 package com.tuhooo.cglib.demo;
  2. 2
  3. 3 // 待增强的方法
  4. 4 public class Strength {
  5. 5 // 前增强
  6. 6 public void begin(){
  7. 7 System.out.println("begin......");
  8. 8 }
  9. 9 // 后增强
  10. 10 public void end(){
  11. 11 System.out.println("end.......");
  12. 12 }
  13. 13 }

测试增强

  1. 1 package com.tuhooo.cglib.demo;
  2. 2
  3. 3 import org.junit.Test;
  4. 4
  5. 5 /**
  6. 6 * 使用cglib产生的代理类,其代理类是目标类的子类
  7. 7 */
  8. 8 public class TestHello {
  9. 9 @Test
  10. 10 public void testPersonDaoProxy(){
  11. 11 Object target = new HelloWorld();
  12. 12 Strength strength = new Strength();
  13. 13 HelloWorldInterceptor interceptor = new HelloWorldInterceptor(target, strength);
  14. 14 HelloWorld helloWorldProxy = (HelloWorld)interceptor.createProxy();
  15. 15 helloWorldProxy.savePerson();
  16. 16 }
  17. 17 }

InvocationHandler源码

  1. 1 package java.lang.reflect;
  2. 2
  3. 3 /**
  4. 4 * 每个代理类实例都有一个与之关联的invocation句柄。
  5. 5 * 当代理类的实例调用一个方法的时候,
  6. 6 * 该方法的invocation被编码并且被分发到它对应的invocation句柄
  7. 7 */
  8. 8 public interface InvocationHandler {
  9. 9
  10. 10 public Object invoke(Object proxy, Method method, Object[] args)
  11. 11 throws Throwable;
  12. 12 }

Proxy的源码

  1. 1 package java.lang.reflect;
  2. 2
  3. 3 import java.lang.ref.Reference;
  4. 4 import java.lang.ref.WeakReference;
  5. 5 import java.security.AccessController;
  6. 6 import java.security.Permission;
  7. 7 import java.security.PrivilegedAction;
  8. 8 import java.util.Arrays;
  9. 9 import java.util.Collections;
  10. 10 import java.util.HashMap;
  11. 11 import java.util.HashSet;
  12. 12 import java.util.Map;
  13. 13 import java.util.Set;
  14. 14 import java.util.List;
  15. 15 import java.util.WeakHashMap;
  16. 16 import sun.misc.ProxyGenerator;
  17. 17 import sun.reflect.CallerSensitive;
  18. 18 import sun.reflect.Reflection;
  19. 19 import sun.reflect.misc.ReflectUtil;
  20. 20 import sun.security.util.SecurityConstants;
  21. 21
  22. 22 /**
  23. 23 * Proxy类提供了用于创建代理对象和实例的静态方法,
  24. 24 并且它也是所有通过这些静态方法创建出的代理类的父类。
  25. 25 *
  26. 26 * 从某个接口创建代理对象的方法如下:
  27. 27 *
  28. 28 * InvocationHandler handler = new MyInvocationHandler(...);
  29. 29 * Class proxyClass = Proxy.getProxyClass(
  30. 30 * Foo.class.getClassLoader(), new Class[] { Foo.class });
  31. 31 * Foo f = (Foo) proxyClass.
  32. 32 * getConstructor(new Class[] { InvocationHandler.class }).
  33. 33 * newInstance(new Object[] { handler });
  34. 34 * 或者有更简单的方法
  35. 35 * Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
  36. 36 * new Class[] { Foo.class }, handler);
  37. 37 *
  38. 38 * 一个动态代理类(以下简称为代理类)是该类被创建时在运行时动态
  39. 39 * 实现一系列接口的类,有如下的特性。
  40. 40 *
  41. 41 * 动态代理接口是被动态类实现的接口。
  42. 42 * 代理实例是代理类一个一个实例。
  43. 43 *
  44. 44 * 每个代理实例都和一个实现了InvocationHandler接口的invocation handler对象关联。
  45. 45 * 通过代理接口进行的代理类实例的方法调用将会被分发给invocation handler实例,同时传递的
  46. 46 * 参数有代理实例,确定被调用方法的Method对象和Object数组类型的参数。
  47. 47 *
  48. 48 *
  49. 49 * containing the arguments. The invocation handler processes the
  50. 50 * encoded method invocation as appropriate and the result that it
  51. 51 * returns will be returned as the result of the method invocation on
  52. 52 * the proxy instance.
  53. 53 *
  54. 54 * <p>A proxy class has the following properties:
  55. 55 *
  56. 56 * <ul>
  57. 57 * <li>Proxy classes are public, final, and not abstract.
  58. 58 *
  59. 59 * <li>The unqualified name of a proxy class is unspecified. The space
  60. 60 * of class names that begin with the string {@code "$Proxy"}
  61. 61 * should be, however, reserved for proxy classes.
  62. 62 *
  63. 63 * <li>A proxy class extends {@code java.lang.reflect.Proxy}.
  64. 64 *
  65. 65 * <li>A proxy class implements exactly the interfaces specified at its
  66. 66 * creation, in the same order.
  67. 67 *
  68. 68 * <li>If a proxy class implements a non-public interface, then it will
  69. 69 * be defined in the same package as that interface. Otherwise, the
  70. 70 * package of a proxy class is also unspecified. Note that package
  71. 71 * sealing will not prevent a proxy class from being successfully defined
  72. 72 * in a particular package at runtime, and neither will classes already
  73. 73 * defined by the same class loader and the same package with particular
  74. 74 * signers.
  75. 75 *
  76. 76 * <li>Since a proxy class implements all of the interfaces specified at
  77. 77 * its creation, invoking {@code getInterfaces} on its
  78. 78 * {@code Class} object will return an array containing the same
  79. 79 * list of interfaces (in the order specified at its creation), invoking
  80. 80 * {@code getMethods} on its {@code Class} object will return
  81. 81 * an array of {@code Method} objects that include all of the
  82. 82 * methods in those interfaces, and invoking {@code getMethod} will
  83. 83 * find methods in the proxy interfaces as would be expected.
  84. 84 *
  85. 85 * <li>The {@link Proxy#isProxyClass Proxy.isProxyClass} method will
  86. 86 * return true if it is passed a proxy class-- a class returned by
  87. 87 * {@code Proxy.getProxyClass} or the class of an object returned by
  88. 88 * {@code Proxy.newProxyInstance}-- and false otherwise.
  89. 89 *
  90. 90 * <li>The {@code java.security.ProtectionDomain} of a proxy class
  91. 91 * is the same as that of system classes loaded by the bootstrap class
  92. 92 * loader, such as {@code java.lang.Object}, because the code for a
  93. 93 * proxy class is generated by trusted system code. This protection
  94. 94 * domain will typically be granted
  95. 95 * {@code java.security.AllPermission}.
  96. 96 *
  97. 97 * <li>Each proxy class has one public constructor that takes one argument,
  98. 98 * an implementation of the interface {@link InvocationHandler}, to set
  99. 99 * the invocation handler for a proxy instance. Rather than having to use
  100. 100 * the reflection API to access the public constructor, a proxy instance
  101. 101 * can be also be created by calling the {@link Proxy#newProxyInstance
  102. 102 * Proxy.newProxyInstance} method, which combines the actions of calling
  103. 103 * {@link Proxy#getProxyClass Proxy.getProxyClass} with invoking the
  104. 104 * constructor with an invocation handler.
  105. 105 * </ul>
  106. 106 *
  107. 107 * <p>A proxy instance has the following properties:
  108. 108 *
  109. 109 * <ul>
  110. 110 * <li>Given a proxy instance {@code proxy} and one of the
  111. 111 * interfaces implemented by its proxy class {@code Foo}, the
  112. 112 * following expression will return true:
  113. 113 * <pre>
  114. 114 * {@code proxy instanceof Foo}
  115. 115 * </pre>
  116. 116 * and the following cast operation will succeed (rather than throwing
  117. 117 * a {@code ClassCastException}):
  118. 118 * <pre>
  119. 119 * {@code (Foo) proxy}
  120. 120 * </pre>
  121. 121 *
  122. 122 * <li>Each proxy instance has an associated invocation handler, the one
  123. 123 * that was passed to its constructor. The static
  124. 124 * {@link Proxy#getInvocationHandler Proxy.getInvocationHandler} method
  125. 125 * will return the invocation handler associated with the proxy instance
  126. 126 * passed as its argument.
  127. 127 *
  128. 128 * <li>An interface method invocation on a proxy instance will be
  129. 129 * encoded and dispatched to the invocation handler's {@link
  130. 130 * InvocationHandler#invoke invoke} method as described in the
  131. 131 * documentation for that method.
  132. 132 *
  133. 133 * <li>An invocation of the {@code hashCode},
  134. 134 * {@code equals}, or {@code toString} methods declared in
  135. 135 * {@code java.lang.Object} on a proxy instance will be encoded and
  136. 136 * dispatched to the invocation handler's {@code invoke} method in
  137. 137 * the same manner as interface method invocations are encoded and
  138. 138 * dispatched, as described above. The declaring class of the
  139. 139 * {@code Method} object passed to {@code invoke} will be
  140. 140 * {@code java.lang.Object}. Other public methods of a proxy
  141. 141 * instance inherited from {@code java.lang.Object} are not
  142. 142 * overridden by a proxy class, so invocations of those methods behave
  143. 143 * like they do for instances of {@code java.lang.Object}.
  144. 144 * </ul>
  145. 145 *
  146. 146 * <h3>Methods Duplicated in Multiple Proxy Interfaces</h3>
  147. 147 *
  148. 148 * <p>When two or more interfaces of a proxy class contain a method with
  149. 149 * the same name and parameter signature, the order of the proxy class's
  150. 150 * interfaces becomes significant. When such a <i>duplicate method</i>
  151. 151 * is invoked on a proxy instance, the {@code Method} object passed
  152. 152 * to the invocation handler will not necessarily be the one whose
  153. 153 * declaring class is assignable from the reference type of the interface
  154. 154 * that the proxy's method was invoked through. This limitation exists
  155. 155 * because the corresponding method implementation in the generated proxy
  156. 156 * class cannot determine which interface it was invoked through.
  157. 157 * Therefore, when a duplicate method is invoked on a proxy instance,
  158. 158 * the {@code Method} object for the method in the foremost interface
  159. 159 * that contains the method (either directly or inherited through a
  160. 160 * superinterface) in the proxy class's list of interfaces is passed to
  161. 161 * the invocation handler's {@code invoke} method, regardless of the
  162. 162 * reference type through which the method invocation occurred.
  163. 163 *
  164. 164 * <p>If a proxy interface contains a method with the same name and
  165. 165 * parameter signature as the {@code hashCode}, {@code equals},
  166. 166 * or {@code toString} methods of {@code java.lang.Object},
  167. 167 * when such a method is invoked on a proxy instance, the
  168. 168 * {@code Method} object passed to the invocation handler will have
  169. 169 * {@code java.lang.Object} as its declaring class. In other words,
  170. 170 * the public, non-final methods of {@code java.lang.Object}
  171. 171 * logically precede all of the proxy interfaces for the determination of
  172. 172 * which {@code Method} object to pass to the invocation handler.
  173. 173 *
  174. 174 * <p>Note also that when a duplicate method is dispatched to an
  175. 175 * invocation handler, the {@code invoke} method may only throw
  176. 176 * checked exception types that are assignable to one of the exception
  177. 177 * types in the {@code throws} clause of the method in <i>all</i> of
  178. 178 * the proxy interfaces that it can be invoked through. If the
  179. 179 * {@code invoke} method throws a checked exception that is not
  180. 180 * assignable to any of the exception types declared by the method in one
  181. 181 * of the proxy interfaces that it can be invoked through, then an
  182. 182 * unchecked {@code UndeclaredThrowableException} will be thrown by
  183. 183 * the invocation on the proxy instance. This restriction means that not
  184. 184 * all of the exception types returned by invoking
  185. 185 * {@code getExceptionTypes} on the {@code Method} object
  186. 186 * passed to the {@code invoke} method can necessarily be thrown
  187. 187 * successfully by the {@code invoke} method.
  188. 188 *
  189. 189 * @author Peter Jones
  190. 190 * @see InvocationHandler
  191. 191 * @since 1.3
  192. 192 */
  193. 193
  194. 194 // 代理对象是可以序列化的
  195. 195 public class Proxy implements java.io.Serializable {
  196. 196
  197. 197 private static final long serialVersionUID = -2222568056686623797L;
  198. 198
  199. 199 /** 为所有代理类的类名添加前缀,难怪hibernate中很多类都有$Proxy */
  200. 200 private final static String proxyClassNamePrefix = "$Proxy";
  201. 201
  202. 202 /** 代理类构造方法的参数类型 */
  203. 203 private final static Class[] constructorParams =
  204. 204 { InvocationHandler.class };
  205. 205
  206. 206 /** 将代理类的类加载器放在Proxy的maps结构中缓存起来 */
  207. 207 /** 为啥这里用了WeakHashMap() */
  208. 208 private static Map<ClassLoader, Map<List<String>, Object>> loaderToCache
  209. 209 = new WeakHashMap<>();
  210. 210
  211. 211 /** 用于标记正在生成的代理类 */
  212. 212 private static Object pendingGenerationMarker = new Object();
  213. 213
  214. 214 /** 下一个用于生成唯一代理类名字的数字 */
  215. 215 private static long nextUniqueNumber = 0;
  216. 216 private static Object nextUniqueNumberLock = new Object();
  217. 217
  218. 218 /** 所有代理类的集合,用于isProxyClass方法的实现 */
  219. 219 private static Map<Class<?>, Void> proxyClasses =
  220. 220 Collections.synchronizedMap(new WeakHashMap<Class<?>, Void>());
  221. 221
  222. 222 /**
  223. 223 * 代理实例的invocationHandler对象,原来放在这儿啊.
  224. 224 * @serial
  225. 225 */
  226. 226 protected InvocationHandler h;
  227. 227
  228. 228 /**
  229. 229 * 禁止实例化
  230. 230 */
  231. 231 private Proxy() {
  232. 232 }
  233. 233
  234. 234 /**
  235. 235 * 从一个子类中创建代理类的实例(最典型的是一个动态代理类)
  236. 236 * 构造方法的参数是用来指定invocation handler
  237. 237 * 这个方法还是受保护的呢
  238. 238 */
  239. 239 protected Proxy(InvocationHandler h) {
  240. 240 doNewInstanceCheck();
  241. 241 this.h = h;
  242. 242 }
  243. 243
  244. 244 /**
  245. 245 * 为什么不注释这个方法?
  246. 246 * 类名大致意思是代理访问助手?
  247. 247 * 这还是个静态的内部类
  248. 248 */
  249. 249 private static class ProxyAccessHelper {
  250. 250 // The permission is implementation specific.
  251. 251 // 权限是指定实现的。
  252. 252 static final Permission PROXY_PERMISSION =
  253. 253 new ReflectPermission("proxyConstructorNewInstance");
  254. 254 // These system properties are defined to provide a short-term
  255. 255 // workaround if customers need to disable the new security checks.
  256. 256 // 这些系统配置被定义用来提供短期的配置,如果用户需要禁止新的安全检查。
  257. 257 static final boolean allowNewInstance;
  258. 258 static final boolean allowNullLoader;
  259. 259 static {
  260. 260 allowNewInstance = getBooleanProperty("sun.reflect.proxy.allowsNewInstance");
  261. 261 allowNullLoader = getBooleanProperty("sun.reflect.proxy.allowsNullLoader");
  262. 262 }
  263. 263
  264. 264 // 方法参数还可以这样修饰final?
  265. 265 private static boolean getBooleanProperty(final String key) {
  266. 266 String s = AccessController.doPrivileged(new PrivilegedAction<String>() {
  267. 267 public String run() {
  268. 268 return System.getProperty(key);
  269. 269 }
  270. 270 });
  271. 271 return Boolean.valueOf(s);
  272. 272 }
  273. 273
  274. 274 // 需要进行创建新实例的检查么
  275. 275 static boolean needsNewInstanceCheck(Class<?> proxyClass) {
  276. 276 if (!Proxy.isProxyClass(proxyClass) || allowNewInstance) {
  277. 277 return false;
  278. 278 }
  279. 279
  280. 280 if (ReflectUtil.isNonPublicProxyClass(proxyClass)) {
  281. 281 for (Class<?> intf : proxyClass.getInterfaces()) {
  282. 282 if (!Modifier.isPublic(intf.getModifiers())) {
  283. 283 return true;
  284. 284 }
  285. 285 }
  286. 286 }
  287. 287 return false;
  288. 288 }
  289. 289 }
  290. 290
  291. 291 /*
  292. 292 * Access check on a proxy class that implements any non-public interface.
  293. 293 *
  294. 294 * @throws SecurityException if a security manager exists, and
  295. 295 * the caller does not have the permission.
  296. 296 */
  297. 297 private void doNewInstanceCheck() {
  298. 298 SecurityManager sm = System.getSecurityManager();
  299. 299 Class<?> proxyClass = this.getClass();
  300. 300 if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(proxyClass)) {
  301. 301 try {
  302. 302 sm.checkPermission(ProxyAccessHelper.PROXY_PERMISSION);
  303. 303 } catch (SecurityException e) {
  304. 304 throw new SecurityException("Not allowed to construct a Proxy "
  305. 305 + "instance that implements a non-public interface", e);
  306. 306 }
  307. 307 }
  308. 308 }
  309. 309
  310. 310 /**
  311. 311 * 这个方法是重头了
  312. 312 * 从给定的类加载器和接口数组返回动态代理类的Class对象。
  313. 313 * Returns the {@code java.lang.Class} object for a proxy class
  314. 314 * given a class loader and an array of interfaces.
  315. 315 * 代理将会通过指定的类加载器所定义而且将会实现所提供的所有的接口。
  316. 316 *
  317. 317 * 如果已经有实现了相同接口的代理类存在,那么将会直接放回已经存在的代理类;
  318. 318 * 否则,将会动态地创建和由类加载器定义的代理类被返回。
  319. 319
  320. 320 * 对于传递给Proxy.getProxyClass方法的参数有若干限制。
  321. 321 *
  322. 322 * 在数组中所有的类类型必须是接口,不是其他的类类型或者原始类型。
  323. 323 * 接口数组中的接口必须两两不同
  324. 324 * 所有的接口类型都可以通过指定的类加载器按名字进行加载。
  325. 325 * 也就是对于数组中的每个接口i如下判断必须为true
  326. 326 * Class.forName(i.getName(), false, cl) == i
  327. 327 *
  328. 328 * 所有非公开的接口都必须在相同的包里面;不然的话,代理类不可能实现所有的接口,
  329. 329 * 不论它是在哪个包里面定义的。
  330. 330 *
  331. 331 * 对于指定接口的成员方法的集合的中的方法必须有相同的签名。
  332. 332 *
  333. 333 * 如果任何方法的返回类型是原始类型或者为空,那么所有的的方法都必须有相同的返回类型。
  334. 334 *
  335. 335 * 不然,其中一个方法必须可以对其他方法的返回类型是可赋值的。
  336. 336 *
  337. 337 * 生成的代理类不能超过虚拟机对其中的类的限制。例如,虚拟机会限制一个类的接口个数不超过65535;
  338. 338 * 因此传进去的接口的数组的长度不能超过65535
  339. 339 *
  340. 340 * 如果以上约束有违反的话,将会抛出IllegalArgumentException异常。
  341. 341 * 如果有接口是null的话,就会报NullPointerException异常。
  342. 342 *
  343. 343 * 注意到特定的代理接口的顺序是有意义的:对于创建具有相同代理接口的不同顺序的请求生成的代理类是不同的两个
  344. 344 * 代理类。(卧槽,这个也有影响,不读下源码一脸懵逼)
  345. 345 */
  346. 346 @CallerSensitive
  347. 347 public static Class<?> getProxyClass(ClassLoader loader,
  348. 348 Class<?>... interfaces)
  349. 349 throws IllegalArgumentException
  350. 350 {
  351. 351 // 安全管理
  352. 352 SecurityManager sm = System.getSecurityManager();
  353. 353 if (sm != null) {
  354. 354 checkProxyAccess(Reflection.getCallerClass(), loader, interfaces);
  355. 355 }
  356. 356
  357. 357 // 返回代理对象
  358. 358 return getProxyClass0(loader, interfaces);
  359. 359 }
  360. 360
  361. 361 /*
  362. 362 * Check permissions required to create a Proxy class.
  363. 363 *
  364. 364 * To define a proxy class, it performs the access checks as in
  365. 365 * Class.forName (VM will invoke ClassLoader.checkPackageAccess):
  366. 366 * 1. "getClassLoader" permission check if loader == null
  367. 367 * 2. checkPackageAccess on the interfaces it implements
  368. 368 *
  369. 369 * To get a constructor and new instance of a proxy class, it performs
  370. 370 * the package access check on the interfaces it implements
  371. 371 * as in Class.getConstructor.
  372. 372 *
  373. 373 * If an interface is non-public, the proxy class must be defined by
  374. 374 * the defining loader of the interface. If the caller's class loader
  375. 375 * is not the same as the defining loader of the interface, the VM
  376. 376 * will throw IllegalAccessError when the generated proxy class is
  377. 377 * being defined via the defineClass0 method.
  378. 378 */
  379. 379 private static void checkProxyAccess(Class<?> caller,
  380. 380 ClassLoader loader,
  381. 381 Class<?>... interfaces)
  382. 382 {
  383. 383 SecurityManager sm = System.getSecurityManager();
  384. 384 if (sm != null) {
  385. 385 ClassLoader ccl = caller.getClassLoader();
  386. 386 if (loader == null && ccl != null) {
  387. 387 if (!ProxyAccessHelper.allowNullLoader) {
  388. 388 sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);
  389. 389 }
  390. 390 }
  391. 391 ReflectUtil.checkProxyPackageAccess(ccl, interfaces);
  392. 392 }
  393. 393 }
  394. 394
  395. 395 /**
  396. 396 * Generate a proxy class. Must call the checkProxyAccess method
  397. 397 * to perform permission checks before calling this.
  398. 398 */
  399. 399 private static Class<?> getProxyClass0(ClassLoader loader,
  400. 400 Class<?>... interfaces) {
  401. 401
  402. 402 // 判断接口的个数
  403. 403 if (interfaces.length > 65535) {
  404. 404 throw new IllegalArgumentException("interface limit exceeded");
  405. 405 }
  406. 406
  407. 407 // 代理类
  408. 408 Class<?> proxyClass = null;
  409. 409
  410. 410 /* 接口名称的集合用来作为代理类的缓存 */
  411. 411 String[] interfaceNames = new String[interfaces.length];
  412. 412
  413. 413 // 检查接口是否有重复的
  414. 414 Set<Class<?>> interfaceSet = new HashSet<>();
  415. 415
  416. 416 for (int i = 0; i < interfaces.length; i++) {
  417. 417 /*
  418. 418 * 验证类加载器对接口名称的解析是同一个对象
  419. 419 * 因为不同类加载加载的类是不同的
  420. 420 */
  421. 421
  422. 422 // 获取接口的名字
  423. 423 String interfaceName = interfaces[i].getName();
  424. 424 Class<?> interfaceClass = null;
  425. 425 try {
  426. 426 // 拿到接口的类类型
  427. 427 interfaceClass = Class.forName(interfaceName, false, loader);
  428. 428 } catch (ClassNotFoundException e) {
  429. 429 }
  430. 430
  431. 431 // 两个类类型之间的比较
  432. 432 if (interfaceClass != interfaces[i]) {
  433. 433 throw new IllegalArgumentException(
  434. 434 interfaces[i] + " is not visible from class loader");
  435. 435 }
  436. 436
  437. 437 /*
  438. 438 * Verify that the Class object actually represents an
  439. 439 * interface.
  440. 440 */
  441. 441 if (!interfaceClass.isInterface()) {
  442. 442 throw new IllegalArgumentException(
  443. 443 interfaceClass.getName() + " is not an interface");
  444. 444 }
  445. 445
  446. 446 /*
  447. 447 * Verify that this interface is not a duplicate.
  448. 448 */
  449. 449 if (interfaceSet.contains(interfaceClass)) {
  450. 450 throw new IllegalArgumentException(
  451. 451 "repeated interface: " + interfaceClass.getName());
  452. 452 }
  453. 453
  454. 454 // 将接口加入到集合中
  455. 455 interfaceSet.add(interfaceClass);
  456. 456
  457. 457 interfaceNames[i] = interfaceName;
  458. 458 }
  459. 459
  460. 460 /*
  461. 461 * 使用字符串代表代理接口作为代理类缓存的主键(而不是它们的类对象)
  462. 462 * 已经足够了因为我们要求代理接口必须能根据给定的类加载器通过名字被解析,
  463. 463 * 而且还有一个好处是,使用字符串代表类弥补了对类的隐式弱引用。
  464. 464 */
  465. 465
  466. 466 // 将数组转为字符串
  467. 467 List<String> key = Arrays.asList(interfaceNames);
  468. 468
  469. 469 /*
  470. 470 * 根据类加载器找到或者创建代理类的缓存.
  471. 471 */
  472. 472 Map<List<String>, Object> cache;
  473. 473 synchronized (loaderToCache) {
  474. 474 cache = loaderToCache.get(loader);
  475. 475 if (cache == null) {
  476. 476 cache = new HashMap<>();
  477. 477 loaderToCache.put(loader, cache);
  478. 478 }
  479. 479 /*
  480. 480 * 这个映射在这个方法执行时一直有效,不用过多的同步,
  481. 481 * 因为仅仅会在类加载器不可达的时候被移除。
  482. 482 */
  483. 483 }
  484. 484
  485. 485 /*
  486. 486 * Look up the list of interfaces in the proxy class cache using
  487. 487 * the key. This lookup will result in one of three possible
  488. 488 * kinds of values:
  489. 489 * null, if there is currently no proxy class for the list of
  490. 490 * interfaces in the class loader,
  491. 491 * the pendingGenerationMarker object, if a proxy class for the
  492. 492 * list of interfaces is currently being generated,
  493. 493 * or a weak reference to a Class object, if a proxy class for
  494. 494 * the list of interfaces has already been generated.
  495. 495 */
  496. 496 synchronized (cache) {
  497. 497 /*
  498. 498 * Note that we need not worry about reaping the cache for
  499. 499 * entries with cleared weak references because if a proxy class
  500. 500 * has been garbage collected, its class loader will have been
  501. 501 * garbage collected as well, so the entire cache will be reaped
  502. 502 * from the loaderToCache map.
  503. 503 */
  504. 504 do {
  505. 505 Object value = cache.get(key);
  506. 506 if (value instanceof Reference) {
  507. 507 proxyClass = (Class<?>) ((Reference) value).get();
  508. 508 }
  509. 509 if (proxyClass != null) {
  510. 510 // proxy class already generated: return it
  511. 511 return proxyClass;
  512. 512 } else if (value == pendingGenerationMarker) {
  513. 513 // proxy class being generated: wait for it
  514. 514 try {
  515. 515 cache.wait();
  516. 516 } catch (InterruptedException e) {
  517. 517 /*
  518. 518 * The class generation that we are waiting for should
  519. 519 * take a small, bounded time, so we can safely ignore
  520. 520 * thread interrupts here.
  521. 521 */
  522. 522 }
  523. 523 continue;
  524. 524 } else {
  525. 525 /*
  526. 526 * No proxy class for this list of interfaces has been
  527. 527 * generated or is being generated, so we will go and
  528. 528 * generate it now. Mark it as pending generation.
  529. 529 */
  530. 530 cache.put(key, pendingGenerationMarker);
  531. 531 break;
  532. 532 }
  533. 533 } while (true);
  534. 534 }
  535. 535
  536. 536 try {
  537. 537 String proxyPkg = null; // package to define proxy class in
  538. 538
  539. 539 /*
  540. 540 * Record the package of a non-public proxy interface so that the
  541. 541 * proxy class will be defined in the same package. Verify that
  542. 542 * all non-public proxy interfaces are in the same package.
  543. 543 */
  544. 544 for (int i = 0; i < interfaces.length; i++) {
  545. 545 int flags = interfaces[i].getModifiers();
  546. 546 if (!Modifier.isPublic(flags)) {
  547. 547 String name = interfaces[i].getName();
  548. 548 int n = name.lastIndexOf('.');
  549. 549 String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
  550. 550 if (proxyPkg == null) {
  551. 551 proxyPkg = pkg;
  552. 552 } else if (!pkg.equals(proxyPkg)) {
  553. 553 throw new IllegalArgumentException(
  554. 554 "non-public interfaces from different packages");
  555. 555 }
  556. 556 }
  557. 557 }
  558. 558
  559. 559 if (proxyPkg == null) {
  560. 560 // if no non-public proxy interfaces, use com.sun.proxy package
  561. 561 proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
  562. 562 }
  563. 563
  564. 564 {
  565. 565 /*
  566. 566 * Choose a name for the proxy class to generate.
  567. 567 */
  568. 568 long num;
  569. 569 synchronized (nextUniqueNumberLock) {
  570. 570 num = nextUniqueNumber++;
  571. 571 }
  572. 572 String proxyName = proxyPkg + proxyClassNamePrefix + num;
  573. 573 /*
  574. 574 * Verify that the class loader hasn't already
  575. 575 * defined a class with the chosen name.
  576. 576 */
  577. 577
  578. 578 /*
  579. 579 * 生成指定的代理类,可以看出这里的是二进制类型
  580. 580 * ProxyGenerator的源码看不到了
  581. 581 */
  582. 582 byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
  583. 583 proxyName, interfaces);
  584. 584 try {
  585. 585 // defineClass0是一个本地方法
  586. 586 proxyClass = defineClass0(loader, proxyName,
  587. 587 proxyClassFile, 0, proxyClassFile.length);
  588. 588 } catch (ClassFormatError e) {
  589. 589 /*
  590. 590 * A ClassFormatError here means that (barring bugs in the
  591. 591 * proxy class generation code) there was some other
  592. 592 * invalid aspect of the arguments supplied to the proxy
  593. 593 * class creation (such as virtual machine limitations
  594. 594 * exceeded).
  595. 595 */
  596. 596 throw new IllegalArgumentException(e.toString());
  597. 597 }
  598. 598 }
  599. 599 // 将其加入到所有生成的代理类的集合中,用于判断是否是代理类.
  600. 600 proxyClasses.put(proxyClass, null);
  601. 601
  602. 602 } finally {
  603. 603 /*
  604. 604 * We must clean up the "pending generation" state of the proxy
  605. 605 * class cache entry somehow. If a proxy class was successfully
  606. 606 * generated, store it in the cache (with a weak reference);
  607. 607 * otherwise, remove the reserved entry. In all cases, notify
  608. 608 * all waiters on reserved entries in this cache.
  609. 609 */
  610. 610 synchronized (cache) {
  611. 611 if (proxyClass != null) {
  612. 612 cache.put(key, new WeakReference<Class<?>>(proxyClass));
  613. 613 } else {
  614. 614 cache.remove(key);
  615. 615 }
  616. 616 cache.notifyAll();
  617. 617 }
  618. 618 }
  619. 619
  620. 620 // 返回代理类
  621. 621 return proxyClass;
  622. 622 }
  623. 623
  624. 624 @CallerSensitive
  625. 625 public static Object newProxyInstance(ClassLoader loader,
  626. 626 Class<?>[] interfaces,
  627. 627 InvocationHandler h)
  628. 628 throws IllegalArgumentException
  629. 629 {
  630. 630 if (h == null) {
  631. 631 throw new NullPointerException();
  632. 632 }
  633. 633
  634. 634 // 安全检查
  635. 635 final SecurityManager sm = System.getSecurityManager();
  636. 636 if (sm != null) {
  637. 637 checkProxyAccess(Reflection.getCallerClass(), loader, interfaces);
  638. 638 }
  639. 639
  640. 640 /*
  641. 641 * 查找或者生成指定的代理类
  642. 642 */
  643. 643 Class<?> cl = getProxyClass0(loader, interfaces);
  644. 644
  645. 645 /*
  646. 646 * 调用指定了invocation handler的构造方法。
  647. 647 */
  648. 648 try {
  649. 649 final Constructor<?> cons = cl.getConstructor(constructorParams);
  650. 650 final InvocationHandler ih = h;
  651. 651 if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
  652. 652 // create proxy instance with doPrivilege as the proxy class may
  653. 653 // implement non-public interfaces that requires a special permission
  654. 654 return AccessController.doPrivileged(new PrivilegedAction<Object>() {
  655. 655 public Object run() {
  656. 656 return newInstance(cons, ih);
  657. 657 }
  658. 658 });
  659. 659 } else {
  660. 660 // 直接返回了代理对象
  661. 661 return newInstance(cons, ih);
  662. 662 }
  663. 663 } catch (NoSuchMethodException e) {
  664. 664 throw new InternalError(e.toString());
  665. 665 }
  666. 666 }
  667. 667
  668. 668 private static Object newInstance(Constructor<?> cons, InvocationHandler h) {
  669. 669 try {
  670. 670 return cons.newInstance(new Object[] {h} );
  671. 671 } catch (IllegalAccessException | InstantiationException e) {
  672. 672 throw new InternalError(e.toString());
  673. 673 } catch (InvocationTargetException e) {
  674. 674 Throwable t = e.getCause();
  675. 675 if (t instanceof RuntimeException) {
  676. 676 throw (RuntimeException) t;
  677. 677 } else {
  678. 678 throw new InternalError(t.toString());
  679. 679 }
  680. 680 }
  681. 681 }
  682. 682
  683. 683 /**
  684. 684 * Returns true if and only if the specified class was dynamically
  685. 685 * generated to be a proxy class using the {@code getProxyClass}
  686. 686 * method or the {@code newProxyInstance} method.
  687. 687 *
  688. 688 * <p>The reliability of this method is important for the ability
  689. 689 * to use it to make security decisions, so its implementation should
  690. 690 * not just test if the class in question extends {@code Proxy}.
  691. 691 *
  692. 692 * @param cl the class to test
  693. 693 * @return {@code true} if the class is a proxy class and
  694. 694 * {@code false} otherwise
  695. 695 * @throws NullPointerException if {@code cl} is {@code null}
  696. 696 */
  697. 697 public static boolean isProxyClass(Class<?> cl) {
  698. 698 if (cl == null) {
  699. 699 throw new NullPointerException();
  700. 700 }
  701. 701
  702. 702 return proxyClasses.containsKey(cl);
  703. 703 }
  704. 704
  705. 705 /**
  706. 706 * Returns the invocation handler for the specified proxy instance.
  707. 707 *
  708. 708 * @param proxy the proxy instance to return the invocation handler for
  709. 709 * @return the invocation handler for the proxy instance
  710. 710 * @throws IllegalArgumentException if the argument is not a
  711. 711 * proxy instance
  712. 712 */
  713. 713 @CallerSensitive
  714. 714 public static InvocationHandler getInvocationHandler(Object proxy)
  715. 715 throws IllegalArgumentException
  716. 716 {
  717. 717 /*
  718. 718 * Verify that the object is actually a proxy instance.
  719. 719 */
  720. 720 if (!isProxyClass(proxy.getClass())) {
  721. 721 throw new IllegalArgumentException("not a proxy instance");
  722. 722 }
  723. 723
  724. 724 final Proxy p = (Proxy) proxy;
  725. 725 final InvocationHandler ih = p.h;
  726. 726 if (System.getSecurityManager() != null) {
  727. 727 Class<?> ihClass = ih.getClass();
  728. 728 Class<?> caller = Reflection.getCallerClass();
  729. 729 if (ReflectUtil.needsPackageAccessCheck(caller.getClassLoader(),
  730. 730 ihClass.getClassLoader()))
  731. 731 {
  732. 732 ReflectUtil.checkPackageAccess(ihClass);
  733. 733 }
  734. 734 }
  735. 735
  736. 736 return ih;
  737. 737 }
  738. 738
  739. 739 private static native Class defineClass0(ClassLoader loader, String name,
  740. 740 byte[] b, int off, int len);
  741. 741 }

总结Proxy:

Java中的动态代理以及Proxy类的偷瞄的更多相关文章

  1. 使用Java中的动态代理实现数据库连接池

    2002 年 12 月 05 日 作者通过使用JAVA中的动态代理实现数据库连接池,使使用者可以以普通的jdbc连接的使用习惯来使用连接池. 数据库连接池在编写应用服务是经常需要用到的模块,太过频繁的 ...

  2. java中的动态代理机制

    java中的动态代理机制 在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface).另一个则是 Proxy(Class),这一个类和接口是实现 ...

  3. 十分钟理解Java中的动态代理

    十分钟理解 Java 中的动态代理   一.概述 1. 什么是代理 我们大家都知道微商代理,简单地说就是代替厂家卖商品,厂家“委托”代理为其销售商品.关于微商代理,首先我们从他们那里买东西时通常不知道 ...

  4. 深度剖析java中JDK动态代理机制

    https://www.jb51.net/article/110342.htm 本篇文章主要介绍了深度剖析java中JDK动态代理机制 ,动态代理避免了开发人员编写各个繁锁的静态代理类,只需简单地指定 ...

  5. 一文读懂Java中的动态代理

    从代理模式说起 回顾前文: 设计模式系列之代理模式(Proxy Pattern) 要读懂动态代理,应从代理模式说起.而实现代理模式,常见有下面两种实现: (1) 代理类关联目标对象,实现目标对象实现的 ...

  6. 浅谈代理模式与java中的动态代理

    代理模式的定义: 代理模式是一个使用律非常高的模式,定义如下: 为其他对象提供一种代理,以控制对这个对象的访问. 类图: 简单的静态代理: public interface IRunner{ //这是 ...

  7. java中的动态代理Proxy

    动态代理是java语言的一个神奇的地方,不是很好理解,下面来看看关键的地方. InvocationHandler 是一个接口,官方文档解释说,每个代理的实例都有一个与之关联的 InvocationHa ...

  8. java中的动态代理

    1.动态代理的定义:为其他对象提供一个代理以控制对这个对象的访问 2.通过api看下proxy生成代理类的2中写法: 创建某一接口 Foo 的代理: InvocationHandler handler ...

  9. 代理模式与java中的动态代理

    前言    代理模式又分为静态代理与动态代理,其中动态代理是Java各大框架中运用的最为广泛的一种模式之一,下面就用简单的例子来说明静态代理与动态代理. 场景    李雷是一个唱片公司的大老板,很忙, ...

随机推荐

  1. Android UI Material Design

    Material Design 中文版: http://wiki.jikexueyuan.com/project/material-design/ Material Design开发文章系列1:App ...

  2. JavaScript 的闭包用于什么场景

    本文翻译自 MDN ( Mozilla Developer Network ): 原文地址:MDN 译文地址:shixinzhang 的博客 读完本文你将了解到: 词法作用域 闭包 闭包实战场景之回调 ...

  3. 几种常用的json序列化和反序列化工具介绍

    一.前言 Json序列化和反序列化工作中会时常用到,也是目前数据交互的常用格式,Rest风格的接口加上json格式的数据交互,真的是天作之合. 目前Json字符与Json对象的相互转换方式有很多,接下 ...

  4. android多线程-AsyncTask之工作原理深入解析(下)

    关联文章: Android 多线程之HandlerThread 完全详解 Android 多线程之IntentService 完全详解 android多线程-AsyncTask之工作原理深入解析(上) ...

  5. [Linux] ubuntu 格式化u盘

    $sudo fdisks -l 基本功,格式化命令,以格式化 /dev/sdb4 分区为例:$ sudo umount /dev/sdb4    # 必须先卸载该分区 # 格式化为 FAT 分区$ s ...

  6. Mac的SourceTree安装时如何注册?

    目前安装SourceTree的过程中是必须注册的,但是注册又是国外的,因此需要FQ.但是未注册成功,是没有办法设置SourceTree的代理的,虽然不知道SourceTree有没有设置代理的功能,解决 ...

  7. php漏洞挖掘书籍

    PHP是一种被广泛使用的脚本语言,尤其适合web开发.具有跨平台,容易学习,功能强大等特点,据统计全世界超过34%的网站有php的应用,包括Yahoo.sina.163.sohu等大型门户网站.而且很 ...

  8. spring+mybatis项目启动报错Initializing Spring root WebApplicationContext

    这个问题很怪异,各种各样的情况都会导致这个问题的出现,主要是由于sping加载读取配置文件的时候出了问题.我在处理mybatis的时候出现了这个问题,后来排查发现,在mybatis的配置文件中如果有大 ...

  9. 【Bootstrap 多级菜单】

    参考资料: Bootstrap-submenu:http://www.html580.com/11848/demo Bootstrap-submenu:https://vsn4ik.github.io ...

  10. java 压缩和解压zip包

    网上有关压缩和解压zip包的博文一大堆,我随便找了一个.看了看,依照自己的须要改动了一下,与各位分享一下,希望各位大神指正: package com.wangpeng.utill; import ja ...