【设计模式】代理模式

  • 定义

        代理模式是指为对象提供一种代理,以控制对该对象的访问,属于一种结构型模式。在某些情况下,一个对象不能直接访问另一个对象,代理模式就在客户端和被服务端之间起到了一个中介的作用。

  • 举例

    1.静态代理

              场景:张三要结婚,但是由于工作繁忙,找不到对象。那么张三的父亲可以代理张三找对象。

       1. 创建IPerson接口,给出找对象的抽象方法。

public interface IPerson {
    void findLove();
}

      2.创建张三的实例,继承IPerson接口,重写找对象的方法。

public class ZhanSan implements IPerson {
    @Override
    public void findLove() {
        System.out.println("张三要求:肤白貌美大长腿");
    }
}

     3.创建张三的代理对象(他父亲),在构造方法中传入张三,重写张三的找对象方法。

public class ZhangLaoSan implements IPerson {

    private ZhanSan zhanSan;

    public ZhangLaoSan (ZhanSan zhanSan){
        this.zhanSan = zhanSan;
    }

    @Override
    public void findLove() {
        System.out.println("张老三开始物色");
        zhanSan.findLove();
        System.out.println("开始交往");
    }
}

     4.测试

public class Test {
    public static void main(String[] args) {
        ZhangLaoSan zhangLaoSan = new ZhangLaoSan(new ZhanSan());
        zhangLaoSan.findLove();
    }
}

    5.结果

  

       这个场景有个弊端,如果还有赵六要相亲,就需要再创建一个赵六的代理。如果还有其他对象也需要相亲的话,就需要创建更多的代理对象,这样做就违背了开闭原则。而在真实的场景中,有专门找对象的中介或者机构:媒婆。所以这里可以用动态代理来构建媒婆这样角色。

     2.jdk动态代理

       Java的生态中,用的比较多的是JDK自带的动态代理。

       1.创建媒婆代理类,继承InvocationHandler接口,重写invoke方法。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MeiPoProxy implements InvocationHandler {

    private IPerson target;

    public IPerson getInstance(IPerson target){
        this.target = target;
        Class<?> clazz = target.getClass();
        return (IPerson) Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(this.target,args);
        after();
        return result;
    }

    private void after() {
        System.out.println("开始交往");
    }

    private void before() {
        System.out.println("开始物色");
    }


}

      2.创建赵六实例。

public class ZhaoLiu implements IPerson {
    @Override
    public void findLove() {
        System.out.println("赵六要求:有车有房学历高");
    }
}

     3. 测试。

public class Test {
    public static void main(String[] args) {
        MeiPoProxy proxy = new MeiPoProxy();
        IPerson zhangsan =  proxy.getInstance(new ZhanSan());
        zhangsan.findLove();

        IPerson zhaoliu = proxy.getInstance(new ZhaoLiu());
        zhaoliu.findLove();
    }
}

    4.结果。

  

     3.cglib动态代理

       1.引入cglib的jar包

<dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib</artifactId>
  <version>3.2.4</version>
</dependency>

      2.写一个孟婆类继承MethodInterceptor

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

public class CglibMeipo implements MethodInterceptor {

    public Object getInstance(Class<?> clazz) throws Exception {

        Enhancer enhancer = new Enhancer();

        //要把哪个设置为即将生产的新的父类
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);

        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //业务的增强
        before();
        Object obj = methodProxy.invokeSuper(o, objects);
        after();
        return obj;
    }

    private void before() {
        System.out.println("我是媒婆:我要给你找对象,现在已确认需求");
        System.out.println("开始物色");
    }

    private void after() {
        System.out.println("已经找到");
        System.out.println("开始交往");
    }
}

    3.创建单身客户类。这里可以看到Customer是没有实现任何接口的。它是通过动态继承代理对象类来实现动态代理的。

public class Customer {

    public void findLove(){
        System.out.println("客户要求: 肤白貌美大长腿");
    }
}

   4.写一个测试类。

public class Test {
    public static void main(String[] args) {

        try {
            Customer customer = (Customer) new CglibMeipo().getInstance(Customer.class);
            customer.findLove();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

  5.查看测试结果。

     那么cglib的底层是如何实现动态代理的呢,这里可以通过一行代码,将cglib动态生成的代理class从内存读取到磁盘中。

import net.sf.cglib.core.DebuggingClassWriter;

public class Test {
    public static void main(String[] args) {

        try {
            //利用cglib的代理类将内存中的.class写入磁盘
            System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"E://cglib_proxy_class/");

            Customer customer = (Customer) new CglibMeipo().getInstance(Customer.class);
            customer.findLove();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

     这时可以在E盘根目录中看到从内存中读取的文件。打开目录就能看到cglib动态的生成了三个class文件。

     把第二个文件Customer$$EnhancerByCGLIB$$ef2de5ed.class拖入到idea编辑器中,可以看到,这是cglib动态生成的一个代理对象,这个对象继承了目标对象。

    重写了目标对象的方法。

      可以看到这里有一个CGLIB$findLove$0() 的方法,这个方法是被 CglibMeipo类的 method.invokeSuper() 调用的,而下面的  findLove() 方法是被MethodProxy的invoke()方法调用的。 

      所以总的来说,调用的顺序是,代理对象调用findlove() --> 调用拦截器 --> methodProxy. invokeSuper() --> CGLIB$findLove$0() --> Customer.findLove()。

     我们可以点开methodProxy. invokeSuper()的源码看看。

    可以发现,这里获取了代理对象的FastClass信息。然后调用了被代理对象的对应方法。

  • 总结

       1.静态代理和动态代理

      (1)静态代理,实际上就是一种通过继承来重写方法并实现方法增强的,代理类是没有实现任何接口的。而动态代理是通过实现jdk或者cglib的接口来创建一个拦截器,然后在拦截器中进行方法增强的。

      (2)静态代理只能手动完成代理操作,如果代理类增加了方法,代理类需要同步增加该方法。违背了开闭原则。

       2.jdk动态代理和cglib动态代理

      (1)jdk动态代理要求被代理对象必须要实现一个接口,而cglib动态代理则只需要代理对象是一个类即可。

        (2)  jdk动态代理调用代理方法是通过反射机制调用的,而cglib动态代理是通过FastClass机制直接调用的,所以cglib动态代理的执行效率要更高。

      3.代理模式的优点

      (1)代理模式将目标对象与代理对象分离,起到了保护目标对象的作用。

      (2)在一定程度上降低了系统的耦合性,扩展性比较好。

      (3)可以增强目标对象的功能。

      4.代理模式的缺点

      (1)代理模式会造成系统设计中类的增加。

      (2)在客户端和服务端多出了一个代理对象,请求的处理速度会变慢。

      (3)增加了系统的复杂度。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值