设计模式之代理模式

什么是代理模式

代理模式是为其他对象提供一种代理以控制对这个对象的访问。

代理模式可以分为两种:

  • 静态代理:代理类在编译器已经确定。
  • 动态代理:代理类在JVM运行时动态生成。

静态代理相对动态代理来说,效率比较高,但是一旦修改静态代理的接口,代理类和委托类都需要做出修改,十分恼人。

原理图

在这里插入图片描述

注:该原理图是静态代理原理图。动态代理时,Proxy类无需实现Subject,若使用cglib进行代理,则Subject接口可以不存在,但代理类需要实现MethodInterceptor接口。

为什么要用代理模式

使用代理模式通常有以下四种情况:

  1. 远程代理,为一个对象在不同的地址空间提供局部代表。这样可以隐藏一个对象存放于不同地址空间的事实。

  2. 虚拟代理,根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的真实对象。

  3. 安全代理,用来控制真实对象访问时的权限。

  4. 智能指引,当调用真实的对象时,代理处理另外一些事。

在Spring Boot中所使用的AOP技术便是用了动态代理。

创业者的创业之路

凡事都要亲历亲为

一名创业者在创业初期凡事都亲历亲为,找了几个人组成了一个团队。

招聘接口:

public interface IRecruit {
    void recruit();
}

创业者:

public class Entrepreneur implements IRecruit {

    public void order() {
        System.out.println("拉项目");
    }

    @Override
    public void recruit() {
        System.out.println("招聘");
    }
}

公司:

public class company {
    public static void main(String[] args) {
        Entrepreneur entrepreneur = new Entrepreneur();
        entrepreneur.order();
        entrepreneur.recruit();
    }
}

运行结果:

拉项目
招聘

小王做招聘——静态代理

渐渐地,团队发展成了一个小公司,事情愈发繁杂了起来。创业者一想,还是事事亲力亲为很可能会被很多无关紧要的小事占用太多时间,不行,得分出去一部分事情让别人做。

“这样吧,招聘新员工的事情让小王去做吧。”

从此,小王代理了创业者的招聘事项。

小王:

public class Wang implements IRecruit{
    private Entrepreneur boss;

    public Wang(Entrepreneur boss) {
        this.boss = boss;
    }

    /**
     * 代理boss的recruit,也会使用到boss的recruit,并不是不一样的重写。
     * 小王的recruit是对boss的recruit的增强。
     */
    @Override
    public void recruit() {
        System.out.println("筛选面试者");
        boss.recruit();
        System.out.println("发offer");
    }
}

现在的公司:

public class company {
    public static void main(String[] args) {
        Entrepreneur entrepreneur = new Entrepreneur();
        Wang wang = new Wang(entrepreneur);
        entrepreneur.order();

        wang.recruit();
    }
}

结果:

拉项目
筛选面试者
招聘
发offer

现在的小王和创业者,便是一种静态代理的关系,小王和创业者实现了同一个招聘接口,都可以进行招聘,现在不需要创业者进行招聘了,小王代理了这件事,由于小王可以更加专注于招聘这件事,他也将招聘事宜进行了增强。


招聘秘书——动态代理

经过全体公司员工的不懈努力,现在的公司已经由一个名不见经传的小公司,发展成了一个中型企业。

创业者也将公司体系进行了改变,变得更加合理,层级更加明确,每件事都有对应的负责人。

但创业者毕竟是公司的老板,每天还是要面对很多的文件,单单自己一个人是很难办完的;公司里其他人的工作也不轻松,叫他们来给自己打下手未免有些不妥。

招聘一个秘书好了,专门在老板身边整理整理文件,做一些杂事。

这就出现了一个问题:一个人出来工作,并不是注定要给创业者做秘书,在应聘之前,甚至都不知道这创业者的详细信息,仅能通过公司公开的信息了解一二;甚至有的人来应聘,是从前一家公司跳槽来的,原来是给别人做秘书,代理的人并不是现在的创业者。

这样,我们的代码不能写成与小王类似的代码。

