代理

代理

静态代理

主要目的是扩展原有类的功能

例子:
1
2
3
4
// 声明接口
public interface Handler {
void handle(String data);
}
1
2
3
4
5
6
7
// 具体实现类
public class HandlerImpl implements Handler {
@Override
public void handle(String data) {
System.out.println("handle: " + data);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 代理类
public class HandlerProxy implements Handler{
private Handler mHandler;
// 传入需要代理的实例对象
public HandlerProxy(Handler handler) {
mHandler = handler;
}
//扩展其用法
@Override
public void handle(String data) {
System.out.println("start handle");
mHandler.handle(data);
System.out.println("end handle");
}
}

// 使用
new HandlerProxy(new HandlerImpl()).handle("待处理数据");

就这样,知道缺点是被代理的类扩展了代理类也需一并扩展。

动态代理

例子:
1
2
3
4
// 声明接口
public interface Handler {
void handle(String data);
}
1
2
3
4
5
6
7
// 具体实现类
public class HandlerImpl implements Handler {
@Override
public void handle(String data) {
System.out.println("handle: " + data);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 声明实现InvocationHandler的处理器
public class MyHandler implements InvocationHandler {
// 传入需要代理的实例对象
private Handler mHandler;
public MyHandler(Handler handler) {
mHandler = handler;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("start handle");
// 利用反射机制将请求分派给委托类处理
Object invoke = method.invoke(mHandler, args);
System.out.println("end handle");
return invoke;
}
}
1
2
3
4
5
6
7
8
9
// 使用
Handler o = (Handler) Proxy.newProxyInstance(
// 类加载器
HandlerImpl.class.getClassLoader(),
// 需要代理的接口,也可:HandlerImpl.class.getInterfaces()
new Class[]{Handler.class},
// 处理器
new MyHandler(new HandlerImpl()));
o.handle("待处理数据");

可以看到,动态代理跟静态代理一样,在代理类内部保存了一个委托类的实例,实际上都是调用原来的委托实例来进行需要的操作。

区别:
  1. 动态代理跟静态代理最大的不同便是生成代理类的时期不同,静态代理是在编译期,而动态代理则是在运行时根据委托类信息动态生成
  2. 动态代理实现的是InvocationHandler接口,而静态代理则是直接实现公共接口
  3. 动态代理可以获得更多的运行时信息,使用起来也会更加灵活
缺点:

由于最终调用实际逻辑采用方法反射调用的方式,效率并不是很高

原理:

最终通过ProxyGenerator.generateProxyClass()函数动态生成代理字节码二进制数据,然后通过native方法defineClass0将字节码加载进方法区,并获取参数为InvocationHandler的构造器,然后通过该构造器生成实例并传入我们自定义的处理器

注:android中直接通过generateProxy生成了代理类,并没有走ProxyGenerator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
participant Proxy
participant Proxy#ProxyClassFactory
participant WeakCache
participant WeakCache#Factory

Note left of WeakCache:Proxy类里有静态实例WeakCache成员变量\n生成该实例时传入ProxyClassFactory实例
Proxy -> WeakCache:Proxy.getProxyClass0()
Note right of WeakCache:优先查找缓存
WeakCache -> WeakCache:proxyClassCache.get()
WeakCache -> Proxy:有缓存
Note right of WeakCache:构建Factory工厂类
WeakCache -> WeakCache:new Factory()
WeakCache -> WeakCache#Factory:supplier.get()
WeakCache#Factory -> Proxy#ProxyClassFactory:valueFactory.apply()
Note right of Proxy#ProxyClassFactory:1.类加载器是否解析出相同的class对象
Note right of Proxy#ProxyClassFactory:2.class是否是一个接口
Note right of Proxy#ProxyClassFactory:3.数组类是否重复等判断
Note right of Proxy#ProxyClassFactory:调用defineClass0(native方法)动态生成字节码
Proxy#ProxyClassFactory -> Proxy:ProxyGenerator.generateProxyClass() \n defineClass0()
Note right of Proxy:取参数为InvocationHandler构造器
Note right of Proxy:通过构造器生成实例时传入处理器

Cglib动态代理

CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充

通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择

git地址:https://github.com/cglib/cglib

cglib-nodep-xxx.jar:使用nodep包不需要关联asm的jar包,jar包内部包含asm的类.
cglib-xxx.jar:使用此jar包需要关联asm的jar包,否则运行时报错.

例子:
1
2
3
4
5
6
// 待代理类
public class Handler {
public void method_1(String data) {System.out.println(data);}
public void method_2(String data) {System.out.println(data);}
public void method_3(String data) {System.out.println(data);}
}
代理:
1
2
3
4
5
6
7
8
9
10
11
Handler handler = (Handler) Enhancer.create(Handler.class, new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("start handle");
// methodProxy 代理类方法代理引用,invokeSuper调用实际逻辑
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("end handle");
return result;
}
});
handler.method_1("待处理数据");
过滤器:

作用:可以针对不同的方法调用不同的逻辑,

注意:虽然可以在MethodInterceptor的intercept方法进行区分,但使用过滤器可以有效的减少哈希查找,提高效率

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
// 什么操作都不做,直接调用实际方法
NoOp instance = NoOp.INSTANCE;
// 锁定方法返回值,不会触发实际方法
FixedValue fixedValue = new FixedValue() {
@Override
public Object loadObject() throws Exception {
System.out.println("锁定调用结果");
Object result = 1;
return result;
}
};
// 拦截操作
MethodInterceptor methodInterceptor = new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("start handle");
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("end handle");
return result;
}
};
Handler handler = (Handler) Enhancer.create(Handler.class, null, new CallbackFilter() {
@Override
public int accept(Method method) {
if (method.getName().equals("method_1")){
return 2;
} else if (method.getName().equals("method_2")) {
return 1;
}
return 0;
}
}, new Callback[]{instance, fixedValue, methodInterceptor});
handler.method_1("method_1");
handler.method_2("method_2");
handler.method_3("method_3");
延迟加载:
1
2
3
4
5
6
// 延迟加载类
public class LazyBeam {
private String name;
public String getName() {return name;}
public void setName(String name) {this.name = name;}
}
1
2
3
4
5
6
7
8
9
10
11
// LazyLoader 只有在代理类调用任意方法时才会初始化数据,只会触发一次loadObject
LazyBeam lazyBeam = (LazyBeam) Enhancer.create(LazyBeam.class, null, new LazyLoader() {
@Override
public Object loadObject() throws Exception {
System.out.println("加载数据");
LazyBeam lazyBeam = new LazyBeam();
lazyBeam.setName("lazyBeam");
return lazyBeam;
}
});
System.out.println(lazyBeam.getName());
1
2
3
4
5
6
7
8
9
10
11
// Dispatcher 只有在代理类调用任意方法时才会初始化数据,且每次都会触发loadObject
LazyBeam lazyBeam = (LazyBeam) Enhancer.create(LazyBeam.class, null, new Dispatcher() {
@Override
public Object loadObject() throws Exception {
System.out.println("加载数据");
LazyBeam lazyBeam = new LazyBeam();
lazyBeam.setName("lazyBeam");
return lazyBeam;
}
});
System.out.println(lazyBeam.getName());
接口生成:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
InterfaceMaker maker = new InterfaceMaker();
maker.add(Handler.class);
Class<?> aClass = maker.create();
for (Method method : aClass.getMethods()) {
System.out.println(method.getName());
}
Object obj = Enhancer.create(Object.class, new Class[]{aClass}, new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
if (method.getName().equals("method_1")) {
System.out.println("执行方法1");
return "方法1";
}
if (method.getName().equals("method_2")) {
System.out.println("执行方法2");
return "方法2";
}
return null;
}
});
Method method_1 = aClass.getMethod("method_1", String.class);
method_1.invoke(obj,"方法1");
Method method_2 = aClass.getMethod("method_2", String.class);
method_2.invoke(obj,"方法2");
原理:
表层原理:

动态生成代理类的子类,子类重写要代理的类的所有不是final/private的方法。在子类采用方法拦截的技术拦截所有父类方法的调用。

底层原理:

使用字节码处理框架ASM,来转换字节码并生成新的类。注:不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

优点:

相较与JDK动态代理

  1. 可以实现接口和类的代理,局限性更小【注:代理接口调用invokeSuper相当于直接调用未实现的接口方法,会直接报错
  2. 由于与实际编写继承代码无异,采用的是动态用生成子类方式,方法执行效率要高
  3. 有丰富的操作策略以适应不同的业务
缺点:

由于采用继承的关系,对私有方法和final方法无法代理

包说明:

net.sf.cglib.core:底层字节码处理类,他们大部分与ASM有关系。
net.sf.cglib.transform:编译期或运行期类和类文件的转换
net.sf.cglib.proxy:实现创建代理和方法拦截器的类
net.sf.cglib.reflect:实现快速反射和C#风格代理的类
net.sf.cglib.util:集合排序等工具类
net.sf.cglib.beans:JavaBean相关的工具类