其他分享
首页 > 其他分享> > 静态代理和动态代理

静态代理和动态代理

作者:互联网

代理模式(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)

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

image

3.2 InvocationHandler

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

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

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

image

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

4. 实际场景应用

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

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