代理模式的理解

先看定义:为其他对象提供一种代理以控制对这个对象的访问)。使用代理模式创建代理对象,让代理对象控制目标对象的访问(目标对象可以是远程的对象、创建开销大的对象或需要安全控制的对象),并且可以在不改变目标对象的情况下添加一些额外的功能
我的理解:其实可以这样理解,举个例子我是A可以帮人打排位上分,C是需要上分的人,这个时候C直接叫我上分是没有问题的,但是我比较矫情,我不喜欢直接跟人打交道,不喜欢谈价格,不喜欢收钱,这些事情很繁琐,我只是负责打排位上分,这个时候我有一个经纪人B,B跟,C谈好价格,然后通知我上分,上完分再跟C结账,这个就是代理模式了,B是我A的代理,看代码:
public class MyA {
public static final String TAG=“MyA”;
public void playCame(){
Log.d(TAG,“打排位上分”);
}
}
public class ProxyB {
public static final String TAG=“MyA”;
private MyA myA;
public void sayPrice(){
Log.d(TAG,“讲价格”);
}
public void playCame(){
myA=new MyA();
sayPrice();
myA.playCame();
checkOut();

}
public void checkOut(){
    Log.d(TAG,"结账");
}

}
C调用:
setContentView(R.layout.activity_main);
ProxyB proxyB=new ProxyB();
proxyB.playCame();
打印如下:
2020-11-19 16:56:15.894 7776-7776/? D/MyA: 讲价格
2020-11-19 16:56:15.894 7776-7776/? D/MyA: 打排位上分
2020-11-19 16:56:15.894 7776-7776/? D/MyA: 结账
这样一来代理是不是和简单,非常好理解,这就是典型的静态代理,事先就创建了好代理类,显而易见这样做就是每一个像我这样的人都需要一个代理,而且我的技能增加了,就需要改代理类,第一个问题,就是代理类比较多,维护起来比较麻烦,第二个问题,当被代理人的技能提升了,比如我不单单打排位,我还玩qq飞车,这个时候代理类也要跟着变化,这样也增加了维护难度,这个时候动态代理就有存在的必要性了,在代码运行期间去创建代理类,来代理需要代理的人的功能,说白了动态代理就是想办法,根据接口或目标对象,计算出代理类的字节码,然后再加载到JVM中使用。但是如何计算?如何生成?情况也许比想象的复杂得多,我们需要借助现有的方案:
通过实现接口的方式 -> JDK动态代理
通过继承类的方式 -> CGLIB动态代理
第一种方案实现动态代理:代码如下:
第一步必须要有一个接口
public interface MyAInterface {
void playCame();
}
第二步实现类:
public class MyA implements MyAInterface{
public static final String TAG=“MyA”;
public void playCame(){
Log.d(TAG,“打排位上分”);
}
}
第三步再来一个代理类:
public class DynamicProxyB implements InvocationHandler {
public static final String TAG=“DynamicProxyB”;
Object target;
public DynamicProxyB(Object o){
target=o;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
sayPrice();
Object result = method.invoke(target, args); // 调用 target 的 method 方法
checkOut();
return result;
}
public void sayPrice(){
Log.d(TAG,“将价格”);
}
public void checkOut(){
Log.d(TAG,“结账”);
}
}
调用:
MyA myA=new MyA();
ClassLoader classLoader=myA.getClass().getClassLoader();
Class[] classes=myA.getClass().getInterfaces();
InvocationHandler db= new DynamicProxyB(myA);
MyAInterface proxy= (MyAInterface) Proxy.newProxyInstance(classLoader, classes, db);
proxy.playCame();
打印如下:
2020-11-19 17:49:54.923 9878-9878/? D/MyA: 将价格
2020-11-19 17:49:54.924 9878-9878/? D/MyA: 打排位上分
2020-11-19 17:49:54.924 9878-9878/? D/MyA: 结账
2020-11-19 17:49:54.924 9878-9878/? D/DynamicProxyB: 将价格
2020-11-19 17:49:54.924 9878-9878/? D/MyA: 打排位上分
2020-11-19 17:49:54.924 9878-9878/? D/DynamicProxyB: 结账
两个代理都成功了:
下面我们来重现静态代理上面出现的问题
假如我们A增加了一个新技能不仅会打排位还会打球,修改代码如下:
public interface MyAInterface {
public void playCame();
void playBall();
}
public class MyA implements MyAInterface{
public static final String TAG=“MyA”;
public void playCame(){
Log.d(TAG,“打排位上分”);
}

@Override
public void playBall() {
    Log.d(TAG,"我还会打球");
}

}
调用代码:
ProxyB proxyB=new ProxyB();
proxyB.playCame();
MyA myA=new MyA();
ClassLoader classLoader=myA.getClass().getClassLoader();
Class[] classes=myA.getClass().getInterfaces();
InvocationHandler db= new DynamicProxyB(myA);
MyAInterface proxy= (MyAInterface) Proxy.newProxyInstance(classLoader, classes, db);
proxy.playCame();
proxy.playBall();
这样我们再运行代码打印如下:
2020-11-19 18:00:29.206 10844-10844/com.prafly.myapplication D/MyA: 将价格
2020-11-19 18:00:29.206 10844-10844/com.prafly.myapplication D/MyA: 打排位上分
2020-11-19 18:00:29.206 10844-10844/com.prafly.myapplication D/MyA: 结账
2020-11-19 18:00:29.207 10844-10844/com.prafly.myapplication D/DynamicProxyB: 将价格
2020-11-19 18:00:29.207 10844-10844/com.prafly.myapplication D/MyA: 打排位上分
2020-11-19 18:00:29.207 10844-10844/com.prafly.myapplication D/DynamicProxyB: 结账
2020-11-19 18:00:29.207 10844-10844/com.prafly.myapplication D/DynamicProxyB: 将价格
2020-11-19 18:00:29.207 10844-10844/com.prafly.myapplication D/MyA: 我还会打球
2020-11-19 18:00:29.207 10844-10844/com.prafly.myapplication D/DynamicProxyB: 结账
这样就发现了,我们没改代理类代码却使用代理执行了我们MyA新增的方法,静态代理却没执行,所以说,这就是动态代理相比于静态代理的优势,第二个问题继续,加入MyH也有两个功能需要用代理,这个时候我们不需要再去创建代理类,也不要改代理类,就可直接代理MyH执行它的功能,上代码:
public interface MyHInterface {
void have();
void eat();
}
public class MyH implements MyHInterface{
public static final String TAG=“MyH”;
@Override
public void have() {
Log.d(TAG,“我能吃饭”);
}

@Override
public void eat() {
   Log.d(TAG,"我还能喝");
}

}
调用
MyH myH=new MyH();
ClassLoader classLoader1=myH.getClass().getClassLoader();
Class[] classes1=myH.getClass().getInterfaces();
InvocationHandler db1=new DynamicProxyB(myH);
MyHInterface proxyH= (MyHInterface) Proxy.newProxyInstance(classLoader1,classes1,db1);
proxyH.eat();
proxyH.have();
打印如下:
2020-11-19 18:18:26.930 11257-11257/? D/MyA: 将价格
2020-11-19 18:18:26.930 11257-11257/? D/MyA: 打排位上分
2020-11-19 18:18:26.930 11257-11257/? D/MyA: 结账
2020-11-19 18:18:26.931 11257-11257/? D/DynamicProxyB: 将价格
2020-11-19 18:18:26.931 11257-11257/? D/MyA: 打排位上分
2020-11-19 18:18:26.931 11257-11257/? D/DynamicProxyB: 结账
2020-11-19 18:18:26.931 11257-11257/? D/DynamicProxyB: 将价格
2020-11-19 18:18:26.931 11257-11257/? D/MyA: 我还会打球
2020-11-19 18:18:26.931 11257-11257/? D/DynamicProxyB: 结账
2020-11-19 18:18:26.932 11257-11257/? D/DynamicProxyB: 将价格
2020-11-19 18:18:26.932 11257-11257/? D/MyH: 我还能喝
2020-11-19 18:18:26.932 11257-11257/? D/DynamicProxyB: 结账
2020-11-19 18:18:26.932 11257-11257/? D/DynamicProxyB: 将价格
2020-11-19 18:18:26.932 11257-11257/? D/MyH: 我能吃饭
2020-11-19 18:18:26.932 11257-11257/? D/DynamicProxyB: 结账
这就是动态代理的两大优势,上面例子是基于jdk提供的动态代理解决方案,当然还有
CGLIB动态代理解决方案,这里我就不意义敲代码理解了直接贴优势跟劣势:
JDK动态代理:基于Java反射机制实现,必须要实现了接口的业务类才能用这种办法生成代理对象。
cglib动态代理:基于ASM机制实现,通过生成业务类的子类作为代理类。
静态代理:代理对象和实际对象都继承了同一个接口,在代理对象中指向的是实际对象的实例,这样对外暴露的是代理对象而真正调用的是 Real Object
优点:可以很好的保护实际对象的业务逻辑对外暴露,从而提高安全性。
缺点:不同的接口要有不同的代理类实现,会很冗余
JDK 动态代理:
为了解决静态代理中,生成大量的代理类造成的冗余;
JDK 动态代理只需要实现 InvocationHandler 接口,重写 invoke 方法便可以完成代理的实现,
jdk的代理是利用反射生成代理类 Proxyxx.class 代理类字节码,并生成对象
jdk动态代理之所以只能代理接口是因为代理类本身已经extends了Proxy,而java是不允许多重继承的,但是允许实现多个接口
优点:解决了静态代理中冗余的代理实现类问题。
缺点:JDK 动态代理是基于接口设计实现的,如果没有接口,会抛异常。
CGLIB 代理:
由于 JDK 动态代理限制了只能基于接口设计,而对于没有接口的情况,JDK方式解决不了;
CGLib 采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑,来完成动态代理的实现。
实现方式实现 MethodInterceptor 接口,重写 intercept 方法,通过 Enhancer 类的回调方法来实现。
但是CGLib在创建代理对象时所花费的时间却比JDK多得多,所以对于单例的对象,因为无需频繁创建对象,用CGLib合适,反之,使用JDK方式要更为合适一些。
同时,由于CGLib由于是采用动态创建子类的方法,对于final方法,无法进行代理。
优点:没有接口也能实现动态代理,而且采用字节码增强技术,性能也不错。
缺点:技术实现相对难理解些。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值