声明:
本文只为方便我个人查阅和理解,详细的分析以及源代码请移步 原作者的博客http://chjavach.iteye.com/
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/*
* 下面的代码关注两种代理:
* 1.虚代理
* 2.保护代理
*/
/*
* 订单类接口
* id 订单id
* name 订单名称
*/
interface IOrder {
void setName(String name);
void setID(String id);
String getName();
String getID();
}
class Order implements IOrder {
private String id;
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getID() {
return this.id;
}
public void setID(String id) {
this.id = id;
}
}
class OrderProxy implements IOrder {
private Order order;
private boolean nameLoaded;
public OrderProxy(Order order) {
this.order = order;
this.nameLoaded = false;
}
/*
* 1、虚代理
* a. 客户查询订单信息时,只查询客户要求的信息:订单ID(假设这个查询比较快)
* b. 只有当用户要求更详细的信息(假设只是一个订单名称)时,才再次到数据库查询
* 这样做的原因是,通常查询“详细信息”时,会花费更多的时间和空间,有点“延迟加载”的感觉
*/
private void loadName() {
if (!nameLoaded) {
System.out.println("request name but no name, fetch it from database:");
String name = "aName"; //模拟数据库操作
order.setName(name);
this.nameLoaded = true;
}
}
public String getID() {
return order.getID();
}
/*
* 2.保护代理,可以在这里加上条件,符合则调用真实的order的方法。例如只有订单创建者能修改订单名称
*/
public void setName(String name) {
boolean canChangeName = true; //根据实际设置,例如canChangeName = (name.equals(order.getCreateUser()));
if (canChangeName) {
order.setName(name);
System.out.println("Request accepted. Name changed.");
}
}
public void setID(String id) {
order.setID(id);
}
public String getName() {
if (!nameLoaded) {
this.loadName();
}
return this.order.getName();
}
}
public class ProxyPattern {
public static void main(String[] args) {
//1.静态代理
ProxyPattern p = new ProxyPattern();
IOrder order = p.getOrderFromDatabase(); //模拟从数据库查询Order
System.out.println("id:" + order.getID());
//测试虚代理。如果“客户”没有调用getName,那“到数据库查询name”这个操作就可以不执行了
System.out.println("name:" + order.getName());
//测试保护代理
order.setName("newName");
System.out.println(order.getName());
//2.动态代理
//准备测试数据
Order order2 = new Order();
order2.setID("originalID");
order2.setName("originalName");
DynamicProxy dProxy = new DynamicProxy();
IOrder iOrder = dProxy.getProxyOrder(order2);
iOrder.setID("anotherID");
iOrder.setName("anotherName");
System.out.println(iOrder.getID() + ","+ iOrder.getName());
}
//模拟从数据库查询Order
public IOrder getOrderFromDatabase() {
//这里创建的是代理,而非真实的Order
//IOrder order = new Order();
OrderProxy proxy = new OrderProxy(new Order());
System.out.println("simple info. Only fetch order's id from database:");
String id = "aID"; //模拟从数据库取得ID
proxy.setID(id);
return proxy;
}
}
//=======================以上实现代理的方式称为静态代理===========================================
/**
* JDK里面的InvocationHandler来实现代理,称为动态代理
* 静态代理实现的时候,在Subject接口(IOrder)上定义很多的方法,代理类里面自然也要实现很多方法;
* 而动态代理实现的时候,虽然Subject接口上定义了很多方法,但是动态代理类始终只有一个invoke方法。
* 这样当Subject接口发生变化的时候,动态代理的接口就不需要跟着变化了。
* Java的动态代理目前只能代理接口,基本的实现是依靠Java的反射机制和动态生成class的技术,来动态生成被代理的接口的实现对象。
*/
class DynamicProxy implements InvocationHandler{
private IOrder order;
public IOrder getProxyOrder(Order order) {
this.order = order;
//把真正的订单对象和动态代理关联起来
IOrder iOrder = (IOrder) Proxy.newProxyInstance(order.getClass().getClassLoader(),
order.getClass().getInterfaces(),
this);
return iOrder;
}
/*
* 这个方法并不需要我们显式的调用
* 我们在getProxyOrder后获得一个IOrder,当IOrder调用某方法时,会触发invoke()这个方法
* 也就是所有方法调用都被invoke()拦截了
* 这里面的第一个参数obj是什么呢?看API的说法是“proxy instance”。一般不用到这一个参数
* passing the proxy instance,a java.lang.reflect.Method object identifying the method that was invoked,
* and an array of type Object containing the arguments.
*/
public Object invoke(Object obj, Method method, Object[] aobj) throws Throwable {
Object result = null;
//假设不允许执行setID操作
if (method.getName().equalsIgnoreCase("setID")) {
System.out.println(method.getName() + ":Access denied.");
} else {
//result = method.invoke(obj, aobj); //不能这样写
result = method.invoke(order, aobj); //调用真实的order的method
}
//System.out.println(String.format("Invoke '%s' ; result = %s", method.getName(), result));
return result;
}
}