设计模式-静态代理和动态代理

一、静态代理模式

在我看来,代理模式就是现实中的各种外包。

例如工厂招聘流程:前期招工场地确定以及布置 -> 招工宣传 -> 对应聘者的考核 -> 工资的确认 ->工人进厂然而,对于工厂来说,这一系列的动作中只有“工人进厂”是他们的核心需求,前期的招聘宣传组织考核并不是他们所擅长的,并且耗时耗力。
因此,本着用心做好自己擅长的事的原则,他们找到了人力资源公司,将招工的需求外包给人力资源公司,而工厂要做的只是专注于接收招聘而来的工人,处理他们在工厂内的一切事宜

代理模式在我看来,就是专注于自己的核心需求,针对那些实现核心需求的过程中也不可少的附加功能,可以交给代理去实现,让自己的职责更简单 纯粹。

静态代理demo实例:

package designmode.proxymode.staticproxy;

/**
 * @author pcf
 * @date 2023/3/7 16:42
 */
public interface Recruit {

    // 招聘
    void recruit();
}
package designmode.proxymode.staticproxy;

/**
 * 需要招人的工厂 : 我只想快速招到工人
 * @author pcf
 * @date 2023/3/7 16:40
 */
public class Factory implements Recruit {
    @Override
    public void recruit() {
        System.out.println("招工成功!");
    }
}
package designmode.proxymode.staticproxy;

/**
 * 人力资源公司代理招工
 * @author pcf
 * @date 2023/3/7 16:43
 */
public class HumanResourcesCompanyProxy implements Recruit{
    private Factory factory;

    public HumanResourcesCompanyProxy(Factory factory) {
        this.factory = factory;
    }

    @Override
    public void recruit() {
        // 人力资源公司:确定面试场地
        findArea();
        // 人力资源公司:发布招工信息
        releaseInformation();
        // 人力资源公司:对应聘者考核筛选
        filterPeople();
        // 人力资源公司: 给工人订工资,送工人去工厂
        dealWithPeople();
        // 工厂:接收到工人
        factory.recruit();
        // 人力资源公司: 与工厂结算利润
        settlementWithFactory();
    }

    private void findArea() {
        System.out.println("人力资源公司:确定面试场地。");
    }

    public void releaseInformation() {
        System.out.println("人力资源公司:发布招工信息。");
    }

    public void filterPeople() {
        System.out.println("人力资源公司:对应聘者考核筛选");
    }

    public void dealWithPeople() {
        System.out.println("人力资源公司: 给工人订工资,送工人去工厂");
    }

    public void settlementWithFactory() {
        System.out.println("人力资源公司: 与工厂结算利润");
    }

}
package designmode.proxymode.staticproxy;

import org.junit.Test;

/**
 * @author pcf
 * @date 2023/3/7 16:54
 */
public class TestStaticProxy {
    /**
     * 在我看来,代理模式就是现实中的各种外包。
     * 对于此例来说:
     * 招工是一系列的动作简化来看:前期招工场地确定 -> 招工宣传 -> 对应聘者的考核 -> 工资的确认 -> 工人进厂
     * 然而,对于工厂来说,这一系列的动作中只有“工人进厂”是他们的核心需求,前期的招聘宣传组织考核并不是他们
     * 所擅长的,并且耗时耗力。
     * 因此,让专业的人干专业的事,他们找到了人力资源公司,将招工的需求外包给人力资源公司,
     * 而工厂要做的只是专注于接收招聘而来的工人,处理他们在工厂内的一切事宜
     *
     * 代理模式在我看来,就是专注于自己的核心需求,针对那些实现核心需求的过程中也不可少的附加功能,可以交给
     * 代理去实现,让自己的职责更简单 纯粹
     *
     */
    @Test
    public void testStaticProxy() {
        // 该工厂招人
        Factory factory = new Factory();
        // 工厂找到人力资源公司要人
        HumanResourcesCompanyProxy humanResourcesCompany = new HumanResourcesCompanyProxy(factory);
        // 人力资源公司给工厂拉人
        humanResourcesCompany.recruit();
    }
}

二、动态代理模式

为什么会有动态代理呢? 因为静态代理有时候不太好用,有点麻烦。
静态代理是项目运行之前就写好的代理,可以理解为是针对某个角色已定的代理方案。
在上面的例子中:
如果此时有其它的工厂也要招工,委托这个人力资源公司。比如之前是电子厂,现在是渔场,可能面向人群、考核要求、附加条件等都发生了变化。这时之前的代理不适配了那么这个人力资源公司就要拿出来另一套符合该工厂要求的招聘方案(新创建一个代理)。这显然是很麻烦的,可能针对一个新厂子就要先创建一个新的代理。

