使用代理模式切换数据源
学了代理模式后,小伙伴们可能还是不知道如何将代理模式应用到实际业务场景中,下面来看一个实际的业务场景。
在分布式业务场景中,通常会对数据库进行分库分表,这样使用 Java 操作时就可能需要配置多个数据源,可以通过设置数据源路由来动态切换数据源。本节分别使用静态代理和动态代理来切换数据源。
首先创建 Order 订单类。
创建 DynamicDataSourceEntry 类,使用 ThreadLocal 的单例实现。
动态代理和静态代理的基本思路是一致的,只不过动态代理的功能更强大,随着业务的扩展,适应性更强。
创建动态代理的类 OrderServiceDynamicProxy,代码如下:
在分布式业务场景中,通常会对数据库进行分库分表,这样使用 Java 操作时就可能需要配置多个数据源,可以通过设置数据源路由来动态切换数据源。本节分别使用静态代理和动态代理来切换数据源。
首先创建 Order 订单类。
public class Order { private Object orderInfo; private Long createTime; private String id; public Object getOrderInfo() { return orderInfo; } public void setOrderInfo(Object orderInfo) { this.orderInfo = orderInfo; } public Long getCreateTime() { return createTime; } public void setCreateTime(Long createTime) { this.createTime = createTime; } public String getId() { return id; } public void setId(String id) { this.id = id; } }创建 OrderDao 持久层操作类。
public class OrderDao { public int insert(Order order) { System.out.println("OrderDao创建Order成功"); return 1; } }创建 IOrderService 接口。
public interface IOrderService { int createOrder(Order order); }创建 OrderService 实现类。
public class OrderService implements IOrderService { private OrderDao orderDao; public OrderService() { //如果使用Spring,这里应该是自动注入的 //为了使用方便,我们在构造方法中直接将orderDao初始化 orderDao = new OrderDao(); } @Override public int createOrder(Order order) { System.out.println("OrderService调用orderDao创建订单"); return orderDao.insert(order); } }
使用静态代理切换数据源
以下主要实现根据订单创建时间自动按年来进行分库的功能。根据开闭原则,通过代理对象来完成数据源路由对象的创建。创建 DynamicDataSourceEntry 类,使用 ThreadLocal 的单例实现。
//动态切换数据源 public class DynamicDataSourceEntry { //默认数据源 public final static String DEFAULT_SOURCE = null; private final static ThreadLocal<String> local = new ThreadLocal<String>(); 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); } }创建切换数据源的代理类 OrderServiceStaticProxy。
import java.text.SimpleDateFormat; import java.util.Date; public class OrderServiceStaticProxy implements IOrderService { private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy"); private IOrderService orderService; public OrderServiceStaticProxy(IOrderService orderService) { this.orderService = orderService; } public int createOrder(Order order) { before(); Long time = order.getCreateTime(); Integer dbRouter = Integer.valueOf(yearFormat.format(new Date(time))); System.out.println("静态代理类自动分配到【DB_" + dbRouter + "】数据源处理数据"); DynamicDataSourceEntry.set(dbRouter); orderService.createOrder(order); after(); return 0; } public void before() { System.out.println("代理模式之前的方法"); } public void after() { System.out.println("代理模式之后的方法"); } }DbRouteProxyTest 类测试代码如下。
public class DbRouteProxyTest { public static void main(String[] args) { try { Order order = new Order(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd"); Date date = sdf.parse("2018/01/01"); order.setCreateTime(date.getTime()); IOrderService orderService = new OrderServiceStaticProxy(new OrderService()); orderService.createOrder(order); } catch (ParseException e) { e.printStackTrace(); } } }运行结果如下所示:
代理模式之前的方法
静态代理类自动分配到【DB_2018】数据源处理数据
OrderService调用orderDao创建订单
OrderDao创建Order成功
代理模式之后的方法
动态代理和静态代理的基本思路是一致的,只不过动态代理的功能更强大,随着业务的扩展,适应性更强。
使用动态代理实现无感知切换数据源
在学习了上面的案例后,下面我们使用动态代理实现无感知切换数据源,可以加深小伙伴们对动态代理的印象。创建动态代理的类 OrderServiceDynamicProxy,代码如下:
public class OrderServiceDynamicProxy implements InvocationHandler { private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy"); private Object target; @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(args[0]); Object object = method.invoke(target, args); after(); return object; } private void before(Object target) { System.out.println("代理模式之前的方法"); Long time = null; try { time = (Long) target.getClass().getMethod("getCreatTime").invoke(target); } catch (Exception e) { e.printStackTrace(); } Integer dbRouter = Integer.valueOf(yearFormat.format(new Date(time))); System.out.println("静态代理类自动分配到【DB_" + dbRouter + "】数据源处理数据"); DynamicDataSourceEntry.set(dbRouter); } private void after() { System.out.println("代理模式之后的方法"); } }客户端测试代码如下:
public static void main(String[] args) { try { Order order = new Order(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd"); Date date = sdf.parse("2018/01/01"); order.setCreateTime(date.getTime()); IOrderService orderService = new OrderServiceStaticProxy(new OrderService()); orderService.createOrder(order); } catch (ParseException e) { e.printStackTrace(); } }运行结果如下:
代理模式之前的方法
静态代理类自动分配到【DB_2018】数据源处理数据
OrderService调用orderDao创建订单
OderDao创建Order成功
代理模式之后的方法
需要注意的是,这里有一个比较重要的约定,即(实体类)必须实现 getCreateTime() 方法,因为路由规则是根据时间来运算的,可以通过接口规范达到约束的目的。
所有教程
- C语言入门
- C语言编译器
- C语言项目案例
- 数据结构
- C++
- STL
- C++11
- socket
- GCC
- GDB
- Makefile
- OpenCV
- Qt教程
- Unity 3D
- UE4
- 游戏引擎
- Python
- Python并发编程
- TensorFlow
- Django
- NumPy
- Linux
- Shell
- Java教程
- 设计模式
- Java Swing
- Servlet
- JSP教程
- Struts2
- Maven
- Spring
- Spring MVC
- Spring Boot
- Spring Cloud
- Hibernate
- Mybatis
- MySQL教程
- MySQL函数
- NoSQL
- Redis
- MongoDB
- HBase
- Go语言
- C#
- MATLAB
- JavaScript
- Bootstrap
- HTML
- CSS教程
- PHP
- 汇编语言
- TCP/IP
- vi命令
- Android教程
- 区块链
- Docker
- 大数据
- 云计算