本篇文章主要介绍了深度剖析java中JDK动态代理机制 ,动态代理避免了开发人员编写各个繁锁的静态代理类,只需简单地指定一组接口及目标类对象就能动态的获得代理对象。
 

摘要

相比于静态代理,动态代理避免了开发人员编写各个繁锁的静态代理类,只需简单地指定一组接口及目标类对象就能动态的获得代理对象。

代理模式

使用代理模式必须要让代理类和目标类实现相同的接口,客户端通过代理类来调用目标方法,代理类会将所有的方法调用分派到目标对象上反射执行,还可以在分派过程中添加"前置通知"和后置处理(如在调用目标方法前校验权限,在调用完目标方法后打印日志等)等功能。

使用动态代理的五大步骤

1.通过实现InvocationHandler接口来自定义自己的InvocationHandler;

2.通过Proxy.getProxyClass获得动态代理类

3.通过反射机制获得代理类的构造方法,方法签名为getConstructor(InvocationHandler.class)

4.通过构造函数获得代理对象并将自定义的InvocationHandler实例对象传为参数传入

5.通过代理对象调用目标方法

动态代理的使用

例1(方式一)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
public class MyProxy {
 
  public interface IHello{
 
    void sayHello();
 
  }
 
  static class Hello implements IHello{
 
    public void sayHello() {
 
      System.out.println("Hello world!!");
 
    }
 
  }
 
  //自定义InvocationHandler
 
  static class HWInvocationHandler implements InvocationHandler{
 
    //目标对象
 
    private Object target;
 
    public HWInvocationHandler(Object target){
 
      this.target = target;
 
    }
 
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 
      System.out.println("------插入前置通知代码-------------");
 
      //执行相应的目标方法
 
      Object rs = method.invoke(target,args);
 
      System.out.println("------插入后置处理代码-------------");
 
      return rs;
 
    }
 
  }
 
  public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetExc  eption, InstantiationException {
 
    //生成$Proxy0的class文件
 
    System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
 
    //获取动态代理类
 
    Class proxyClazz = Proxy.getProxyClass(IHello.class.getClassLoader(),IHello.class);
 
    //获得代理类的构造函数,并传入参数类型InvocationHandler.class
 
    Constructor constructor = proxyClazz.getConstructor(InvocationHandler.class);
 
    //通过构造函数来创建动态代理对象,将自定义的InvocationHandler实例传入
 
    IHello iHello = (IHello) constructor.newInstance(new HWInvocationHandler(new Hello()));
 
    //通过代理对象调用目标方法
 
    iHello.sayHello();
 
  }
 
}

输出:

------插入前置通知代码-------------
Hello world!!
------插入后置处理代码-------------

Proxy类中还有个将2~4步骤封装好的简便方法来创建动态代理对象,其方法签名为:newProxyInstance(ClassLoader loader,Class<?>[] instance, InvocationHandler h),如下例:

(方式二)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
 
    //生成$Proxy0的class文件
 
    System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
 
    IHello ihello = (IHello) Proxy.newProxyInstance(IHello.class.getClassLoader(), //加载接口的类加载器
 
        new Class[]{IHello.class},   //一组接口
 
        new HWInvocationHandler(new Hello())); //自定义的InvocationHandler
 
    ihello.sayHello();
 
  }

输出结果一样.

下面以newProxyInstance方法为切入点来剖析代理类的生成及代理方法的调用