那么就需要用到动态代理了。

整理文件接口:

public interface IOrganize {
    void organizeDocuments();
}

现在的创业者:

public class Entrepreneur implements IOrganize {
    @Override
    public void organizeDocuments() {
        System.out.println("整理文件");
    }
}

来应聘的秘书,都应当可以动态代理老板的工作:

秘书类:

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

public class SecretaryFactory {
    private Object o; //维护一个目标对象,由于实现不知道是什么类型,故用Object

    public SecretaryFactory(Object o) {
        this.o = o;
    }

    /**
     * 使用JDK中的Proxy类生成一个代理对象,
     * 用到的newProxyInstance方法的三个参数,分别是:
     * 1.目标对象的类加载器
     * 2.目标对象实现的接口类型
     * 3.事件处理(执行目标对象方法是,会触发事情处理器的方法,把当前执行的目标对象方法作为参数传入)
     */
    public Object getProxyInstance() {
        Object instance = Proxy.newProxyInstance(o.getClass().getClassLoader(),
                o.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("秘书开始代理");
                        Object res = method.invoke(o, args);
                        System.out.println("秘书代理完成");
                        return res;
                    }
                });
        return instance;
    }
}

当前的公司:

public class company {
    public static void main(String[] args) {
        Entrepreneur entrepreneur = new Entrepreneur();
        IOrganize secretary = (IOrganize) new SecretaryFactory(entrepreneur).getProxyInstance();//获取代理对象
        secretary.organizeDocuments(); //通过代理对象调用目标对象的方法
    }
}

运行结果:

秘书开始代理
整理文件
秘书代理完成

现在有一个人成功应聘了秘书的职位,那么这位秘书便知道了具体是给谁、做什么工作,随后便代理了原本就要交给秘书的部分的工作。

上面这种代码的是实现方式是使用了JDK的Proxy类,即通过JDK完成代理。


招聘秘书之前没有秘书要做什么的标准——用cglib动态代理

在上面的代码中,我们不让创业者实现IOrganize接口,再运行代码变化出现以下提示:

Exception in thread "main" java.lang.ClassCastException: class com.sun.proxy.$Proxy0 cannot be cast to class com.designpattern.proxy.IOrganize (com.sun.proxy.$Proxy0 and com.designpattern.proxy.IOrganize are in unnamed module of loader 'app')
	at com.designpattern.proxy.company.main(company.java:11)

这时,招聘的秘书不再能完成这项工作,那招聘秘书的意义也就消失了。

有没有别的方法可以让秘书重新动态代理创业者的工作呢?

使用cglib便可以动态代理一个没有实现任何接口的类:

cglib所使用的jar包自取:(链接:https://pan.baidu.com/s/18uElgT9xXi1Fmi1SJOJITw 提取码:iuvt )

秘书类:

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 SecretaryFactory implements MethodInterceptor {
    private Object o;

    public SecretaryFactory(Object o) {
        this.o = o;
    }

    public Object getProxyInstance() {
        Enhancer enhancer = new Enhancer(); //创建一个工具类
        enhancer.setSuperclass(o.getClass()); //设置父类
        enhancer.setCallback(this); //设置回调函数
        return enhancer.create(); //创建子类对象(代理对象)
    }

    //重写intercept方法,调用目标对象的方法
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("秘书使用cglib开始代理");
        Object res = method.invoke(o, objects);
        System.out.println("秘书使用cglib代理完毕");
        return res;
    }
}

当前公司:

public class company {
    public static void main(String[] args) {
        Entrepreneur entrepreneur = new Entrepreneur();
        Entrepreneur secretary = (Entrepreneur) new SecretaryFactory(entrepreneur).getProxyInstance();//获取代理对象

        secretary.organizeDocuments(); //通过代理对象调用目标对象的方法
    }
}

运行结果

秘书使用cglib开始代理
整理文件
秘书使用cglib代理完毕

这样,即使创业者没有制定任何标准来做一件事,他的秘书也可以代理这项工作。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值