【强烈推荐】Java设计模式:怒肝代理模式,可真把我榨干了!

你的一键三连是我巨大的动力!

设计模式系列

工厂模式 (程序员必知必会)
单例模式 (程序员必知必会)

一、什么是代理模式?

其实代理模式不仅仅只存在编程中,你细想人生处处不代理呢?

  • 一家公司老板身边常常都会有美女秘书(不要想太多),作为老板当然不可能事无巨细,于是就有了秘书代理分担事务(安排行程,会议等等)

  • 一个明星同样由经纪人代理(约参演电影、电视剧、接广告等等)

  • 小到个人,你只管上班,单身的 你要相亲,有婚介所(有七大姑八大姨),想找房有房产中介等等

上面的例子中,可以发现, 代理模式的本质就是将非核心事务转交给第三方处理。

这样的好处有什么?

公司有美女秘书,老板会更有精力干活,什么鬼?

当然不是啦!

老板只需要关注怎么让公司更好发展,获得更多钞能力,至于行程是坐车还是订航班,要告知跟哪个部门开会等非核心事务都给美女秘书安排。秘书是代理,实际决定公司怎么运作的还是老板!
在这里插入图片描述
去找明星合作也是如此,先通过经纪人,再由经纪人告知明星,最后才真正由明星处理。代理是经纪人,明星才是真正去赚钱的。

回到个人本身,你只需关注相亲对象是否是你喜欢的,房子又是否决定要买。至于和谁相亲,有什么样的房子,这些非核心关注的事务统统都转交给婚介和房产中介安排。实际体验相亲的还是我,最终买房的也还是我,婚介还是房产中介就是代理。

似乎扯得有些远…

回到编程本身就是

为其他对象提供一种代理以控制对这个对象的访问,简单来说就是 通过代理对象去访问我们的目标对象。

其本质还是一样一样的,目标对象 就是我们的老板、明星;代理对象 就是秘书、经纪人。

目标对象只做核心的事情,非核心事情还是一样外包给代理对象。所以想要你想要拜访老板还是接触明星,当然是需要经过秘书、经纪人的。

老板、明星忙着挣钱呢,哪有空!有事找秘书、经纪人。

所以代理模式定义也是这样的,只能通过代理对象去访问我们的目标对象。

知道是什么还不行,还得知道怎么实现,走!

二、实现静态代理和动态代理

代码在具体的实现上,跟我们上面所有呈现的例子是一样一样的!

静态代理还是动态代理都只是实现的形式,它们的意图(也就是目的)也是一样一样的!

  • 高内聚低耦合:核心代码专注业务本身,把非业务代码通通交给代理实现

  • 增加扩展性:扩展其他功能时不影响核心业务代码

下面得例子以明星类,作为核心业务代码(专注能红能赚钞能力)经纪人作为代理类负责不修改核心业务扩展功能(把明星安排得明明白白)

2.1 静态代理

定义一个明星的接口,开始他的表演

/**
 * 定义一个明星的接口
 */
public interface IStarDao {
    /**
     * 明星的表演
     */
    void action();
}

实现明星的行为

/**
 * 定义明星用十八般武艺
 */
public class StarDao implements IStarDao {
    @Override
    public void action() {
        System.out.println("一颗剽悍的种子拍广告");
    }
}

静态代理的关键代码

定义经纪人代理类,由经纪人处理代理各种事务,明星专心干活,挣钱。

/**
 * 经纪人代理
 */
public class AgentProxy implements IStarDao {

    /**
     * 由明星行动
     */
    private IStarDao action;

    public AgentProxy(IStarDao target) {
        this.action = target;
    }

    @Override
    public void action() {
        System.out.println("==========广告商联系经纪人接广告==========");
        //表演行动开始::代理人是经纪人,但实际赚钱的是明星
        perform.action();
        System.out.println("==========经纪人数钱==========");
    }
}

开始跑起来

public class Client {
    public static void main(String[] args) {
        
        //要红要挣钱的明星
        StarDao starDao = new StarDao();
        
        //帮明星代理的经纪人
        AgentProxy agentProxy = new AgentProxy(starDao);
        
        //帮明星代理的经纪人,让明星开始他的表演
        agentProxy.action();
    }
}

运行结果

没想到一颗剽悍的种子在代码的世界出道了…
在这里插入图片描述

2.3 静态代理的优劣

静态代理的优势很明显:

拓展新功能时能不修改我们核心代码。 (这也是代理模式的初衷)

但是劣势同样明显:
代理对象和目标对象类(核心业务代码)需要实现一样的接口。 会导致代理类会非常非常多。

一旦经纪人需要代理其他明星就需要再定义接口,现实就不知道经纪人能否代理多个明星,但是你懂所表达的意思就行哈。

因此出现了动态代理。

2.4 JDK动态代理

