使用代理模式切換資料來源

2020-10-13 01:01:19
學了代理模式後,小夥伴們可能還是不知道如何將代理模式應用到實際業務場景中,下面來看一個實際的業務場景。

在分散式業務場景中,通常會對資料庫進行分庫分表,這樣使用 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成功
代理模式之後的方法

執行結果符合預期,類圖如下所示: