一、代理模式的定义
代理模式是指为其他对象提供一种代理,以控制对这个对象的访问。代理对象在客户端和目标对象之前起到中介作用,代理模式属于结构型设计模式。使用代理模式主要有两个目的:一是保护目标对象,二是增强目标对象。
代理模式的类结构图如下所示:
Subject是顶层接口,RealSubject是真实对象(被代理对象),Proxy是代理对象,代理对象持有被代理对象的引用,客户端调用代理对象的方法,同时也调用被代理对象的方法,但是会在代理对象前后加一些处理代码。在代码中,一般代理会被理解为代码增强,实际上就是在原代码前后增加一些代码逻辑,而使调用者无感知。代理模式分为静态代理和动态代理。
二、静态代理
在分布式业务场景中 通常会对数据库进行分库分表,分库分表之后使用 Java 操作时就可能需要配置多个数据源,我们通过设置数据源路由来动态换数据源。先创建 Order 订单类:
package com.dh.proxy.staticproxy;
public class Order {
private String id;
private Long createTime;
private Object orderInfo;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Long getCreateTime() {
return createTime;
}
public void setCreateTime(Long createTime) {
this.createTime = createTime;
}
public Object getOrderInfo() {
return orderInfo;
}
public void setOrderInfo(Object orderInfo) {
this.orderInfo = orderInfo;
}
}
创建 OrderDao 持久层操作类:
package com.dh.proxy.staticproxy;
public class OrderDao {
public int insert(Order order) {
System.out.println("OrderDao 创建 Order 成功!");
return 1;
}
}
创建 IOrderService接口层:
package com.dh.proxy.staticproxy;
public interface IOrderService {
int createOrder(Order order);
}
创建 OrderServiceImpl 实现类:
package com.dh.proxy.staticproxy;
public class OrderServiceImpl implements IOrderService {
private OrderDao orderDao;
public OrderServiceImpl(OrderDao orderDao) {
// 如果使用 Spring 应该是自动注入的
// 为了使用方便,我们在构造方法中将 derDao 直接初始化
this.orderDao = orderDao;
}
@Override
public int createOrder(Order order) {
System.out.println("OrderServiceImpl 调用 OrderDao 创建订单");
return orderDao.insert(order);
}
}
接下来使用静态代理,主要完成的功能是:根据订单创建时间自动按年进行分库。根据开闭原则,我们不修改原来写好的代码逻辑,通过代理对象来完成。先创建数据源路由对象,使用 ThreadLocal 的单例实现 DynamicDataSourceEntry 类:
package com.dh.proxy.staticproxy;
public class DynamicDataSourceEntry {
private static final String DEFAULT_SOURCE = null;
private static final ThreadLocal<String> local = new ThreadLocal<>();
private DynamicDataSourceEntry() {
}
// 清空数据源
public static void clear() {
local.remove();
}
// 获取当前正在使用的数据源的名字
public static String get() {
return local.get();
}
// 还原数据源
public static void restore() {
local.set(DEFAULT_SOURCE);
}
// 设置已知名字的数据源
public static void set(String source) {
local.set(source);
}
// 根据年份动态设置数据源
public static void set(int year) {
local.set("DB_" + year);
}
}
创建切换数据源的代理类
package com.dh.proxy.staticproxy;
import java.text.SimpleDateFormat;
public class OrderServiceStaticProxy implements IOrderService {
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy");
private IOrderService iOrderService;
public OrderServiceStaticProxy(IOrderService iOrderService) {
this.iOrderService = iOrderService;
}
@Override
public int createOrder(Order order) {
before();
Long createTime = order.getCreateTime();
Integer year = Integer.valueOf(sdf.format(createTime));
DynamicDataSourceEntry.set(year);
System.out.println("静态代理自动分配到【" + DynamicDataSourceEntry.get() +"】数据源处理数据");
int result = this.iOrderService.createOrder(order);
after();
return result;
}
private void before() {
System.out.println("Proxy before method");
}
private void after() {
System.out.println("Proxy after method");
}
}
测试代码:
package com.dh.proxy.staticproxy;
import java.util.Date;
public class TestStatic {
public static void main(String[] args) {
Order order = new Order();
order.setCreateTime(new Date().getTime());
IOrderService orderService = new OrderServiceStaticProxy(new OrderServiceImpl());
orderService.createOrder(order);
}
}
三、动态代理
动态代理和静态代理的基本思路是一致的,只不过动态代理功能更加强大,随着业务的扩展适应性更强。
3.1 JDK实现方式
以数据源动态路由为例,创建动态代理的类OrderServiceDynamicProxy:
package com.dh.proxy.dynamicproxy;
import com.dh.proxy.staticproxy.DynamicDataSourceEntry;
import com.dh.proxy.staticproxy.IOrderService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.text.SimpleDateFormat;
public class OrderServiceDynamicProxy implements InvocationHandler {
private Object target;
public Object getInstance(Object target) {
this.target = target;
Class<?> clazz = target.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before(args[0]);
Object res = method.invoke(this.target, args);
after();
return res;
}
private void before(Object target) {
try {
System.out.println("Proxy before method");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy");
Method method = target.getClass().getMethod("getCreateTime");
Long time = (Long) method.invoke(target);
Integer year = Integer.valueOf(sdf.format(time));
DynamicDataSourceEntry.set(year);
System.out.println("动态代理自动分配到" + DynamicDataSourceEntry.get() + "数据源处理数据");
} catch (Exception e) {
e.printStackTrace();
}
}
private void after() {
System.out.println("Proxy after method");
}
}
测试代码如下:
package com.dh.proxy.dynamicproxy;
import com.dh.proxy.staticproxy.IOrderService;
import com.dh.proxy.staticproxy.Order;
import com.dh.proxy.staticproxy.OrderServiceImpl;
import java.util.Date;
public class TestDynamic {
public static void main(String[] args) {
OrderServiceDynamicProxy dynamicProxy = new OrderServiceDynamicProxy();
IOrderService orderService = (IOrderService) dynamicProxy.getInstance(new OrderServiceImpl());
Order order = new Order();
order.setCreateTime(new Date().getTime());
orderService.createOrder(order);
}
}
3.2 手写实现 JDK 动态代理
未完待续…