动态代理分别有JDK代理和cglib代理。

先说JDK代理,JDK代理实现是利用lang包下的reflect

java.lang.reflect

同样定义一个明星的接口,开始他的表演

public interface IStarDao {

    void action();

}

实现明星的行为

public class StarDao implements IStarDao {
    @Override
    public void action() {
        System.out.println("一颗剽悍的种子拍广告");
    }
}

JDK动态代理的关键代码

代理类无需再和目标对象类实现同样的接口,更加的灵活。(可以代理多个明星也不麻烦了,嘿嘿)

代理实例的三个参数:

  • ClassLoader loader : 指定当前目标对象使用的类加载器

  • Class<?>[] interfaces : 目标对象实现的接口类型

  • InvocationHandler : 事情处理,执行目标对象的方法时,会触发事情处理方法,把当前执行的目标对象方法作为参数传入

public class AgentProxyFactory {

    private Object target;

    public AgentProxyFactory(Object target){
        this.target = target;
    }

    /**
     * 给目标对象 生成一个代理对象
     */
    public Object getProxyInstance(){
        return Proxy.newProxyInstance(getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //反射机制调用目标对象的方法
                System.out.println("==========广告商联系经纪人接广告==========");
                Object returnValue = method.invoke(target,args);
                System.out.println("==========经纪人数钱==========");
                return returnValue;
            }

        });
    }
}

可以发现代理类不需要继承接口,但需要在真正实例化时将目标对象给代理对象既可。

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

        //创建目标对象
        StarDao iStarDao = new StarDao();

        //给目标对象,创建代理对象,可以转成Dao
        IStarDao proxyInstance = (IStarDao) new AgentProxyFactory(iStarDao).getProxyInstance();

        //通过代理对象,调用目标对象
        proxyInstance.action();
    }
}

2.5 JDK动态代理的优劣

优势还是一样在扩展功能时不修改核心代码,相比静态代理,JDK动态代理类不需要和目标对象类(核心业务代码)一样都需要实现同样的接口,让代理类更加灵活。

劣势是目标对象依然需要实现接口。

2.5 cglib动态代理

cglib代理也称 子类代理,它是从内存中构建出一个子类来扩展目标对象的功能。

同时CGLIB是一个强大且高性能的代码生成包,可以在运行期扩展Java类与实现Java接口。

cglib跟前面两种很不一样。需要先导入cglib的jar包。

cglib包的下载地址,已经放在微信公众号“一颗剽悍的种子”,回复cglib就可以下载cglib的jar包。
在这里插入图片描述

同样定义目标类,但是不同的不需要再定义接口,直接实现。

public class StarDao  {
    public void action() {
        System.out.println("一颗剽悍的种子拍广告");
    }
}

代理类AgentProxyFactory 实现 MethodInterceptor 接口用来调用目标类,实现动态代理

public class AgentProxyFactory implements MethodInterceptor {

    /**
     * 维护一个目标对象
     */
    private Object targer;

    public AgentProxyFactory(Object targer) {
        this.targer = targer;
    }

    /**
     * 返回一个代理对象,是target 对象的代理对象
     */
    public Object getProxyInstance(){
        //1.创建一个工具类
        Enhancer enhancer = new Enhancer();
        //2.设置父类
        enhancer.setSuperclass(targer.getClass());
        //3.设置回调函数
        enhancer.setCallback(this);
        //4.创建子类对象,即代理对象
        return enhancer.create();
    }

    /**
     * 调用目标对象
     * @param o
     * @param method
     * @param args
     * @param methodProxy
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

        System.out.println("==========广告商联系经纪人接广告==========");
        Object returnValue = method.invoke(targer, args);
        System.out.println("==========经纪人数钱==========");
        return returnValue;
    }
}

cglib动态代理跑起来跟JDK动态代理跑一样。

public class Client {
    public static void main(String[] args) {
        //创建目标对象
        StarDao target = new StarDao();
        //获取到代理对象,并且将目标对象传给代理对象
        StarDao proxyInstance = (StarDao) new AgentProxyFactory(target).getProxyInstance();
        //执行代理对象的方法
        proxyInstance.sing();
    }
}

cglib动态代理,优势相比于静态代理和动态代理,可以说不用像后两者一样去实现同一个接口。让代理模式真正灵动起来。劣势,可能就是需要多导入jar包了,更加方便灵活的同时也是有代价的。

三、代理模式的应用场景

用Spring的同学一定对其两大特性不陌生,一个是IOC,另一个是AOP。面向切面编程AOP的底层其实就是动态代理来实现的。

最后

我是一颗剽悍的种子 把我会的,认真的分享 是我写博客一直不变的信条
用有限的生命去学无限的知识注定是失败,所以我会用有限的时间,尽可能输出最有价值的知识

谢谢能遇见可爱又一键三连的你。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值