代理模式的自我总结

代理模式

什么是代理?

代理模式,就是给一个对象提供种代理对象以控制对该对象的访问。 在这个模式中,我们通过创建代理对象作为“替身"替代了原有对象,从而达到我们控制对象访问的目的。
通俗来说,代理=代替处理。是由另一个对象来代替原对象来处理某些逻辑。
举个例子:房产中介,代跑腿业务,送外卖…

代理模式能带给我们什么?

代理模式能带给我们控制访问某个对象的能力,在某些情况下,一个对象的某些方法想要进行屏蔽或者某种逻辑的控制,则我们可以通过代理的方式进行。再此能力上,引申出来的作用,也是目前在开发中经常使用的一个作用,就是一” 在不修改原对象代码的基础上, 对原对象的功能进行修改或者增强

Java中的代理模式

Java中代理有三种方式来创建代理对象:静态代理、基于jDK (接口)的动态代理、基于CGLIB (父类)的动态代理。

相关概念:

目标类:原对象,我们需要通过代理对象控制它的访问,扩展其功能。
代理类:代理模式产生的对象,是原对象的“替身”,已经在原有基础上修改了逻辑。

静态代理

静态代理,也就是我们会手写代理类的代码,工程中有代理类的源码,代理类会编译后执行。编写代理类,实现目标类的接口或者直接继承目标类,完成逻辑的修改。
接口实现方式:
在这里插入图片描述
继承方式:
在这里插入图片描述
以下的静态代理的继承例子,我们先创建一个顾客类:

public class Customer {
    public String order(String foodName) {
        return "买了" + foodName;
    }
}

再创建一个外卖类:(我是照着B站黑马的视频写的,原老师特别有意思,时间也不长,有兴趣的可以去看一下,搜代理模式就可以了)

public class DeliveryClerx extends Customer {
    @Override
    public String order(String foodName) {
        String result = super.order(foodName);
        System.out.println("已经接受订单,正前往取餐中。。。");
        System.out.println("已经取餐,正在派送。。。");
        return result + ",已经搅拌均匀";
    }
}

下面,让我们测试一下:
在这里插入图片描述
下面我们看一下,静态代理的实现接口例子:
先写一个订单的接口:

public interface OrderInterface {
    public String order(String foodName);
}

把之前顾客的类,我们稍稍改动一下。

public class Customer implements OrderInterface {
    @Override
    public String order(String foodName) {
        return "买了" + foodName;
    }
}

创建一个外卖小哥类:

public class DeliveyClerx2 implements OrderInterface {
    //把原来的对象传入,并保存到成员位置,也就是目标类对象
    public OrderInterface source;

    public DeliveyClerx2(OrderInterface source) {
        this.source = source;
    }

    //顾客和外卖小哥,同时继承这个订单类
    @Override
    public String order(String foodName) {
        String result = source.order(foodName);
        System.out.println("已经接受订单,正前往取餐中。。。");
        System.out.println("已经取餐,正在派送。。。");
        return result + ",已经搅拌均匀";
    }
}

再次测试一下:
在这里插入图片描述

动态代理

动态代理技术。是在内存中生成代理对象的一种技术,也就是整个代理过程在内存中进行,我们不需要手写代理类的代码,也不会存在代理类编译的过程,而是直接在运行期,在JVM中"凭空"造出个代理类对象供我们便用。一般使用的动态代理技术有以下两种:

基于JDK (接口)的动态代理

jdk自带的动态代理技术,需要使用一个静态方法来创建代理对象。它要求被代通对象,也就是目标类。必须实现接口。生成的代理对象和原对象都实现相同的接口。是兄弟关系。

Proxy.newProxyInstance(ClassLoader loader.Class<?>[] interfaces
.InvocationHandler h)

主要关注参数列表:

ClassLoader loader:固定写法,指定目标类对象的类加载器即可。用于加载目标类及其接口的字节码文件(.Class)
通常,使用目标类的字节码对象调用getClassLoader()方法即可得到。
Class<?>[] incerfaces:固定写法,指定目标类的实现的所有接口的字节码对象的数组。
通常,使用目标类的字节码对象调用getInterfaces()方法即可得到。
InvocationHandler h
这个参数是一个接口,主要关注它里面唯一一个方法->invoke

Object invoke(Object proxy,Method method,Object[] args)

下面是例子:
我们修改了下之前的代码,模拟一下,如果一个接口里,有多个方法的时候,怎么办。

public interface OrderInterface {
    public String order(String foodName);

    public void test();

    public void test1();
}

顾客类:

public class Customer implements OrderInterface {
    @Override
    public String order(String foodName) {
        return "买了" + foodName;
    }

    @Override
    public void test() {

    }

    @Override
    public void test1() {

    }
}

测试一下:
在这里插入图片描述
这里用的Proxy是reflect包下的。我们这里就不需要写那么多的别的接口或者继承类什么的了。直接用上面这个就可以了。
换一种方式:
在这里插入图片描述
我们可以看下InvocationHandler的底层:
package java.lang.reflect;

public interface InvocationHandler {
    java.lang.Object invoke(java.lang.Object o, java.lang.reflect.Method method, java.lang.Object[] objects) throws java.lang.Throwable;
}

就是一个很简单的方法。
可以看到所执行的方法名:
在这里插入图片描述
在这里插入图片描述
我们可以指定,哪个方法来执行:
顾客类修改了一下。。

public class Customer implements OrderInterface {
    @Override
    public String order(String foodName) {
        return "买了" + foodName;
    }

    @Override
    public void test() {
        System.out.println("test");
    }

    @Override
    public void test1() {

    }
}

在这里插入图片描述
外卖小哥类,还可以这样写:

public class DeliveyClerx2 implements OrderInterface {
    //接收外部传递过来的InvocationHandler对象
    private InvocationHandler handler;

    public DeliveyClerx2(InvocationHandler handler) {
        this.handler = handler;
    }

    //顾客和外卖小哥,同时继承这个订单类
    @Override
    public String order(String foodName) {
        //每个方法的实现,实际上并没有做其他的事情,而是直接调用了InvocationHandler的invoke方法。
        try {
            Method method = OrderInterface.class.getMethod("order", String.class);
            Object result = handler.invoke(this, method, new Object[]{foodName});
            return (String) result;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }

    @Override
    public void test() {

    }

    @Override
    public void test1() {

    }
}

在这里插入图片描述
总结:
基于接口的动态代理,实际上是在内存中生成了一个对象,该对象实现了指定的目标类对象拥有的接口。所以代理类对象和目标类对象是兄弟关系。
兄弟关系:并列的对象,不能互相转换,包容性比较差。如果配置JDK的动态代理方式,一定要用接口类型接收代理类。

基于CGLIB(父类)的动态代理

第三方CGLIB的动态代理技术,也是可以使用一个静态方法来创建代理对象。
它不要求目标类实现接口,但是要求目标类不能是最终类,也就是不能被final修饰。
因为CGLIB是基于目标类生成改类的一个子类作为代理类,所以目标类必须可被继承。

Enhancer.create(Class type,Callback callback)

主要参数列表:
Class type:指定我们要代理的目标类的字节码对象,也就是指定目标类的类型。
Callback callback:回调,意思是我们提供一个方法,它会在合适的时候帮我们调用它。
Callback 是一个接口,由于该接口只是一个名称定义的作用,并不包含方法的声明。所以我们通常使用它的一个子接口MethodInterceptor ,意思是方法拦截器。MethodInterceptor接口中也只有一个方法,intercept。

Object intercept(Object proxy,Method method,Object[]
args,MethodProxy methodProxy)

我用的是Spring现成的,导个包就可以了
在这里插入图片描述

测试一下:在这里插入图片描述

public class DeliveryClerx extends Customer {
    //模拟在内存中调用CGLIB的 Enhancer.create(Class type,Callback callback)的方法后,该方法内部写了一个什么样的代理类
    //保存方法拦截器
    private final MethodInterceptor methodInterceptor;

    public DeliveryClerx(MethodInterceptor methodInterceptor) {
        this.methodInterceptor = methodInterceptor;
    }

    @Override
    public String order(String foodName) {
        try {
            Method method = OrderInterface.class.getMethod("order", String.class);
            Object result = methodInterceptor.intercept(this, method, new Object[]{foodName}, null);
            return (String) result;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return super.order(foodName);
    }

    @Override
    public void test() {
        try {
            Method method = OrderInterface.class.getMethod("test", String.class);
            Object result = methodInterceptor.intercept(this, method, new null, null);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }

    @Override
    public void test1() {

    }
}

总结

  1. 代理模式在Java开发中是广泛应用的,特别是在框架中底层原理经常涉及到代理模式(尤其是动态代理)
  2. 静态代理和动态代理,实际使用时还是动态代理使用的比较多。原因就是静态代理需要自行手写代码,维护、修改非常繁琐,会额外引入很多工作量。也不能很好的使用配置完成逻辑的指定,所以使用较少。
  3. 基于JDK和基于CGLIB,实际使用时两个都会用到。

1.在spring中,默认情况下它就支持了两种动态代理方式。如果你指定的目标类实现了接口,spring就会自动选择jdk的动态代理。而如果目标类没有实现接口,则spring会使用CGLIB。

2.我们在开发时,由于基于JDK的动态代理要求比较多,更不容易实现,所以很多人习惯于统配置为使用CGLIB进行代理。也就是CGLIB更通用。

3.如果使用dubbo+zookeeper,底层进行代理时,最好配置定死使用CGLIB的方式进行代理。因为dubbo会使用基于包名的扫描方式进行类的处理,而JDK的代理类生成的包名类似com.sun.proxy…格式。我们实际需要让代理类和目标类保持样的包名, 所以只有CGLIB能保持原包名不变生成代理类。I

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值