代理
代理模式(Proxy):为其他对象提供一种代理来控制对这个对象的访问。
代理又分为静态代理和动态代理。
静态代理指的是代理类由我们程序员自己编写代理类。常见实现方式有组合实现,继承等。
动态代理指的是程序运行时代理类自动生成,我们只需要提供核心代码即可。常见动态代理有JDK原生动态代理,CGLIB动态代理。
静态代理
有一个接口:
public interface Action {
public abstract void action();
}
它的一个实现类:
public class Target implements Action{
@Override
public void action() {
System.out.println("This is targetMethod.");
}
}
现在我有一个需求,要对 Target
的 action()
方法进行一个增强。
那么这时候就有人要说了,这简单,直接修改action()
方法内部逻辑,做一个增强不就好了吗…
这其实也可以,但这违背了 OCP 原则(对扩展开放,对修改关闭),这条原则建议我们最好不要修改原有的代码,而是用其他的方法扩展原有的代码。
这就引入这期的主角 - 代理
设想一下我可不可以在一个对象中有 Target
的引用,在另一个对象中调用Target
的方法。这就是静态代理。
public class MyProxy implements Action{
private Action target;
public MyProxy(Action target){
this.target = target;
}
@Override
public void action() {
// 做一些增强
System.out.println("目标方法 前 的增强");
target.action();
System.out.println("目标方法 后 的增强");
}
}
使用方式如下:
public static void main(String[] args) {
Action proxy = new MyProxy(new Target());
proxy.action();
}
// out:
// 目标方法 前 的增强
// This is targetMethod.
// 目标方法 后 的增强
这是通过组合实现,还可以通过继承实现, CGLIB 就是通过继承实现动态代理的。
继承实现:
public class MyProxy extends Target implements Action{
@Override
public void action() {
// 做一些增强
System.out.println("目标方法 前 的增强");
super.action();
System.out.println("目标方法 后 的增强");
}
}
使用方式:
public static void main(String[] args) {
Action proxy = new MyProxy();
proxy.action();
}
// out:
// 目标方法 前 的增强
// This is targetMethod.
// 目标方法 后 的增强
试想一下我们每次要代理一个类,就要多出来一个代理类,容易造成类爆炸(类的数量急剧增多),于是人们就想有没有一种能够帮助我自动生成代理类的技术呢?
有的兄弟,有的,这样的技术有两种:
-
- JDK原生动态代理
- CGLIB动态代理
动态代理
JDK原生动态代理
jdk代理是java自带的代理方法,主要采用了多态和反射的技术,让代理对象和目标类一起去实现一个共同的接口,这样就可以保证两者有相同的方法。
jdk代理通过 Proxy.newProxyInstance() 获取到代理对象
public static void main(String[] args) {
Action target = new Target();
Action proxyInstance = (Action)Proxy.newProxyInstance(Action.class.getClassLoader(),
new Class[]{Action.class},
new MyProxy(target));
proxyInstance.action();
}
Proxy.newProxyInstance(类加载器,
接口集合,代理对象要实现的接口,
一个函数式接口 -> (代理对象,原方法,参数));
public class MyProxy implements InvocationHandler {
// 原对象
Action target;
public MyProxy(Action target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// invoke(代理对象自己,原方法,方法参数)
// 做一些增强
System.out.println("目标方法 前 的增强");
Object result = method.invoke(target,args);
System.out.println("目标方法 后 的增强");
return result;
}
}
代理类反编译后:
public final class $Proxy0 extends Proxy implements Action {
// hashCode()
private static final Method m0;
// equals()
private static final Method m1;
// toString()
private static final Method m2;
// 真正代理的方法 action()
private static final Method m3;
public $Proxy0(InvocationHandler var1) {
super(var1);
}
public final int hashCode() {
return (Integer)super.h.invoke(this, m0, (Object[])null);
}
public final boolean equals(Object var1) {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
}
public final String toString() {
return (String)super.h.invoke(this, m2, (Object[])null);
}
public final void action() {
super.h.invoke(this, m3, (Object[])null);
}
static {
ClassLoader var0 = $Proxy0.class.getClassLoader();
m0 = Class.forName("java.lang.Object", false, var0).getMethod("hashCode");
m1 = Class.forName("java.lang.Object", false, var0).getMethod("equals", Class.forName("java.lang.Object", false, var0));
m2 = Class.forName("java.lang.Object", false, var0).getMethod("toString");
m3 = Class.forName("proxy.Action", false, var0).getMethod("action");
}
private static MethodHandles.Lookup proxyClassLookup(MethodHandles.Lookup var0) throws IllegalAccessException {
if (var0.lookupClass() == Proxy.class && var0.hasFullPrivilegeAccess()) {
return MethodHandles.lookup();
} else {
throw new IllegalAccessException(var0.toString());
}
}
}
可以看到,自动生成的代理类,不仅增强了 action()
, 还增强了 hashCode() , equels() , toString()
三个方法。
CGLIB 动态代理
cglib代理不是jdk自带的代理,需要导外部依赖。
底层是让代理类继承目标类,通过Enhancer.create
获取代理对象。
Enhancer.create(目标类,
一个函数式接口 -> (代理对象自己,需要被增强的方法,方法参数,MethodProxy 代理不走反射的关键));
interface MethodInterceptor extends Callback
public static void main(String[] params) {
Action target = new Target();
Action proxyObj = (Action)Enhancer.create(Action.class, (MethodInterceptor) (proxy, meth, arg, methodProxy) -> {
System.out.println("enhance before");
// 反射调用目标方法
// Object result = meth.invoke(target, arg);
// methodProxy 可以避免反射调用
// 内部没有用反射,需要原对象
Object result = methodProxy.invoke(target, arg);
// 内部没有用反射,需要代理对象
//Object result = methodProxy.invokeSuper(proxy, arg);
System.out.println("enhance after");
return result;
});
proxyObj.action();
}
// out:
// enhance before
// This is targetMethod.
// enhance after
生成如下三个文件:
区别:
- 使用JDK动态代理,被代理类必须要实现接口;使用CGLIB动态代理,被代理类可以不实现接口
- JDK动态代理生成的代理类继承了java.lang.reflect.Proxy,因为Java是单继承的,如果不通过实现接口的形式,无法对类进行扩展。
- CGLIB动态代理生成的代理类实际上是被代理类的子类,所以被代理类可以不实现接口。
- 自动生成类的数量不同
-
JDK动态代理只会生成1个代理类,一般情况下名称为:com.sun.proxy.$Proxy0。
-
CGLIB动态代理会生成好几个类,核心的3个分别是:
-
1)代理类:被代理类的子类,名称格式为
Action$$EnhancerByCGLIB$$62adcfbf
,包名和被代理类包名一致。 -
2)代理类的索引类:名称格式为
Action$$EnhancerByCGLIB$$62adcfbf$$FastClassByCGLIB$$2fbc00a6
,包名和被代理类包名一致。 -
3)被代理类的索引类:名称格式为
Action$$FastClassByCGLIB$$7219d5a6
,包名和被代理类包名一致。
-
-
- 调用方式不同
- JDK动态代理:代理类—>InvocationHandler.invoke()—>被代理类方法(用到了反射)。
- CGLIB动态代理:代理类—>MethodInterceptor.intercept()—>代理类索引类 getIndex()—> 代理类索引类invoke()—>代理类—>被代理类。(直接调用)
总结
- JDK原生动态代理是Java原生支持的,不需要任何外部依赖,但是它只能基于接口进行代理(需要代理的对象必须实现于某个接口)
- CGLIB通过继承的方式进行代理(让需要代理的类成为Enhancer的父类),无论目标对象有没有实现接口都可以代理,但是无法处理final的情况。