(为了篇幅整洁去掉了次要的代码)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public static Object newProxyInstance(ClassLoader loader,
 
                     Class<!--?-->[] interfaces,
 
                     InvocationHandler h)
 
      throws IllegalArgumentException
 
  {
 
    if (h == null) {  //如果h为空直接抛出异常,所以InvocationHandler实例对象是必须的
 
      throw new NullPointerException();
 
    }
 
    //对象的拷贝,暂不知道这里拷贝下的意义是啥?
 
    final Class<!--?-->[] intfs = interfaces.clone();
 
    //一些安全的权限检查
 
    final SecurityManager sm = System.getSecurityManager();
 
    if (sm != null) {
 
      checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
 
    }
 
    //产生代理类
 
    Class<!--?--> cl = getProxyClass0(loader, intfs);
 
  
 
    //获取代理类的构造函数对象
 
    //参数constructorParames为常量值:private static final Class<!--?-->[] constructorParams = { InvocationHandler.class };
 
    final Constructor<!--?--> cons = cl.getConstructor(constructorParames);
 
    final InvocationHandler ih = h;
 
    //根据代理类的构造函数对象来创建代理类对象
 
    return newInstance(cons, ih);
 
        
 
  }

这段代码就是对代理类对象的创建,就是对例1中34~38行封装,其中getProxyClass0就是生成代理类的方法

getProxyClass0方法剖析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private static Class<!--?--> getProxyClass0(ClassLoader loader,
 
                      Class<!--?-->... interfaces) {
 
//接口数不得超过65535个
 
    if (interfaces.length > 65535) {
 
      throw new IllegalArgumentException("interface limit exceeded");
 
    }
 
//代理类缓存,如果缓存中有代理类了直接返回,否则将由ProxyClassFactory创建代理类
 
    return proxyClassCache.get(loader, interfaces);
 
  }

看看ProxyClassFactory是怎样生成代理类的?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
private static final class ProxyClassFactory
 
    implements BiFunction<classloader, class<?="">[], Class<!--?-->>
 
  {
 
    //统一代理类的前缀名都以$Proxy开关
 
    private static final String proxyClassNamePrefix = "$Proxy";
 
    //使用唯一的编号给作为代理类名的一部分,如$Proxy0,$Proxy1等
 
    private static final AtomicLong nextUniqueNumber = new AtomicLong();
 
    @Override
 
    public Class<!--?--> apply(ClassLoader loader, Class<!--?-->[] interfaces) {
 
      Map<class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
 
      for (Class<!--?--> intf : interfaces) {
 
        //验证指定的类加载器(loader)加载接口所得到的Class对象(interfaceClass)是否与intf对象相同
 
        Class<!--?--> interfaceClass = null;
 
        try {
 
          interfaceClass = Class.forName(intf.getName(), false, loader);
 
        } catch (ClassNotFoundException e) {
 
        }
 
        if (interfaceClass != intf) {
 
          throw new IllegalArgumentException(
 
            intf + " is not visible from class loader");
 
        }
 
        //验证该Class对象是不是接口
 
        if (!interfaceClass.isInterface()) {
 
          throw new IllegalArgumentException(
 
            interfaceClass.getName() + " is not an interface");
 
        }
 
        // 验证该接口是否重复了
 
        if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
 
          throw new IllegalArgumentException(
 
            "repeated interface: " + interfaceClass.getName());
 
        }
 
      }
 
         //声明代理类所在包
 
      String proxyPkg = null
 
      /*验证你传入的接口中是否有非public接口,只要有一个接口是非public的,那么这些接口都必须在同一包中
 
      这里的接口修饰符直接影响到System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true")所生成
 
      的代理类的路径,往下看!!*/
 
      for (Class<!--?--> intf : interfaces) {
 
        int flags = intf.getModifiers();
 
        if (!Modifier.isPublic(flags)) {
 
          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) {
 
        /*如果都是public接口,那么生成的代理类就在com.sun.proxy包下如果报java.io.FileNotFoundException: com\sun\proxy\$Proxy0.c         lass (系统找不到指定的路径。)的错误,就先在你项目中创建com.sun.proxy路径*/     
 
        proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
 
      }
 
  
 
       //将当前nextUniqueNumber的值以原子的方式的加1,所以第一次生成代理类的名字为$Proxy0.class
 
      long num = nextUniqueNumber.getAndIncrement();
 
      //代理类的完全限定名,如com.sun.proxy.$Proxy0.calss,
 
      String proxyName = proxyPkg + proxyClassNamePrefix + num;
 
      //生成代理类字节码文件      
 
      byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
 
        proxyName, interfaces);
 
      try {
 
        return defineClass0(loader, proxyName,
 
                  proxyClassFile, 0, proxyClassFile.length);
 
      } catch (ClassFormatError e) {
 
        throw new IllegalArgumentException(e.toString());
 
      }
 
    }
 
  }</class<?></classloader,>

