java 代理

1、JDK静态代理

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 org.jphoebe.edu.demo.proxy.staticProxy;

/**
* 静态代理
*/
public class UserProxy implements StaticPerson {

public static void main(String[] args) {
// 静态代理
StaticUser targetObj = new StaticUser();
new UserProxy(targetObj).exection();

}

public StaticUser user;

public UserProxy(StaticUser user) {
this.user = user;
}

@Override
public void exection() {
System.out.println("执行真正的代码前。。。。。。");
user.exection();
System.out.println("执行真正的代码后。。。。。。");
}

}
interface StaticPerson {
void exection();
}
class StaticUser implements StaticPerson {
@Override
public void exection() {
System.out.println("实际执行的代码");
}
}

2、JDK动态代理

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
package org.jphoebe.edu.demo.proxy.dynamic;

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

/**
* 动态代理
*/
public class ProxyHandle implements InvocationHandler {
public static void main(String[] args) {
// 参数:-Dsun.misc.ProxyGenerator.saveGeneratedFiles 可以让jdk生成代理后的class文件, 源码:ProxyGenerator.saveGeneratedFiles
DynamicUser targetObj = new DynamicUser();
ProxyHandle proxyHandle = new ProxyHandle(new DynamicUser());
DynamicPerson proxyObject = (DynamicPerson) Proxy.newProxyInstance(
DynamicUser.class.getClassLoader(),
DynamicUser.class.getInterfaces(),
proxyHandle);
proxyObject.exection();

}
private Object proxyObj;
public ProxyHandle(Object targetObj) {
this.proxyObj = targetObj;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
System.out.println("DynamicProxy类对" + methodName + "进行了增强处理,begin....");
// 执行原有方法
method.invoke(proxyObj, args);
System.out.println("DynamicProxy类对" + methodName + "进行了增强处理,end....");
return null;
}
}
interface DynamicPerson {
void exection();
}
class DynamicUser implements DynamicPerson {
@Override
public void exection() {
System.out.println("实际执行的代码");
}
}

这样就可以解释Mybatis为什么定义一个接口就可以执行sql

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
package org.jphoebe.edu.demo.proxy.dynamic;

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

/**
* ProxyFactory class
*
*/
public class MybatisProxy implements InvocationHandler {

public static void main(String[] args) {
// mybatis mapper代理的实现
MybatisProxy mybatisProxy = new MybatisProxy(PersonMapper.class);
PersonMapper proxyObject = (PersonMapper) Proxy.newProxyInstance(
PersonMapper.class.getClassLoader(),
new Class[] { PersonMapper.class },
mybatisProxy);
proxyObject.exection();
}

private Object proxyObj;

public MybatisProxy(Object targetObj) {
this.proxyObj = targetObj;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
System.out.println("执行与该接口对应的mapping xml文件中的sql");
// 执行sql的操作
return null;
}
}

interface PersonMapper {
void exection();
}

动态代理生成的类

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
package org.jphoebe.edu.demo.proxy.dynamic;

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

final class $Proxy0 extends Proxy implements PersonMapper {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;

public $Proxy0(InvocationHandler var1) throws {
super(var1);
}

public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}

public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}

public final void exection() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}

public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}

static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("org.jphoebe.edu.demo.proxy.dynamic.PersonMapper").getMethod("exection");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}

可以看出,生成的类继承了被代理对象的接口,所以JDK原生的动态代理是基于接口的,如果对象没有使用接口,那么就无法使用jdk动态代理,需要使用cglib动态代理

2、cglib动态代理

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
package org.jphoebe.edu.demo.proxy.cglib;


import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
* CglibProxy class
*
* @author 蒋时华
* @date 2019/8/29
*/
public class CglibProxy implements MethodInterceptor {

public static void main(String[] args) {
// cglib 代理
// 代理类class文件存入本地磁盘方便我们反编译查看源码
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\\prod\\edu-demo");
// 通过CGLIB动态代理获取代理对象的过程
Enhancer enhancer = new Enhancer();
// 设置enhancer对象的父类
enhancer.setSuperclass(CglibUser.class);
// 设置enhancer的回调对象
enhancer.setCallback(new CglibProxy());
// 创建代理对象
CglibUser proxy= (CglibUser)enhancer.create();
// 通过代理对象调用目标方法
proxy.exection();


// 源码
// MethodInterceptor
// Enhancer.createHelper()
// KeyFactory.generateClass()
// AbstractClassGenerator.create()
// BeanGenerator.nextInstance()
}

/**
* sub:cglib生成的代理对象
* method:被代理对象方法
* objects:方法入参
* methodProxy: 代理方法
*/
/**
* FastClass f1; // net.sf.cglib.test.Target的fastclass
* FastClass f2; // Target$$EnhancerByCGLIB$$788444a0 的fastclass
* int i1; //方法g在f1中的索引
* int i2; //方法CGLIB$g$0在f2中的索引
*
**/
@Override
public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("======插入前置通知======");
Object object = methodProxy.invokeSuper(sub, objects);
System.out.println("======插入后者通知======");
return object;
}
}

class CglibUser{
public void exection() {
System.out.println("实际执行的代码");
}
}

使用cglib代理,会生成

1
2
3
CglibUser$$EnhancerByCGLIB$$45809d07$$FastClassByCGLIB$$db8bd788.class
CglibUser$$EnhancerByCGLIB$$45809d07.class
CglibUser$$FastClassByCGLIB$$575d67e9.class

三个文件
EnhancerByCGLIB:是生成的代理对象
FastClassByCGLIB:是原对象方法的索引地址(避免使用了反射,而是直接调用对象方法)
EnhancerByCGLIB$$45809d07$$FastClassByCGLIB:是生成代理对方法的索引地址
fastclass中的部分代理

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
public int getIndex(Signature var1) {
String var10000 = var1.toString();
switch(var10000.hashCode()) {
case -1651887568:
if (var10000.equals("exection()V")) {
return 0;
}
break;
case 1826985398:
if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
return 1;
}
break;
case 1913648695:
if (var10000.equals("toString()Ljava/lang/String;")) {
return 2;
}
break;
case 1984935277:
if (var10000.equals("hashCode()I")) {
return 3;
}
}

return -1;
}

生成class源码:

1
2
3
4
5
// MethodInterceptor
// Enhancer.createHelper()
// KeyFactory.generateClass()
// AbstractClassGenerator.create()
// BeanGenerator.nextInstance()

生成对象核心代码

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
public void generateClass(ClassVisitor v) {
ClassEmitter ce = new ClassEmitter(v);

Method newInstance = ReflectUtils.findNewInstance(keyInterface);
if (!newInstance.getReturnType().equals(Object.class)) {
throw new IllegalArgumentException("newInstance method must return Object");
}

Type[] parameterTypes = TypeUtils.getTypes(newInstance.getParameterTypes());
ce.begin_class(Constants.V1_2,
Constants.ACC_PUBLIC,
getClassName(),
KEY_FACTORY,
new Type[]{Type.getType(keyInterface)},
Constants.SOURCE_FILE);
EmitUtils.null_constructor(ce);
EmitUtils.factory_method(ce, ReflectUtils.getSignature(newInstance));

int seed = 0;
CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC,
TypeUtils.parseConstructor(parameterTypes),
null);
e.load_this();
e.super_invoke_constructor();
e.load_this();
List<FieldTypeCustomizer> fieldTypeCustomizers = getCustomizers(FieldTypeCustomizer.class);
for (int i = 0; i < parameterTypes.length; i++) {
Type parameterType = parameterTypes[i];
Type fieldType = parameterType;
for (FieldTypeCustomizer customizer : fieldTypeCustomizers) {
fieldType = customizer.getOutType(i, fieldType);
}
seed += fieldType.hashCode();
ce.declare_field(Constants.ACC_PRIVATE | Constants.ACC_FINAL,
getFieldName(i),
fieldType,
null);
e.dup();
e.load_arg(i);
for (FieldTypeCustomizer customizer : fieldTypeCustomizers) {
customizer.customize(e, i, parameterType);
}
e.putfield(getFieldName(i));
}
e.return_value();
e.end_method();

// hash code
e = ce.begin_method(Constants.ACC_PUBLIC, HASH_CODE, null);
int hc = (constant != 0) ? constant : PRIMES[(Math.abs(seed) % PRIMES.length)];
int hm = (multiplier != 0) ? multiplier : PRIMES[(Math.abs(seed * 13) % PRIMES.length)];
e.push(hc);
for (int i = 0; i < parameterTypes.length; i++) {
e.load_this();
e.getfield(getFieldName(i));
EmitUtils.hash_code(e, parameterTypes[i], hm, customizers);
}
e.return_value();
e.end_method();

// equals
e = ce.begin_method(Constants.ACC_PUBLIC, EQUALS, null);
Label fail = e.make_label();
e.load_arg(0);
e.instance_of_this();
e.if_jump(CodeEmitter.EQ, fail);
for (int i = 0; i < parameterTypes.length; i++) {
e.load_this();
e.getfield(getFieldName(i));
e.load_arg(0);
e.checkcast_this();
e.getfield(getFieldName(i));
EmitUtils.not_equals(e, parameterTypes[i], fail, customizers);
}
e.push(1);
e.return_value();
e.mark(fail);
e.push(0);
e.return_value();
e.end_method();

// toString
e = ce.begin_method(Constants.ACC_PUBLIC, TO_STRING, null);
e.new_instance(Constants.TYPE_STRING_BUFFER);
e.dup();
e.invoke_constructor(Constants.TYPE_STRING_BUFFER);
for (int i = 0; i < parameterTypes.length; i++) {
if (i > 0) {
e.push(", ");
e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING);
}
e.load_this();
e.getfield(getFieldName(i));
EmitUtils.append_string(e, parameterTypes[i], EmitUtils.DEFAULT_DELIMITERS, customizers);
}
e.invoke_virtual(Constants.TYPE_STRING_BUFFER, TO_STRING);
e.return_value();
e.end_method();

ce.end_class();
}

再来看看正常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
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
// class version 52.0 (52)
// access flags 0x21
public class org/jphoebe/edu/demo/proxy/staticProxy/UserProxy implements org/jphoebe/edu/demo/proxy/staticProxy/StaticPerson {

// compiled from: UserProxy.java

// access flags 0x1
public Lorg/jphoebe/edu/demo/proxy/staticProxy/StaticUser; user

// access flags 0x9
public static main([Ljava/lang/String;)V
// parameter args
L0
LINENUMBER 10 L0
NEW org/jphoebe/edu/demo/proxy/staticProxy/StaticUser
DUP
INVOKESPECIAL org/jphoebe/edu/demo/proxy/staticProxy/StaticUser.<init> ()V
ASTORE 1
L1
LINENUMBER 11 L1
NEW org/jphoebe/edu/demo/proxy/staticProxy/UserProxy
DUP
ALOAD 1
INVOKESPECIAL org/jphoebe/edu/demo/proxy/staticProxy/UserProxy.<init> (Lorg/jphoebe/edu/demo/proxy/staticProxy/StaticUser;)V
INVOKEVIRTUAL org/jphoebe/edu/demo/proxy/staticProxy/UserProxy.exection ()V
L2
LINENUMBER 13 L2
RETURN
L3
LOCALVARIABLE args [Ljava/lang/String; L0 L3 0
LOCALVARIABLE targetObj Lorg/jphoebe/edu/demo/proxy/staticProxy/StaticUser; L1 L3 1
MAXSTACK = 3
MAXLOCALS = 2

// access flags 0x1
public <init>(Lorg/jphoebe/edu/demo/proxy/staticProxy/StaticUser;)V
// parameter user
L0
LINENUMBER 17 L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
L1
LINENUMBER 18 L1
ALOAD 0
ALOAD 1
PUTFIELD org/jphoebe/edu/demo/proxy/staticProxy/UserProxy.user : Lorg/jphoebe/edu/demo/proxy/staticProxy/StaticUser;
L2
LINENUMBER 19 L2
RETURN
L3
LOCALVARIABLE this Lorg/jphoebe/edu/demo/proxy/staticProxy/UserProxy; L0 L3 0
LOCALVARIABLE user Lorg/jphoebe/edu/demo/proxy/staticProxy/StaticUser; L0 L3 1
MAXSTACK = 2
MAXLOCALS = 2

// access flags 0x1
public exection()V
L0
LINENUMBER 23 L0
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "\u6267\u884c\u771f\u6b63\u7684\u4ee3\u7801\u524d\u3002\u3002\u3002\u3002\u3002\u3002"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L1
LINENUMBER 25 L1
ALOAD 0
GETFIELD org/jphoebe/edu/demo/proxy/staticProxy/UserProxy.user : Lorg/jphoebe/edu/demo/proxy/staticProxy/StaticUser;
INVOKEVIRTUAL org/jphoebe/edu/demo/proxy/staticProxy/StaticUser.exection ()V
L2
LINENUMBER 27 L2
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "\u6267\u884c\u771f\u6b63\u7684\u4ee3\u7801\u540e\u3002\u3002\u3002\u3002\u3002\u3002"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L3
LINENUMBER 28 L3
RETURN
L4
LOCALVARIABLE this Lorg/jphoebe/edu/demo/proxy/staticProxy/UserProxy; L0 L4 0
MAXSTACK = 2
MAXLOCALS = 1
}

会发现有诸多相似的命令,如:

1
2
3
dup (dup())
aload (load_this())
return (return_value())

等等
所以CGlib实际上是直接写字节码来创建代理类,所以也就无所无是否继承了接口,代理类只需要直接代理这个类就行。

总结

jdk动态代理

特点

  • Interface:对于JDK Proxy,业务类是需要一个Interface的,这是一个缺陷;

  • Proxy:Proxy类是动态产生的,这个类在调用Proxy.newProxyInstance()方法之后,产生一个Proxy类的实力。实际上,这个Proxy类也是存在的,不仅仅是类的实例,这个Proxy类可以保存在硬盘上;

  • Method:对于业务委托类的每个方法,现在Proxy类里面都不用静态显示出来。

  • InvocationHandler:这个类在业务委托类执行时,会先调用invoke方法。invoke方法在执行想要的代理操作,可以实现对业务方法的再包装。

  • JDK动态代理类实现了InvocationHandler接口,重写的invoke方法。
    JDK动态代理的基础是反射机制(method.invoke(对象,参数))Proxy.newProxyInstance()

cglib动态代理

特点

  • CGLib采用底层的字节码技术,全称是:Code Generation Library,CGLib可以为一个类创建一个子类,在子类中采用方法拦截的技术拦截所有父类方法的调用并顺势织入横切逻辑。
  • 对指定的目标生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
  • 注意:jdk的动态代理只可以为接口去完成操作,而cglib它可以为没有实现接口的类去做代理,也可以为实现接口的类去做代理。

性能对比

关于两者之间的性能的话,JDK动态代理所创建的代理对象,在以前的JDK版本中,性能并不是很高,虽然在高版本中JDK动态代理对象的性能得到了很大的提升,但是他也并不是适用于所有的场景。主要体现在如下的两个指标中:

  • CGLib所创建的动态代理对象在实际运行时候的性能要比JDK动态代理高不少,有研究表明,大概要高10倍;
  • 但是CGLib在创建对象的时候所花费的时间却比JDK动态代理要多很多,有研究表明,大概有8倍的差距;
  • 对于singleton的代理对象或者具有实例池的代理,因为无需频繁的创建代理对象,所以比较适合采用CGLib动态代理,反正,则比较适用JDK动态代理。
查看 =>>> 具体测试结果