代理设计模式
代理设计模式再生活中应该很常见了,现在各种中间商的货物代售方便了我们的生活也增加了我们生活的成本。这种生活中的中间商行为就是一种代理模式。
拿一个品牌来说明:
在编程领域中一般存在两种代理模式
- 静态代理。(仅仅可以代理一个类的行为,不能随类的变化而变化)
- 动态代理。(可以代理所有类的行为)
接下来我们先来看静态代理
1. 静态代理
仅仅用来代理一个类的行为。
代码演示一下:
class NaiKe {
void run() {
System.out.println("耐克");
}
}
//代理类
class ShoesProxy extends NaiKe{
@Override
void run() {
System.out.println("agency shoes before");
super.run();
System.out.println("agency shoes after");
}
}
class NaiKe{
void run() {
System.out.println("耐克");
}
}
class ShoesProxy {
NaiKe naiKe = new NaiKe();
void run() {
System.out.println("agency shoes before");
naiKe.run();
System.out.println("agency shoes after");
}
}
public class ProxyDesgin {
public static void main(String[] args) {
Shoes shoes = new ShoesProxy(new ShoesTimer(new NaiKe()));
shoes.run();
}
}
abstract class Shoes{
abstract void run();
}
class NaiKe extends Shoes{
@Override
void run() {
System.out.println("耐克");
}
}
class Adi extends Shoes{
@Override
void run() {
System.out.println("阿迪达斯");
}
}
//代理类
class ShoesProxy extends Shoes {
Shoes shoes ;
public ShoesProxy(Shoes shoes){
this.shoes = shoes ;
}
void run() {
System.out.println("agency shoes before");
shoes.run();
System.out.println("agency shoes after");
}
}
class ShoesTimer extends Shoes {
Shoes shoes ;
public ShoesTimer(Shoes shoes){
this.shoes = shoes ;
}
void run() {
System.out.println("log timer shoes before");
shoes.run();
System.out.println("log timer shoes after");
}
}
画个图瞅瞅静态代理
这个就是静态代理,兄弟们应该已经发现了它的缺点,只能指定自己想要进行代理的类,而不能对所有的类进行代理,扩展性太差,所以引出了动态代理
2.动态代理
谈到动态代理,脑子里第一个出现的肯定就是Java 动态代理了。我们先来聊一下Java 动态代理。
2.1 Java动态代理
先来看一个动态代理的案例
NaiKe naiKe = new NaiKe();
Shoes shoes = (Shoes) Proxy.newProxyInstance(NaiKe.class.getClassLoader(),new Class[]{Shoes.class},new InvocationHandler() {
@Override
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable {
System.out.println("begin timer : " + System.currentTimeMillis());
method.invoke(naiKe,args);
System.out.println("after timer : " + System.currentTimeMillis());
return null;
}
});
shoes.run();
- 第一个参数。通过动态代理创建的对象被哪个加载器加载,一般使用本类的类加载器即可
- 第二个参数。被代理对象要实现的方法
- 第三个参数。被代理对象被调用的时候该如何处理逻辑
我们看一下动态代理的源码。
我们可以通过以下方式让JVM 将动态生成的代理类保存到我们的项目中
-
JDK1.8 使用System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
-
JDK1.8 以上可以使用1 System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles","true");
生成的代理类如下:
final class $Proxy0 extends Proxy implements Shoes {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
}
public final void run() throws {
try {
super.h.invoke(this,m3,(Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
}
public final int hashCode() throws {
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals",Class.forName("java.lang.Object"));
m3 = Class.forName("desgin.proxy.Shoes").getMethod("run");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
从这个类的结构中,我们可以看出很多的东西
- 为什么说
JAVA 动态代理仅仅只能代理接口。(类单继承,代理对象默认继承Proxy类)
- 动态代理的第二个参数,接口内部的方法会被代理对象重写,然后调用第三个参数的
invoke 方法。
上面两个也是动态代理的原理了。我们来仔细看一下我们的run() 方法,也就是我们代理对象要实现的接口
public final void run() throws {
try {
super.h.invoke(this,(Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
- 调用了父类的
h ,父类的h 是InvocationHandler ,然后调用了invoke 方法执行了我们的执行逻辑。
这个就是动态代理的全部实现过程
还有一个非常牛逼的点,它怎么生成的这个代理类。来看一下代理的全过程
图中的ASM 就是为我们动态生成一个代理类的工具,它直接操作了Class 字节码的二进制,然后创建了一个代理类,返回给我们。
Java 动态代理就聊到这里了。下面看一看CGLIb 和AOP
2.2 CGLIB动态代理
弥补了Java 动态代理的不足,CGLIB 动态代理可以代理类。它直接创建了一个被代理对象的子类,实现了对其的代理过程。我们来看一下它的代理过程
//打印生成的代理对象,放置于当前项目下
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,".");
//创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数
Enhancer enhancer = new Enhancer();
//设置目标类的字节码文件
enhancer.setSuperclass(Tank.class);
//设置回调函数
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o,Object[] objects,MethodProxy methodProxy) throws Throwable {
methodProxy.invokeSuper(o,objects);
return null;
}
});
//这里的creat方法就是正式创建代理类
Tank proxyDog = (Tank)enhancer.create();
//调用代理类的eat方法
proxyDog.tank();
还是和Java 动态代理相似,传入一个需要代理的Class ,设置代理的回调函数。然后调用create 创建一个代理对象,调用代理对象的方法。
代理第一行可以输出代理对象,会生成三个代理对象。
查看中间那个,可以看到我们被代理对象的方法
public class Tank$$EnhancerByCGLIB$$a4ec679a extends Tank implements Factory {
//构造方法
public Tank$$EnhancerByCGLIB$$a4ec679a() {
CGLIB$BIND_CALLBACKS(this);
}
//被代理方法
final void tank() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
//调用增强的方法
var10000.intercept(this,CGLIB$tank$0$Method,CGLIB$emptyArgs,CGLIB$tank$0$Proxy);
} else {
super.tank();
}
}
}
在之前的CGLIB 动态代理实现中,我们看到了拦截的回调中传入了四个参数,从上面的源码中可以看到对应参数的作用。
-
Object o 代表生成的代理对象
-
Method method 代表当前代理对象调用的方法
-
Object[] objects 代表方法的参数
-
MethodProxy methodProxy 我们调用方法的方法代理,它没有使用Java 本身的反射,而是动态生成一个新的类,(继承FastClass ),向类中写入委托类实例直接调用方法的语句。
我们可以看一下superinvoke 的源码
public Object invokeSuper(Object obj,Object[] args) throws Throwable {
try {
this.init();
MethodProxy.FastClassInfo fci = this.fastClassInfo;
return fci.f2.invoke(fci.i2,obj,args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
}
}
private static class FastClassInfo {
FastClass f1;
FastClass f2;
int i1;
int i2;
private FastClassInfo() {
}
}
一个图理解CgLib 动态代理过程
写了这么多,感觉对于代理设计模式讲解的篇幅不是很大,而是着重讲解了动态代理的实现方式。总的而言,代理设计模式与我们日常生活非常的接近,生活中的事物几乎都在被代理,所以这个设计模式应该很好懂,所以着重讲解了动态代理的实现方式。 (编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|