模式概述:
让你能够提供对象的替代品或其占位符。 代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。
使用场景:
1.延迟初始化 (虚拟代理)。 如果你有一个偶尔使用的重量级服务对象, 一直保持该对象运行会消耗系统资源时, 可使用代理模式。
2.访问控制 (保护代理)。 如果你只希望特定客户端使用服务对象, 这里的对象可以是操作系统中非常重要的部分, 而客户端则是各种已启动的程序 (包括恶意程序), 此时可使用代理模式。
3.本地执行远程服务 (远程代理)。 适用于服务对象位于远程服务器上的情形。
4.记录日志请求 (日志记录代理)。 适用于当你需要保存对于服务对象的请求历史记录时。 代理可以在向服务传递请求前进行记录。
5. 智能引用。 可在没有客户端使用某个重量级对象时立即销毁该对象。
代码样例(静态代理):
代码背景:在不改变源码的情况下,分析相关业务接口的耗时情况(接口调用前、后记录时间)并对接口的调用记录日志。代理与装饰者模式非常相像。
/**
* 被代理对象接口
*/
public interface OrderService {
Map<String,String> getOrderInfoByOrderId(long orderId);
}
/**
* 被代理对象接口实现
*/
public class OrderServiceImpl implements OrderService{
@Override
public Map<String, String> getOrderInfoByOrderId(long orderId) {
System.out.println("被代理对象执行方法,orderId="+orderId);
Map<String,String> map = new HashMap<>();
map.put("orderId",String.valueOf(orderId));
map.put("orderName","灰机");
return map;
}
}
/**
* 代理--时间
*/
public class OrderServiceTimeProxy implements OrderService{
private OrderService orderService;
public OrderServiceTimeProxy(OrderService orderService) {
this.orderService = orderService;
}
@Override
public Map<String, String> getOrderInfoByOrderId(long orderId) {
Map<String,String> map = null;
System.out.println("时间代理----before--time:"+System.currentTimeMillis());
map = orderService.getOrderInfoByOrderId(orderId);
System.out.println("时间代理----after--time:"+System.currentTimeMillis());
return map;
}
}
/**
* 代理--日志
*/
public class OrderServiceLogProxy implements OrderService{
private OrderService orderService;
public OrderServiceLogProxy(OrderService orderService) {
this.orderService = orderService;
}
@Override
public Map<String, String> getOrderInfoByOrderId(long orderId) {
Map<String,String> map = null;
System.out.println("日志代理----before");
map = orderService.getOrderInfoByOrderId(orderId);
System.out.println("日志代理----after");
return map;
}
}
/**
* 代理测试
*/
public class TestProxy {
public static void main(String[] args) {
//静态代理
System.out.println("静态代理:");
OrderService orderService = new OrderServiceLogProxy(new OrderServiceTimeProxy(new OrderServiceImpl()));
orderService.getOrderInfoByOrderId(1001l);
// System.out.println();
// System.out.println();
// System.out.println();
// System.out.println("动态代理:");
// //动态代理
// OrderService orderService2 = new OrderServiceImpl();
// OrderService orderServiceDynamicProxy = (OrderService) DynamicProxyFactory.createProxy(orderService2);
// Map<String,String> map = orderServiceDynamicProxy.getOrderInfoByOrderId(1002l);
// System.out.println("接口返回信息:"+map.toString());
}
}
测试结果:
此案例中是对orderService接口的getOrderInfoByOrderId()方法进行了代理;如果想要对orderService接口中的其他方法也设置代理,甚至对其他接口也设置日志和时间的代理,那么就需要增加很多代理类,因为静态代理类中代理对象和代理对象的方法都是已知固定的;只能代理指定的接口。因此就有了对应的动态代理,动态代理是动态生成代理类,而不是事先编码好代理类;动态代理和静态代理都是为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息;静态代理是事先编码好代理类,动态代理是程序运行时,动态生成代理类load到内存中。
JDK中的动态代理,生成代理对象需要传入3个参数,第一个是被代理对象的类加载器,第二个参数是被代理对象实现的接口,第三个参数InvocationHandler,可以理解是代理的实现逻辑。动态代理的生成是在程序运行时进行动态生成的,是操作字节码生成动态代理load到内存中的,生成的代理类会包含第二个参数中的接口数组的所有方法,方法内部的逻辑是调用第三个参数InvocationHandler的invoke方法;InvocationHandler的invoke方法通过反射调用到被代理对象的方法。注意JDK的代理的对象必须是实现接口 类;以下是JDK的代理的代码样例:
/**
* 动态代理工厂--日志
*/
public class DynamicProxyFactory {
public static <T> Object createProxy(T t){
return Proxy.newProxyInstance(t.getClass().getClassLoader(),t.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object o;
System.out.println("动态代理--日志--before");
o = method.invoke(t,args);
System.out.println("动态代理--日志--after");
return o;
}
});
}
}
/**
* 代理测试
*/
public class TestProxy {
public static void main(String[] args) {
//静态代理
// System.out.println("静态代理:");
// OrderService orderService = new OrderServiceLogProxy(new OrderServiceTimeProxy(new OrderServiceImpl()));
// orderService.getOrderInfoByOrderId(1001l);
// System.out.println();
// System.out.println();
// System.out.println();
// System.out.println("动态代理:");
//动态代理
OrderService orderService2 = new OrderServiceImpl();
OrderService orderServiceDynamicProxy = (OrderService) DynamicProxyFactory.createProxy(orderService2);
Map<String,String> map = orderServiceDynamicProxy.getOrderInfoByOrderId(1002l);
System.out.println("接口返回信息:"+map.toString());
}
}
测试结果: