动态代理与静态代理

欢迎查看Eetal的第十七篇博客–动态代理与静态代理

静态代理—硬编码

没啥好说的,就是像装饰模式一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class StaticProxy extends Object{

Object object;

public StaticProxy(Object object) {
this.object = object;
}

@Override
public int hashCode() {
System.out.println("proxy decorate start...");
int hashCode = object.hashCode();
System.out.println("proxy decorate end...");
return hashCode;
}

public static void main(String[] args) {
Object proxy = new StaticProxy(new Object());
System.out.println(proxy.hashCode());
}
}

动态代理

JDK动态代理—反射、Proxy、InvocationHandler

jdk动态代理要求对象必须实现接口
并且仅会代理接口方法以及equals,hashCode和toString三个Object类的方法
生成的代理类也仅是实现这些接口和继承Proxy所以接口里没有的方法在代理对象里不存在

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
public class DynamicProxy {
static class BAInvocationHandler implements InvocationHandler{

private Object realObj;

public BAInvocationHandler(Object realObj) {
this.realObj = realObj;
}

public void doBefore() {
System.out.println("before ...");
}

public void doAfter() {
System.out.println("after ...");
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//proxy为代理对象,所以此处不能调用proxy的方法否则会栈溢出死锁
//proxy是在newProxyInstance时生成并在调用时传进来的,因为invocationHandler与Proxy是聚合模式
//所以在invocation中获取不到代理对象,只能通过这种方式传递进来
doBefore();
Object result = method.invoke(realObj, args);
System.out.println("invoke result :"+result);
doAfter();
return result;
}

}
static <T>T getInstance(Object realObj,Class<T>... clazz) {
//第一个T泛型方法
//clazz 该代理类实现的接口
return (T)Proxy.newProxyInstance(clazz[0].getClassLoader(), clazz,new BAInvocationHandler(realObj));
}
public static void main(String[] args) {
Object o = getInstance(new Object(),Serializable.class);
o.hashCode();
System.out.println(o instanceof Proxy);

}
}

由于jdk动态代理基于接口的特性,使得其只能代理接口对象的方法,也就是必须实现接口
至于为什么,我个人的理解是在运行时
1.其要继承Proxy,因为Proxy封装了动态代理的底层实现,所以Proxy不能是接口,因此只能通过继承来组合(通过聚合或者组合还是离不开要修改字节码)
2.jdk反射只能得到每一个class,interface等的field的名称,返回值类型,形参类型,方法体是得不到的,
否则就得通过读取修改字节码来生成新的class的字节码文件(cglib就是这么做的),而读取解析修改字节码效率肯定低一点
因此jdk代理只代理接口对象,实际需要一个原对象,再生成一个代理对象,使用的是代理对象
注意是对象因为原对象一些在接口中不存在的属性在代理对象中是不存在的,这也是需要getter、setter存在的一个原因

CGLIB动态代理—修改字节码,生成新的子类Class

当面对特殊的场景,比如就是没有实现任何接口,或者希望获取到所有属性,得到一个子类型的类型,就需要通过cglib了
cglib的做法是,通过读取修改字节码,创建一个继承了原有class的子类,所以cglib是代理类,代理方法,而不是代理对象
cglib会代理方法中所有不是final类型的非静态方法方法(因为静态方法不会使用代理对象和代理类)
使用时是直接使用代理类去创建对象,只有一个对象,因此效率较jdk代理低一点
在spring中就是根据要代理的类是否有实现接口,而决定使用cglib还是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
47
48
49
50
51
52
53
54
public class CGLIBDynamicProxy {
static class Test{
public final void finalMethod() {
System.out.println("final");
}
public void method() {
System.out.println("method");
}
public static void staticMethod(){
System.out.println("staticMethod...");
}
}
static class BAInterceptor implements MethodInterceptor{

public void doBefore() {
System.out.println("\nbefore ...");
}

public void doAfter() {
System.out.println("after ...\n");
}

@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//proxy---代理类对象
//method---代理后的方法
//args---参数
//methodProxy---方法的代理对象
//不能直接使用 method.invoke,因为会再调用invoke导致死锁stackOverFlow
doBefore();
//使用父类方法,也就是原类方法获取返回值
Object result = methodProxy.invokeSuper(proxy, args);
System.out.println("invoke super result..."+result);
doAfter();
return result;
}


}
static <T> T getInstance(Class<T> clazz) {
//第一个T泛型方法
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(new BAInterceptor());
return (T) enhancer.create();
}
public static void main(String[] args) {
Test test = getInstance(Test.class);
test.hashCode();
test.finalMethod();
test.method();
test.staticMethod();
}
}

请移步

个人主页: yangyitao.top