ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

静态代理和动态代理

2021-11-20 10:00:49  阅读:159  来源: 互联网

标签:sell SellShoes adidasFactory 静态 代理 number 动态 public


代理模式(Proxy Pattern)

代理模式是程序设计中的一种设计模式, 属于结构型模式.

为其他对象提供一种代理以控制对这个对象的访问, 换句话说, 就是实现代理的类代表他所代理的另一个类的功能.

组成:

  • 抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
  • 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
  • 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。

image

优点:

  • 职责清晰
    • 真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件完成事务,附带的结果就是编程简洁清晰。
  • 代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了中介的作用和保护了目标对象的作用。
  • 高扩展性
    • 可以在目标对象实现的基础上,增强额外的功能操作,扩展目标对象的功能.

遵守开闭原则, 不修改别人已经写好的代码或者方法, 通过代理的方式来扩展该方法.

代理模式分为静态代理、动态代理

  • 静态代理是由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
  • 动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。

1. 静态代理

很多人的朋友圈中会有微商卖鞋, 他们从厂家拿货,然后在朋友圈中宣传,然后卖给熟人。

首先需要一个接口, 通用的接口是代理模式实现的基础.

接口命名为 SellShoes , 功能为卖鞋, 参数是数量, 返回总价

public interface SellShoes {
    int sell(int number);
}

接着还需要一个实现类, 一个名为 AdidasFactory 的类实现了这个接口, 表示微商从源头厂家拿货, 他实现了SellShoes接口

public class AdidasFactory implements SellShoes {
    //一双鞋150
    @Override
    public int sell(int number) {
        System.out.println("[代理]从[阿迪]批发了"+number+"双鞋, 花了"+150*number+"元");
        return 150*number;
    }
}

下面编写代理类 AdidasStaticProxy

public class AdidasStaticProxy implements SellShoes {

    private AdidasFactory adidasFactory;

    public AdidasStaticProxy(AdidasFactory adidasFactory) {
        this.adidasFactory = adidasFactory;
    }

    @Override
    public int sell(int number) {

        int sell = adidasFactory.sell(number);

        System.out.println("[买家]从[代理]购买了"+number+"双鞋, 花了"+8*sell+"元");
        return 8*sell;
    }
}

AdidasStaticProxy就是代理对象, 它有一个sell方法, 但是调用的是AdidasFactory的sell方法, 并对返回的值进行处理.

现在我们编写测试代码

新建一个Customer类代表顾客

public class Customer {
    public static void main(String[] args) {
        AdidasFactory adidasFactory = new AdidasFactory();
        AdidasStaticProxy adidasStaticProxy = new AdidasStaticProxy(adidasFactory);

        adidasStaticProxy.sell(5);
    }
}

测试结果:
image

现在可以看到,代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。值得注意的是,代理类和被代理类应该共同实现一个接口,或者是共同继承某个类。

静态代理的优点

  • 可以使得我们的真实角色更加纯粹,不再去关注一些公共的事情
  • 公共的业务由代理来完成,实现了业务的分工
  • 公共业务发生扩展时变得更加集中和方便

静态代理的缺点

  • 需要代理的类如果很多, 则需要为每一个类编写代理类, 工作量大, 开发效率低

2. 动态代理

上一节代码中 AdidasStaticProxy 类是代理,我们需要手动编写代码让 AdidasStaticProxy 实现 SellShoes 接口,而在动态代理中,我们可以让程序在运行的时候自动在内存中创建一个实现 SellShoes 接口的代理,而不需要去定义 AdidasStaticProxy 这个类。

假设微商小王拿到了Nike和Adidas两个厂家的代理. 我们进行代码的模拟。

直接借用上一节的接口SellShoes

另外编写一个工厂NikeFactory 实现上述接口

public class NikeFactory implements SellShoes {
    //一双鞋100
    @Override
    public int sell(int number) {
        System.out.println("[代理]从[耐克]批发了"+number+"双鞋, 花了"+100*number+"元");
        return 100*number;
    }
}

接下来需要编写小王, 这个动态代理人DynamicProxy

public class DynamicProxy implements InvocationHandler {
    private Object target;
    public void setTarget(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.print("微商小王");
        String name = target.getClass().getName();
        log(name);

        return method.invoke(target, args);
    }

    private void log(String message){
        System.out.println("贩卖["+message+"]厂的鞋");
    }
}

然后, 顾客可以买鞋了Customer