而生成代理类字节码文件又主要通过ProxyGenerate的generateProxyClass(proxyName,interfaces)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public static byte[] generateProxyClass(final String var0, Class[] var1) {
 
    ProxyGenerator var2 = new ProxyGenerator(var0, var1);
 
    //生成代理类字节码文件的真正方法
 
    final byte[] var3 = var2.generateClassFile();
 
    //保存文件
 
    if(saveGeneratedFiles) {
 
      AccessController.doPrivileged(new PrivilegedAction() {
 
        public Void run() {
 
          try {
 
            FileOutputStream var1 = new FileOutputStream(ProxyGenerator.dotToSlash(var0) + ".class");
 
            var1.write(var3);
 
            var1.close();
 
            return null;
 
          } catch (IOException var2) {
 
            throw new InternalError("I/O exception saving generated file: " + var2);
 
          }
 
        }
 
      });
 
    }
 
    return var3;
 
  }

层层调用后,最终generateClassFile才是真正生成代理类字节码文件的方法,注意开头的三个addProxyMethod方法是只将Object的hashcode,equals,toString方法添加到代理方法容器中,代理类除此之外并没有重写其他Object的方法,所以除这三个方法外,代理类调用其他方法的行为与Object调用这些方法的行为一样不通过Invoke

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
private byte[] generateClassFile() {
 
    /addProxyMethod系列方法就是将接口的方法和Object的hashCode,equals,toString方法添加到代理方法容器(proxyMethods),
 
     其中方法签名作为key,proxyMethod作为value*/
 
    /*hashCodeMethod方法位于静态代码块中通过Object对象获得,hashCodeMethod=Object.class.getMethod("hashCode",new Class[0]),
 
     相当于从Object中继承过来了这三个方法equalsMethod,toStringMethod*/ 
 
    this.addProxyMethod(hashCodeMethod, Object.class);  -->
 
    this.addProxyMethod(equalsMethod, Object.class);
 
    this.addProxyMethod(toStringMethod, Object.class);
 
    int var1;
 
    int var3;
 
     //获得所有接口中的所有方法,并将方法添加到代理方法中
 
    for(var1 = 0; var1 < this.interfaces.length; ++var1) {
 
      Method[] var2 = this.interfaces[var1].getMethods();     
 
      for(var3 = 0; var3 < var2.length; ++var3) {
 
        this.addProxyMethod(var2[var3], this.interfaces[var1]);
 
      }
 
    }
 
      
 
    Iterator var7 = this.proxyMethods.values().iterator();
 
    List var8;
 
    while(var7.hasNext()) {
 
      var8 = (List)var7.next();
 
      checkReturnTypes(var8);  //验证具有相同方法签名的的方法的返回值类型是否一致,因为不可能有两个方法名相同,参数相同,而返回值却不同的方法
 
    };
 
  //接下来就是写代理类文件的步骤了
 
    Iterator var11
 
    try {
 
       //生成代理类的构造函数
 
      this.methods.add(this.generateConstructor());
 
      var7 = this.proxyMethods.values().iterator();
 
      while(var7.hasNext()) {
 
        var8 = (List)var7.next();
 
        var11 = var8.iterator();
 
        while(var11.hasNext()) {
 
          ProxyGenerator.ProxyMethod var4 = (ProxyGenerator.ProxyMethod)var11.next();
 
          /将代理字段声明为Method,10为ACC_PRIVATE和ACC_STATAIC的与运算,表示该字段的修饰符为private static
 
           所以代理类的字段都是private static Method XXX*/
 
          this.fields.add(new ProxyGenerator.FieldInfo(var4.methodFieldName, "Ljava/lang/reflect/Method;", 10));
 
          //生成代理类的代理方法
 
          this.methods.add(var4.generateMethod());
 
        }
 
      }
 
      //为代理类生成静态代码块,对一些字段进行初始化
 
      this.methods.add(this.generateStaticInitializer());
 
    } catch (IOException var6) {
 
      throw new InternalError("unexpected I/O Exception");
 
    }
 
     
 
    if(this.methods.size() > '\uffff') { //代理方法超过65535将抛出异常
 
      throw new IllegalArgumentException("method limit exceeded");
 
    } else if(this.fields.size() > '\uffff') {  //代理类的字段超过65535将抛出异常
 
      throw new IllegalArgumentException("field limit exceeded");
 
    } else {
 
     //这里开始就是一些代理类文件的过程,此过程略过
 
      this.cp.getClass(dotToSlash(this.className));
 
      this.cp.getClass("java/lang/reflect/Proxy");
 
      for(var1 = 0; var1 < this.interfaces.length; ++var1) {
 
        this.cp.getClass(dotToSlash(this.interfaces[var1].getName()));
 
      }
 
      this.cp.setReadOnly();
 
      ByteArrayOutputStream var9 = new ByteArrayOutputStream();
 
      DataOutputStream var10 = new DataOutputStream(var9);
 
      try {
 
        var10.writeInt(-889275714);
 
        var10.writeShort(0);
 
        var10.writeShort(49);
 
        this.cp.write(var10);
 
        var10.writeShort(49);
 
        var10.writeShort(this.cp.getClass(dotToSlash(this.className)));
 
        var10.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
 
        var10.writeShort(this.interfaces.length);
 
        for(var3 = 0; var3 < this.interfaces.length; ++var3) {
 
          var10.writeShort(this.cp.getClass(dotToSlash(this.interfaces[var3].getName())));
 
        }
 
        var10.writeShort(this.fields.size());
 
        var11 = this.fields.iterator();
 
        while(var11.hasNext()) {
 
          ProxyGenerator.FieldInfo var12 = (ProxyGenerator.FieldInfo)var11.next();
 
          var12.write(var10);
 
        }
 
        var10.writeShort(this.methods.size());
 
        var11 = this.methods.iterator();
 
        while(var11.hasNext()) {
 
          ProxyGenerator.MethodInfo var13 = (ProxyGenerator.MethodInfo)var11.next();
 
          var13.write(var10);
 
        }
 
        var10.writeShort(0);
 
        return var9.toByteArray();
 
      } catch (IOException var5) {
 
        throw new InternalError("unexpected I/O Exception");
 
      }
 
    }
 
  }