这时候就要用动态代理了:
相对比与静态代理项目运行前就先写好的代理,动态代理是在项目运行后,在需要的时候动态生成代理。我们只需要在创建代理的时候把需要附加的功能在代理中实现即可。

动态代理demo实例:
package designmode.proxymode.dynamicproxy;

/**
 * @author pcf
 * @date 2023/3/7 16:42
 */
public interface Recruit {

    // 招聘
    void recruit();
}
package designmode.proxymode.dynamicproxy;

/**
 * 需要招人的工厂 : 我只想快速招到工人
 * @author pcf
 * @date 2023/3/7 16:40
 */
public class Factory implements Recruit {
    @Override
    public void recruit() {
        System.out.println("招工成功!");
    }
}
package designmode.proxymode.dynamicproxy;

import org.junit.Test;

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

/**
 * @author pcf
 * @date 2023/3/8 11:47
 */
public class TestDynamicProxy {
    /**
     * 为什么会有动态代理呢? 因为静态代理有时候不太好用,有点麻烦。
     * 静态代理是项目编译之前就写好的代理,可以理解为是针对某个角色已定的代理方案。
     * 在上面的例子中:
     * 如果此时有其它的工厂也要招工,委托这个人力资源公司。比如之前是电子厂,现在是渔场,可能面向人群、考核要求、附加条件等
     * 都发生了变化。这时之前的代理不适配了,那么这个人力资源公司就要拿出来另一套符合该工厂要求的招聘方案(新创建一个代理)。
     * 这显然是很麻烦的,可能针对一个新厂子就要先创建一个新的代理。
     *
     * 这时候就要用动态代理了:
     * 相对比与静态代理运行前就先写好的代理,动态代理是在项目运行后,在需要的时候动态生成代理。我们只需要在创建代理的时候把
     * 需要附加的功能在代理中实现即可。
     *
     * 这样的话,不论针对什么操作,我们都可以通过动态代理,把核心业务和附加功能分隔开。
     * 如此例中:让Factory类更加专注于实现自己的工厂和工人管理,其它招聘事宜交给代理去完成。
     *
     *
     * 总结下来:代理就是对原有业务的加强和补充,让类更关注于本类中原有功能的实现,多出来的附加功能,如日志打印、事务控制,
     * 交给代理去做。
     * 事实上spring的AOP就是通过动态代理实现的。
     *
     */

    @Test
    public void testDynamicProxy() {

        // 使用动态代理对象: 指的是在程序运行过程中动态通过代码方式为指定的类生成动态代理对象
        // proxy: 用来生成动态对象的类
        // 目标类
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // 取当前线程的类加载器
        Class[] classes = {Recruit.class};
        /**
         * @Note  为目标类生成动态代理对象
         *
         * @Param classLoader  类加载器
         *                      需要底层的类加载器去读取到目标类的class文件并加载到内存中,然后才能对应的为其生成动态代理对象
         * @Param classes      目标类的接口的类型的数组
         *                      一个类可以实现多个接口,故用数组
         * @Param new InvocationHandler() {}   InvocationHandle接口类型
         *
         * @Return  userServiceProxy    创建好目标类的动态代理对象
         */
        Recruit factoryProxy = (Recruit) Proxy.newProxyInstance(classLoader, classes, new InvocationHandler() {
            @Override
            /**
             *  @Param proxy Object
             *          当前创建好的代理对象
             *  @Param method Method
             *          当前代理对象执行的方法对象
             *  @Param args Object[]
             *          当前代理对象执行的方法的参数
             *
             * @Note  通过动态代理对象调用自己里面代理方法时会优先执行InvocationHandle类中的invoke
             */
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 调用目标类中业务方法前的操作
                System.out.println("==============InvocationHandle类中的invoke=================");
                System.out.println("当前执行的方法: " + method.getName());
//                System.out.println("当前执行的方法的参数: " + args[0]); // 此示例中recruit是无参方法,因此打印参数会报错
                // 招工前的附加操作
                System.out.println("proxy-人力资源公司:确定面试场地");
                System.out.println("proxy-人力资源公司:发布招工信息");
                System.out.println("proxy-人力资源公司:对应聘者考核筛选");
                System.out.println("proxy-人力资源公司: 给工人订工资,送工人去工厂");
                // 招工
                Object invoke = method.invoke(new Factory(), args);
                // 招工后的附加操作
                System.out.println("proxy-人力资源公司:与工厂结算");
                return invoke;
            }
        });
        System.out.println(factoryProxy.getClass());
        factoryProxy.recruit();
    }
}

总结下来:代理就是对原有业务的加强和补充,让类更关注于本类中原有功能的实现,多出来的附加功能,如日志打印、事务控制,交给代理去做。
而相对于静态代理,动态代理的优势:
1、可以减少代理对象的个数,降低程序复杂度。
2、易于复杂业务的动态扩展。
事实上,spring的AOP就是通过动态代理实现的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容