public class Customer {
    public static void main(String[] args) {
        NikeFactory nikeFactory = new NikeFactory();
        AdidasFactory adidasFactory = new AdidasFactory();

        DynamicProxy dynamicProxy = new DynamicProxy();
        dynamicProxy.setTarget(adidasFactory);
        SellShoes proxy = (SellShoes) Proxy.newProxyInstance(adidasFactory.getClass().getClassLoader(), adidasFactory.getClass().getInterfaces(), dynamicProxy);
        proxy.sell(10);

        dynamicProxy.setTarget(nikeFactory);
        SellShoes proxy1 = (SellShoes) Proxy.newProxyInstance(nikeFactory.getClass().getClassLoader(), nikeFactory.getClass().getInterfaces(),dynamicProxy);
        proxy1.sell(50);
    }
}

输出结果:
image

我们并没有像静态代理那样为 SellShoes 接口实现一个代理类,但最终它仍然实现了相同的功能, 并且不同厂家都可以通过小王这一个代理来实现

小王不仅仅可以卖鞋子, 甚至可以卖衣服, 先编写一个卖衣服的接口SellClothes

public interface SellClothes {
    int sell(int number);
}

创建品牌工厂来实现接口UNIQLOFactory

public class UNIQLOFactory implements SellClothes{
    @Override
    public int sell(int number) {
        System.out.println("[代理]从[优衣库]批发了"+number+"件衣服, 花了"+50*number+"元");
        return 50*number;
    }
}

再次测试

public class Customer {
    public static void main(String[] args) {
        NikeFactory nikeFactory = new NikeFactory();
        AdidasFactory adidasFactory = new AdidasFactory();
        UNIQLOFactory uniqloFactory = new UNIQLOFactory();

        DynamicProxy dynamicProxy = new DynamicProxy();
        dynamicProxy.setTarget(adidasFactory);
        SellShoes proxy = (SellShoes) Proxy.newProxyInstance(adidasFactory.getClass().getClassLoader(), adidasFactory.getClass().getInterfaces(), dynamicProxy);
        proxy.sell(10);

        dynamicProxy.setTarget(nikeFactory);
        SellShoes proxy1 = (SellShoes) Proxy.newProxyInstance(nikeFactory.getClass().getClassLoader(), nikeFactory.getClass().getInterfaces(),dynamicProxy);
        proxy1.sell(50);

        dynamicProxy.setTarget(uniqloFactory);
        SellClothes proxy2 = (SellClothes) Proxy.newProxyInstance(uniqloFactory.getClass().getClassLoader(), uniqloFactory.getClass().getInterfaces(),dynamicProxy);
        proxy2.sell(50);
    }
}

测试结果
image

可见, 我们仅仅通过小王这一个代理就可以代理不同品牌(同一接口的不同实现类), 甚至贩卖不同类型的物品(不同接口)

3. Proxy 和 InvocationHandler

3.1 Proxy

Proxy类就是用来创建一个真实对象的真实代理对象,提供了很多方法,最常用的就是newProxyInstance方法

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

方法的作用就是创建一个代理类对象,总共有三个参数,含义如下:

  • loader:一般指的是真实对象的ClassLoader对象
  • interfaces:一般指的是真实对象所实现的接口.
  • h: 指的就是实现了InvocationHandler接口的代理类调用处理类,表示的是当动态代理对象调用方法时会关联到哪个实现了InvocationHandler接口的对象上,并进行转发到invoke()方法中

image

3.2 InvocationHandler

InvocationHandler 是一个接口,官方文档解释说,每个代理的实例都有一个与之关联的 InvocationHandler 实现类,如果代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类,由它决定处理。

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

InvocationHandler 内部只是一个 invoke() 方法,正是这个方法决定了怎么样处理代理传递过来的方法调用。

  • proxy 代理对象
  • method 代理对象调用的方法
  • args 调用的方法中的参数

image

因为,Proxy 动态产生的代理会调用 InvocationHandler 实现类,所以 InvocationHandler 是实际执行者。

4. 实际场景应用

校验用户权限,每一个菜单请求,都要判断一下请求的用户是否有该菜单权限。菜单多了,代码冗余,且容易遗漏。
通过动态代理就可以实现为:每一个用户,每一个菜单的请求,都经过代理(proxy),由他判断是否有权限,调用者只需要调用,实现自己的逻辑,不关心权限问题。

标签:sell,SellShoes,adidasFactory,静态,代理,number,动态,public
来源: https://www.cnblogs.com/kaixin-wbl/p/15558035.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有