addProxyMethod方法剖析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
private void addProxyMethod(Method var1, Class var2) {
 
    String var3 = var1.getName(); //方法名
 
    Class[] var4 = var1.getParameterTypes();  //方法参数类型数组
 
    Class var5 = var1.getReturnType();  //返回值类型
 
    Class[] var6 = var1.getExceptionTypes();  //异常类型
 
    String var7 = var3 + getParameterDescriptors(var4);  //方法签名
 
    Object var8 = (List)this.proxyMethods.get(var7);  //根据方法签名却获得proxyMethods的Value
 
    if(var8 != null) {  //处理多个代理接口中重复的方法的情况
 
      Iterator var9 = ((List)var8).iterator();
 
      while(var9.hasNext()) {
 
        ProxyGenerator.ProxyMethod var10 = (ProxyGenerator.ProxyMethod)var9.next();
 
        if(var5 == var10.returnType) {
 
          /*归约异常类型以至于让重写的方法抛出合适的异常类型,我认为这里可能是多个接口中有相同的方法,而这些相同的方法抛出的异常类           型又不同,所以对这些相同方法抛出的异常进行了归约*/
 
          ArrayList var11 = new ArrayList();
 
          collectCompatibleTypes(var6, var10.exceptionTypes, var11);
 
          collectCompatibleTypes(var10.exceptionTypes, var6, var11);
 
          var10.exceptionTypes = new Class[var11.size()];
 
          //将ArrayList转换为Class对象数组
 
          var10.exceptionTypes = (Class[])var11.toArray(var10.exceptionTypes);
 
          return;
 
        }
 
      }
 
    } else {
 
      var8 = new ArrayList(3);
 
      this.proxyMethods.put(var7, var8);
 
    }  
 
    ((List)var8).add(new ProxyGenerator.ProxyMethod(var3, var4, var5, var6, var2, null));
 
    /*24~27行的意思就是如果var8为空,就创建一个数组,并以方法签名为key,proxymethod对象数组为value添加到proxyMethods*/
 
  }

