什么是代理?
通过第三方(代理)来访问目标对象
例如
买房,通过房产中介买房而不是直接找开发商
看电影,去电影院而不是直接购买电影版权
为什么需要代理?
协调调用者和被调用者,降低耦合性
例如
代理有什么用?
能够在不影响目标对象的基础上,增强功能
例如
中介帮助客户找到最适合的房产,而客户支付一定的费用
影院提供大量电影供消费者选择,消费者支付一定的费用
(否则看一部电影,买一次版权,代价巨大)
饭店可以提供食物,而用户购买食物
如果用户不方便出门,那样饭店赚不到钱,客户吃不到东西
但是通过外卖平台,就可以在不影响饭店做菜的基础上,为客户提供食物
外卖平台本身不提供食物
整个过程分为3步
饭店
public interface Restaurant {
/**
* 提供食物
* @param name 食物名称
*/
public void food(String name);
}
肯德基
public class KFC implements Restaurant{
@Override
public void food(String name) {
System.out.println(name + " ( KFC )");
}
}
外卖平台
public class Takeout implements Restaurant {
// 入驻平台的饭店 (理论上有多个,应该是数组,但这里做简化)
Restaurant restaurant;
Takeout(Restaurant restaurant) {
this.restaurant = restaurant;
}
// 提供食物
@Override
public void food(String name) {
// 客户点餐
order(name);
// 让饭店做饭
this.restaurant.food(name);
// 配送
deliver();
}
// 点餐
public void order(String name) {
System.out.println("点餐 : " + name);
}
// 配送
public void deliver() {
System.out.println("送餐");
}
}
测试
public class App {
public static void main(String[] args) {
// KFC 开店
Restaurant kfc = new KFC();
// KFC 入驻 外卖平台
Takeout takeout = new Takeout(kfc);
// 客户在外卖平台点餐
takeout.food("汉堡");
}
}
控制台输出
点餐 : 汉堡
汉堡 ( KFC )
送餐
为什么叫静态呢?
代理对象(外卖平台)必须事先存在
代理对象不需要实现存在,而是在代码运行的过程中生成
需要借助java.lang.reflect.Proxy的newProxyInstance方法
// 获取代理对象的实例
public static Object newProxyInstance(
ClassLoader loader, // 类加载器
Class<?>[] interfaces, // 接口列表
InvocationHandler h // 处理程序
)
比较陌生的是处理程序(java.lang.reflect.InvocationHandler)
public interface InvocationHandler {
public Object invoke(
Object proxy, // 调用方法的代理对象
Method method, // 调用的方法
Object[] args // 调用方法的参数
) throws Throwable;
}
定义处理程序
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class Takeout implements InvocationHandler {
private Object obj;
Takeout(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 点餐
order(args);
// 做饭
Object result = method.invoke(obj, args);
// 送餐
deliver();
// 虽然 food 方法没有返回值,但为了适应所有的情况
return result;
}
// 点餐
public void order(Object[] args) {
System.out.print("点餐 : ");
for (Object arg : args) {
System.out.println(arg);
}
}
// 送餐
public void deliver() {
System.out.println("送餐");
}
}
定义代理对象获取类
通过该类的静态方法来获取代理对象
import java.lang.reflect.Proxy;
public class TakeoutProxy {
// 为饭店生成一个代理
public static Restaurant getRestaurant(Object obj) {
return (Restaurant) Proxy.newProxyInstance(
obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),
new Takeout(obj)
);
}
}
public class App {
public static void main(String[] args) {
// KFC 开店
Restaurant kfc = new KFC();
// // KFC 入驻 外卖平台
Restaurant takeout = TakeoutProxy.getRestaurant(kfc);
// 客户在外卖平台点餐
takeout.food("汉堡");
}
}
控制台输出
点餐 : 汉堡
汉堡 ( KFC )
送餐
静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类
点餐和送餐本质上是,下订单与配送,所以动态代理不仅仅能够处理饭店,还能够处理花店、超市、等等业务。
乍一看好像两种方法没啥区别,但是静态代理中,把目标对象固定了(只能是Restaurant)
// 让饭店做饭
this.restaurant.food(name);