【设计模式】之静态代理和动态代理

代理模式

什么是代理模式:

例如,有A B C三个类,A原来可以调用C类的方法,现在因为某种原因C类不允许A类调用其方法,但B类可以调用C类的方法,A类通过B类调用C类的方法。这里B是C的代理,A通过代理访问C。

现实生活中也有各种代理模式相关的例子:比如一个公司老板(目标类),普通人(客户类)想找他签文件,一般是不能直接找到老板的,而是通过去找老板的经纪人(代理类),让经纪人帮忙找老板签文件。又比如,平时租房子,我们(客户类)一般都是无法直接找到房东(目标类)租房的,房东会把房子代理给中介(代理类),中介负责帮房东把房子租出去。如果我们想要租房,就会去找中介,然后中介再去和房东确认。

作用

1、功能增强:在原有的功能上,增加了额外的功能

2、控制访问:目标类不让访问其他类访问,只让代理类访问

静态代理

优点:实现简单,容易理解

缺点:

1、当目标类增加了,代理类也要增加

2、当接口中的功能增加或修改,会影响众多的实现类,代理类,他们都需要同步修改

假设小明想买一件耐克球衣,他可以在淘宝店铺上进行购买,然后淘宝店铺又会去耐克工厂拿货,拿货价是100元一件,因为淘宝商家也要赚钱,所以加价了50,所以小明买下一件球衣的价格是150。这个例子中淘宝商家就是代理。

下面用静态代理实现这个例子:

1、定义一个卖衣服的接口

 /**
  * 定义一个卖衣服的接口
  */
public interface SellClothes {
    float sell(int count);
}

2、定义一个目标类,实现卖衣服的这个接口,这里的目标类就是耐克工厂,在这里卖一件衣服的价格是100元

/**
 * 定义一个目标类,实现卖衣服的这个接口
 * 耐克工厂实现了卖衣服这个接口,它可以自己卖衣服,
 * 在工厂这里卖一件衣服,是100元
 */
public class NikeFactory implements SellClothes {
    @Override
    public float sell(int count) {
        System.out.print("目标类,执行sell目标方法。价格是:" + 100f * count);
        return 100f * count;
    }
}

3、定义一个代理类,实现买衣服这个接口,这里的代理类就是淘宝店铺,店铺这里卖一件衣服是150元

/**
 * 定义一个代理类,实现卖衣服的这个接口
 * 淘宝店铺实现了卖衣服这个接口,它从耐克工厂拿货,加价再卖出去
 */
public class TaoBaoShop implements SellClothes {
    //持有目标类对象(跟耐克工厂有合作)
    SellClothes nikeFactory = new NikeFactory();

    @Override
    public float sell(int count) {
        //去耐克工厂拿货
        float price = nikeFactory.sell(count);
        //在拿货价的基础上每件衣服加价了50元,再卖出去
        System.out.print("代理类,执行sell目标方法,并对价格进行加价。最终价格是:" + (float) (price * (1.5)));
        return (float) (price * (1.5));
    }
}

4、创建代理对象,执行目标方法

public static void main(String[] args) {
    SellClothes taoBaoShop = new TaoBaoShop();
    taoBaoShop.sell(10);
}

5、输出结果如下:

在这里插入图片描述

动态代理

在静态代理中,目标类很多的时候,可以使用动态代理,避免静态代理的缺点。

动态代理中目标类即使很多,代理类数量可以很少,当你修改了接口中的方法时,不会影响代理类

在程序执的过程中,使用jdk反射机制,创建代理对象,并动态的指定要代理的目标类。

不需要定义代理类的.java文件——其实就是jvm运行时,动态创建class字节码并加载到jvm中

动态代理有两种实现方式:

1、jdk动态代理

缺点:要求目标类必须有接口,如果没有,则无法使用该方式。

假设小明想买一件耐克球衣,他可以在淘宝店铺上进行购买,然后淘宝店铺又会去耐克工厂拿货,拿货价是100元一件,因为淘宝商家也要赚钱,所以加价了50,所以小明买下一件球衣的价格是150。这个例子中淘宝商家就是代理。

下面用jdk动态代理实现这个例子:

1、定义一个卖衣服的接口

 /**
  * 定义一个卖衣服的接口
  */
public interface SellClothes {
    float sell(int count);
}

2、定义一个目标类,实现卖衣服的这个接口,这里的目标类就是耐克工厂,在这里卖一件衣服的价格是100元

/**
 * 定义一个目标类,实现卖衣服的这个接口
 * 耐克工厂实现了卖衣服这个接口,它可以自己卖衣服,
 * 在工厂这里卖一件衣服,是100元
 */
public class NikeFactory implements SellClothes {
    @Override
    public float sell(int count) {
        System.out.print("目标类,执行sell目标方法。价格是:" + 100f * count);
        return 100f * count;
    }
}

3、创建一个InvocationHandler,完成代理对象的功能——去耐克工厂拿货,并加价50元

/**
 * 定义一个InvocationHandler实现类,在这里完成代理类要实现的功能
 */
public class MySellHandler implements InvocationHandler {

    private Object target = null;

     /**
      * 目标对象是动态的,所以要从外部传进来。
      * 传入的类是什么,就给什么类创建代理,这就是动态代理
      */
    public MySellHandler(Object o) {
        target = o;
    }

    /**
     * 定义一个InvocationHandler实现类,在这里完成代理类要实现的功能
     */
    @Override
    public Object invoke(Object o, Method method, Object[] args) throws Throwable {
        //目标方法的返回值
        Object res = null;
        //执行目标方法,传参:target:目标类实例,args:目标方法所需参数
        res = method.invoke(target, args);
        //执行一些额外的操作,这里是对原价格进行加价
        if (res != null) {
            Float price = (Float) res;
            res = price + 50f;
        }
        System.out.print("代理类,执行sell目标方法,并对价格进行加价。最终价格是:" + res);
        return res;
    }
}

4、使用Proxy类的静态方法,创建代理对象。并把返回值转为接口类型。

public static void main(String[] args) {
        //1、创建目标类对象——耐克工厂
        SellClothes nikeFactory = new NikeFactory();
        //2、创建InvocationHandler对象
        InvocationHandler handler = new MySellHandler(nikeFactory);
        //3、创建代理对象(淘宝代理)
        SellClothes taoBaoProxy = (SellClothes) Proxy.newProxyInstance(
                //目标类的类加载器
                nikeFactory.getClass().getClassLoader(),
                //目标类实现的接口
                nikeFactory.getClass().getInterfaces(),
                //InvocationHandler对象
                handler
        );
        //淘宝代理对象执行sell方法
        taoBaoProxy.sell(1);
    }

5、输出结果如下:

在这里插入图片描述

2、CGLIB动态代理

不要求目标类有接口,其原理是生成目标类的子类,而子类是增强过的,这个子类对象就是代理对象。(继承目标类,创建它的子类,在子类中重写父类的同名方法,实现功能的修改)

缺点:要求目标类和里面的方法都不能是final修饰的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一场雪ycx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值