Java的动、静态代理

Java的动、静态代理

一、什么是代理?

百度百科给出的定义:
Java代理模式即Proxy Pattern,23种java常用设计模式之一。
代理模式的定义:对其他对象提供一种代理以控制对这个对象的访问。

二、静态代理

看完这个定义我们可能还是不懂,一脸懵,那不如来举个简单例子:

1. 假设有一个XX牌饮料公司,它是个接口,有个生产饮料的方法

/**
 * 饮料公司接口
 */
public interface Drink {

    /**
     * 生产饮料
     */
    void makeDrink();
}

2. 饮料公司授权一个矿泉水厂家生产矿泉水

这个厂家需要实现饮料公司的接口,并且实现饮料公司的生产饮料的方法,从而生产矿泉水

/**
 * 获得了饮料公司生产矿泉水的授权,即委托类
 */
public class WaterDrink implements Drink {

    @Override
    public void makeDrink() {
        System.out.println("我们正在生产矿泉水");
    }
}

3. 代理商(中间商)

我们顾客买东西,肯定不是直接从厂家那买到的,这个中间肯定经过了代理商(中间商),
这些中间商对产品就行“包装”、宣传,然后卖给我们,
Java的代理类就是这个代理商(中间商),它也要实现饮料公司的接口,但是他不负责生产,由矿泉水厂进行生产,他只负责“包装” ,所以他要传入真正的生产厂家负责生产

/**
 * 矿泉水 代理,即代理类
 */
public class WaterDrinkProxy implements Drink {
    /**
     * 传入真正的生产厂家
     */
    //     建议这样写:
    private Drink drink;
    public WaterDrinkProxy(Drink drink) {
            this.drink = drink;
    }

    /**
     * 传入真正的生产厂家
     */        
    // 这是方便理解的写法
    // private WaterDrink waterDrink;
    // public WaterDrinkProxy(WaterDrink waterDrink) {
    //     this.waterDrink = waterDrink;
    // }

    @Override
    public void makeDrink() {
    	// 生产前打个广告
        System.out.println("我们马上要生产矿泉水了,要买的快来买");
        // 正在生产
        drink.makeDrink();
        // 生产后打个广告
        System.out.println("矿泉水生产好了,要买的快来买");
    }
}

5. 测试(Test1)

这样,一个简单的静态代理就是实现了,我们测试下

public class Test1 {
    public static void main(String[] args) {
        //父类引用 指向 子类代理对象 并且 传入 真实对象
        Drink drink = new WaterDrinkProxy(new WaterDrink());
        drink.makeDrink();
    }
}

6. Test1类的main方法运行结果

我们马上要生产矿泉水了,要买的快来买
我们正在生产矿泉水
矿泉水生产好了,要买的快来买

7. 小结

代理能在真正的实现类的实现方法上进行“加强处理”。

其实还可以写一个饮料工厂类,将真正的实现类隐藏,就像这样:

/**
 * 饮料工厂
 */
public class DrinkFactory {
    /**
     * 获取矿泉水厂代理
     */
    public static Drink getWaterDrink(){
        return new WaterDrinkProxy(new WaterDrink());
    }
}

写一个测试类

public class Test2 {
    public static void main(String[] args) {
        Drink drink = DrinkFactory.getWaterDrink();
        drink.makeDrink();
    }
}

Test2类的main方法 与 Test1类的main方法 运行结果一样

我们马上要生产矿泉水了,要买的快来买
我们正在生产矿泉水
矿泉水生产好了,要买的快来买

8. 结论

静态代理的优点:
代理使 调用接口者(客户端) 不需要知道这个接口的实现类是什么,具体怎么做的,客户端只需知道代理即可,可以解耦合;
又或者是我们再写个工厂,那就代理都不需要知道是谁了,只需要知道工厂,然后调用工厂的方法即可。

静态代理的缺点:

  1. 增加了代码维护的复杂度,为什么这么说:假如我们的饮料公司扩展业务了,我还要卖水果(举个例子),那它的接口就需要再加一个卖水果的方法,继而它的实现类、实现类的代理类也都需要实现这个方法,这显然是很麻烦的。
  2. 静态代理只能代理一种类型的对象,多种类型对象就不行了,什么意思呢?还是上边的内容举例:我代理商还想代理其他公司的厂的产品,例如我还要代理酒公司(接口)的授权厂商 葡萄酒厂 的生产葡萄酒方法,那显然不行。

这时候我们就需要动态代理来解决这些不足了

二、动态代理

通过上面静态代理的介绍,我们发现静态代理每个代理类只能为一个接口服务,这样的话,程序开发中会产生许多的代理类,所以我们就要想办法通过一个代理类完成全部的代理功能,这时候就需要用到动态代理。

1. 同上,我们创建 饮料公司、矿泉水厂

/**
 * 饮料公司接口
 */
public interface Drink {

    /**
     * 生产饮料
     */
    void makeDrink();
}
/**
 * 矿泉水厂
 * 获得了饮料公司生产生产矿泉水的授权
 */
public class WaterDrink implements Drink {

    @Override
    public void makeDrink() {
        System.out.println("我们正在生产矿泉水");
    }
}

2.动态代理类

/**
 * 动态代理类
 */
public class MyInvocationHandler implements InvocationHandler {

    /**
     * 需要代理的目标对象。这里用Object(这样就可以代理任意类型对象)
     */
    private Object object;

    /**
     * 绑定关系,即代理类代理什么东西,然后和它绑定,返回代理对象
     * @param object
     * @return
     */
    public  Object newInstance(Object object) {
        this.object = object;
        //第一个参数:需要被代理的对象的类加载器
        //第二个参数:需要被代理的对象的接口
        //第三个参数:被拦截的方法(被代理的对象的方法)在被拦截时需要执行MyInvocationHandler的invoke方法
        return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
    }

    /**
     * @param proxy   动态代理生成的代理类
     * @param method  方法对象
     * @param args    方法参数
     * @return
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object obj = null;
        try {
            //调用方法前的处理
            System.out.println("我们马上要生产了");
            // 调用被代理对象的方法
            obj = method.invoke(object,args);
            //调用方法后的处理
            System.out.println("我们生产完了");
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return obj;
    }
}

3.测试(Test3)

public class Test3 {
    public static void main(String[] args) {
        MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
        Drink drink = (Drink) myInvocationHandler.newInstance(new WaterDrink());
        drink.makeDrink();
    }
}

4.运行结果

我们马上要生产了
我们正在生产矿泉水
我们生产完了

5.小结

前面说到,静态代理有两个缺陷:业务扩展麻烦、不能代理多个对象,这里就得到了解决.

首先是业务扩展,饮料公司(接口)新加业务(加方法),则只要在其实现类中新增方法即可,代理类不需要处理。

然后是不能代理多个对象问题,我们举个例子,先创建一个酒公司(接口):

/**
 * 酒公司
 */
public interface Wine {

    /**
     * 生产酒
     */
    void makeWine();
}

创建接口的实现类 葡萄酒厂

/**
 * 葡萄酒厂
 */
public class PutaoWine implements Wine{
    @Override
    public void makeWine() {
        System.out.println("我们在生产葡萄酒");
    }
}

修改Test3测试类的main方法

public class Test3 {
    public static void main(String[] args) {
        MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
        Drink drink = (Drink) myInvocationHandler.newInstance(new WaterDrink());
        drink.makeDrink();

        System.out.println("---------------------------------");

        Wine wine = (Wine) myInvocationHandler.newInstance(new PutaoWine());
        wine.makeWine();
    }
}

运行结果

我们马上要生产了
我们正在生产矿泉水
我们生产完了
---------------------------------
我们马上要生产了
我们在生产葡萄酒
我们生产完了

可以看到,我们可以通过MyInvocationHandler 代理了不同类型的对象

6.结论

优点:
动态代理与静态代理相比,它最大的好处是我们在接口中声明的所有方法,都会被转移然后去调用代理类的一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。而且动态代理的应用使我们的类职责更加单一,复用性更强。
缺点:
1.实际开发中,invoke方法中我们肯定要判断代理的实际对象具体是什么对象,然后分情况处理,这就导致要写很多if语句

该文章内容部分摘自以下链接:
静态代理和动态代理的区别和联系
动态代理与静态代理区别
JAVA静态代理和动态代理理解

如有错误,欢迎指正;如有侵权,请联系作者。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值