InvocationHandler的作用

在动态代理中InvocationHandler是核心,每个代理实例都具有一个关联的调用处理程序(InvocationHandler)。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序(InvocationHandler)的 invoke 方法。所以对代理方法的调用都是通InvocationHadler的invoke来实现中,而invoke方法根据传入的代理对象,方法和参数来决定调用代理的哪个方法

invoke方法签名:invoke(Object Proxy,Method method,Object[] args)

 $Proxy0.class

来看看例1(MyProxy)的代理类是怎样的?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
public final class $Proxy0 extends Proxy implements IHello {  //继承了Proxy类和实现IHello接口
 
  //变量,都是private static Method XXX
 
  private static Method m3; 
 
  private static Method m1;
 
  private static Method m0;
 
  private static Method m2;
 
  //代理类的构造函数,其参数正是是InvocationHandler实例,Proxy.newInstance方法就是通过通过这个构造函数来创建代理实例的
 
  public $Proxy0(InvocationHandler var1) throws {
 
    super(var1);
 
  }
 
  //接口代理方法
 
  public final void sayHello() throws {
 
    try {
 
      super.h.invoke(this, m3, (Object[])null);
 
    } catch (RuntimeException | Error var2) {
 
      throw var2;
 
    } catch (Throwable var3) {
 
      throw new UndeclaredThrowableException(var3);
 
    }
 
  }
 
  //以下Object中的三个方法
 
  public final boolean equals(Object var1) throws {
 
    try {
 
      return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
 
    } catch (RuntimeException | Error var3) {
 
      throw var3;
 
    } catch (Throwable var4) {
 
      throw new UndeclaredThrowableException(var4);
 
    }
 
  }
 
  public final int hashCode() throws {
 
    try {
 
      return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
 
    } 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);
 
    }
 
  }
 
  //对变量进行一些初始化工作
 
  static {
 
    try {
 
      m3 = Class.forName("com.mobin.proxy.IHello").getMethod("sayHello", new Class[0]);
 
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
 
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
 
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
 
    } catch (NoSuchMethodException var2) {
 
      throw new NoSuchMethodError(var2.getMessage());
 
    } catch (ClassNotFoundException var3) {
 
      throw new NoClassDefFoundError(var3.getMessage());
 
    }
 
  }
 
}

以上就是对代理类如何生成,代理类方法如何被调用的分析!在很多框架都使用了动态代理如Spring,HDFS的RPC调用等等,分析过程中收获很多,如果想深入的了解JDK动态代理机制一定要深入到源码去剖析!!希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

