【Hello】动态代理

介绍
Spring AOP 的底层实现有两种方式:一种是 JDK 动态代理,另一种是 CGLib 动态代理。
JDK 动态代理主要涉及 java.lang.reflect 包下边的两个类:ProxyInvocationHandler 。其中 InvocationHandler 是一个接口,可以通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态地将横切逻辑和业务逻辑编织在一起。

JDK 动态代理的限制,是它只能为接口创建代理实例。对于没有通过接口定义业务方法的类,只能通过 CGLib 创建动态代理实例。

CGLib 动态代理采用底层的字节码技术,全称是 Code Generation Library ,CGLib 可以为一个类创建一个子类,在子类中采用方法拦截的技术拦截所有父类方法的调用并顺势织入横切逻辑。

动态代理

Proxy

public object invoke(Object obj, Method method, Object[] args)

  • obj : 代理对象
  • method : 代理的方法
  • args : 代理方法的参数

InvocationHandler

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

  • loader : 类加载器
  • interfaces : 实现接口
  • h : 事件处理器

JDK 动态代理

实现原理:

  • 通过实现 InvocationHandler 接口创建自己的调用处理器
  • 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理
  • 通过反射机制获取动态代理类的构造函数,其唯一参数类型就是调用处理器接口类型
  • 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数参入

JDK 动态代理是面向接口的代理模式,如果被代理目标没有接口那么 Spring 也无能为力,Spring 通过 Java 的反射机制生产被代理接口的新的匿名实现类,重写了其中 AOP 的增强方法。

CGLib 动态代理

实现原理:

CGLib 是一个强大、高性能的 Code 生产类库,可以实现运行期动态扩展 java 类,Spring 在运行期间通过 CGLib 继承要被动态代理的类,重写父类的方法,实现 AOP 面向切面编程呢。

Spring AOP 动态代理的方式选择

  • 如果要被代理的对象是个实现类,那么 Spring 会使用 JDK 动态代理来完成操作(Spirng 默认采用 JDK 动态代理实现机制)
  • 如果要被代理的对象不是个实现类,那么 Spring 会强制使用 CGLib 来实现动态代理

JDK、CGLib 两者比较

  • JDK 动态代理是面向接口的
  • CGLib 动态代理是通过字节码底层继承要代理类来实现(如果被代理类被 final 关键字所修饰,则无法实现)

CGLib 所创建的动态代理对象在实际运行时候的性能要比 JDK 动态代理高,CGLib 在创建对象的时候所花费的时间比 JDK 动态代理多。因此,对于单例的代理对象或者具有实例池的代理,因为无需频繁的创建代理对象,所以比较适合采用 CGLib 动态代理,反之则比较适用 JDK 动态代理。

测试

代码目录

  • Target.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.chen.dynamic;

/**
* 目标类的接口
*
* @Author LeifChen
* @Date 2018-09-27
*/
public interface Target {
/**
* 测试方法
*
* @param i
* @return
*/
int test(int i);
}
  • TargetImpl.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.chen.dynamic;

/**
* 目标类的实现
*
* @Author LeifChen
* @Date 2018-09-27
*/
public class TargetImpl implements Target {
@Override
public int test(int i) {
return i+1;
}
}
  • JdkDynamicProxy.java
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
package com.chen.dynamic;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
* JDK 动态代理
*
* @Author LeifChen
* @Date 2018-09-27
*/
public class JdkDynamicProxy implements InvocationHandler {

private Target target;

public JdkDynamicProxy(Target target) {
this.target = target;
}

/**
* 返回目标类的代理实例
* @param target
* @return
*/
public static Target newProxyInstance(Target target) {
return (Target) Proxy.newProxyInstance(JdkDynamicProxy.class.getClassLoader(),
new Class<?>[]{Target.class},
new JdkDynamicProxy(target));
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(target, args);
}
}
  • CglibProxy.java
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
package com.chen.dynamic;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
* CGLib 动态代理
*
* @Author LeifChen
* @Date 2018-09-27
*/
public class CglibProxy implements MethodInterceptor {

public CglibProxy() {
}

/**
* 获取目标类的代理实例
* @param targetInstanceClazz
* @param <T>
* @return
*/
public static <T extends Target> Target newProxyInstance(Class<T> targetInstanceClazz) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetInstanceClazz);
enhancer.setCallback(new CglibProxy());
return (Target) enhancer.create();
}

@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
return methodProxy.invokeSuper(obj, args);
}
}
  • ProxyPerformance.java
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
package com.chen.dynamic;

import java.util.LinkedHashMap;
import java.util.Map;

/**
* ProxyPerformance
*
* @Author LeifChen
* @Date 2018-09-27
*/
public class ProxyPerformance {

public static void main(String[] args) {
// 创建测试对象
Target nativeTest = new TargetImpl();
Target dynamicProxy = JdkDynamicProxy.newProxyInstance(nativeTest);
Target cglibProxy = CglibProxy.newProxyInstance(TargetImpl.class);

// 执行测试
Map<String, Target> tests = new LinkedHashMap<>();
tests.put("Native ", nativeTest);
tests.put("JDK Dynamic ", dynamicProxy);
tests.put("CGLib Dynamic", cglibProxy);
int repeatCount = 3;
int runCount = 1000000;
runTest(repeatCount, runCount, tests);
runCount = 50000000;
runTest(repeatCount, runCount, tests);
}

private static void runTest(int repeatCount, int runCount, Map<String, Target> tests) {
System.out.println(
String.format("=== run test : [repeatCount=%s] [runCount=%s] [java.version=%s] === ",
repeatCount, runCount, System.getProperty("java.version"))
);
for (int i = 0; i < repeatCount; i++) {
System.out.println(
String.format("--- test: [%s] ---", i + 1)
);
for (String key : tests.keySet()) {
runWithMonitor(tests.get(key), runCount, key);
}
}
}

private static void runWithMonitor(Target target, int runCount, String tag) {
long start = System.currentTimeMillis();
for (int i = 0; i < runCount; i++) {
target.test(i);
}
long end = System.currentTimeMillis();
System.out.println("[" + tag + "] Total Time: " + (end - start) + "ms");
}
}
  • 结果

测试结果

0%