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. 同上,我们创建 饮料公司、矿泉水厂
/**
* 饮料公司接口
*/
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静态代理和动态代理理解
如有错误,欢迎指正;如有侵权,请联系作者。