深度剖析java中JDK动态代理机制的更多相关文章

  1. java中的动态代理机制

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

  2. java反射中的动态代理机制(有实例)

    在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一个就是AOP,对于IoC,依赖注入就不用多说了,而对于Spring的核心AOP来说,我们不但要知道怎么通过AOP来满足的 ...

  3. 深度剖析JDK动态代理机制

    摘要 相比于静态代理,动态代理避免了开发人员编写各个繁锁的静态代理类,只需简单地指定一组接口及目标类对象就能动态的获得代理对象. 代理模式 使用代理模式必须要让代理类和目标类实现相同的接口,客户端通过 ...

  4. Java 反射 设计模式 动态代理机制详解 [ 转载 ]

    Java 反射 设计模式 动态代理机制详解 [ 转载 ] @author 亦山 原文链接:http://blog.csdn.net/luanlouis/article/details/24589193 ...

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

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

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

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

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

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

  8. 利用JDK动态代理机制实现简单拦截器

    利用JDK动态代理机制实现简单的多层拦截器 首先JDK动态代理是基于接口实现的,所以我们先定义一个接口 public interface Executer { public Object execut ...

  9. Java中如何实现代理机制(JDK动态代理和cglib动态代理)

    http://blog.csdn.net/skiof007/article/details/52806714 JDK动态代理:代理类和目标类实现了共同的接口,用到InvocationHandler接口 ...

随机推荐

  1. Jmeter(四十七)_性能测试统计超时率

    概述 今天做一个性能测试的案例.需求中给出并发人数和业务时间段.根据2,5,8原则,统计响应超时率 实际场景 1:登录--考勤打卡--退出 2:并发人数200人,业务持续时间5分钟 3:要求登录响应时 ...

  2. 《京东上千页面搭建基石——CMS前后端分离演进史》阅读笔记

    一.背景 CMS即内容管理系统,目的是用于快速进行网站建设或者网页开发. 对于京东网站部门来说,CMS核心目的是用来快速开发和上线各种页面,诸如各种垂直频道页. 二.CMS核心目的 进行数据和模板的统 ...

  3. eclipse修改代码没用、debug无法进断点、

    设置eclipse自动编译代码 处理tomcat不进断点问题 添加需要debug的项目 以上操作还是不行的话   可以把tomcat删除重新添加试试看

  4. jquery实现select数据回显

    [html] view plain copy     <select class="div_select_re" id="edit_technicalGrade&q ...

  5. FZU Monthly-201906 tutorial

    FZU Monthly-201906 tutorial 题目(难度递增) easy easy-medium medium medium-hard hard 思维难度 AE B DG CF H A. X ...

  6. 【Beta】Scrum meeting 10

    目录 写在前面 进度情况 任务进度表 Beta-1阶段燃尽图 遇到的困难 照片 commit记录截图 后端代码 前端代码 技术博客 写在前面 例会时间:5.14 22:30-22:45 例会地点:微信 ...

  7. 冰多多团队-第五次Scrum会议

    冰多多团队-第五次Scrum会议 工作情况 团队成员 已完成任务 待完成任务 zpj 部分Action整合, 接入语音接口,整合项目解决兼容性问题 ASR bug修复 牛雅哲 跑通了科大讯飞语法识别的 ...

  8. test软件工程第三次作业

    零.前言 本次作业要求个人编写程序,截止日期2019年9月25日23:00. 请先阅读<构建之法>第一章至第三章的内容,并在下方作业里体现出阅读后的成果.特别是第2章中的效能分析及个人软件 ...

  9. JAVA字符编码二:Unicode,ISO-8859,GBK,UTF-8编码及相互转换

    第二篇:JAVA字符编码系列二:Unicode,ISO-8859-1,GBK,UTF-8编码及相互转换   1.函数介绍 在Java中,字符串用统一的Unicode编码,每个字符占用两个字节,与编码有 ...

  10. C# 序列化与反序列化Serialization之Json Xml Binary Soap JavaScript序列化

    所谓的序列化其实就是把一个内存中的对象信息转化成一个可以持久化保存的形式,方便保存数据库和文件或着用于传输, 序列化的主要作用是不同平台之间进行通信与信息的传递保存等,常用的有序列化有Json Xml ...