GOF23 代理模式

动态代理

  • 动态代理和静态代理角色一样
  • 动态代理的代理类是动态生成的,不是我们直接写好的
  • 动态代理分为两大类: 基于接口的动态代理,基于类的动态代理
    • 基于接口 --**JDK** 动态代理
    • 基于类: cglib
    • java字节码实现: javasist

需要了解两个类

  • Proxy 代理类

image-20210307200309908

  • InvocationHandler

image-20210307195738770

  • 代理类具有以下属性:

    • 代理类的不合格名称未指定。 然而,应该为代理类保留以字符串"$Proxy"开头的类名空间。
    • 指定代理类的包和模块指定为below
    • 代理类是最终的和非抽象的
    • 代理类扩展为java.lang.reflect.Proxy
    • 代理类完全按照相同的顺序实现其创建时指定的接口。 调用getInterfaces其上Class对象将返回包含接口的同一列表的阵列(在其创建时指定的顺序),调用getMethods其上Class对象将返回的数组方法对象,其中包括所有的在这些接口的方法,以及调用getMethod将会在代理接口中找到可以预期的方法。
    • 代理类的ProtectionDomain与引导类加载器加载的系统类(例如java.lang.Object ,因为代理类的代码是由可信系统代码生成的。 这个保护域通常会被授予java.security.AllPermission
    • 可以使用Proxy.isProxyClass方法来确定给定的类是否是代理类。
  • 代理实例具有以下属性:

    • 给定代理实例proxy和由其代理类实现的接口Foo ,以下表达式将返回true:

      proxy instanceof Foo

      并且以下的投射操作将会成功(而不是抛出一个ClassCastException ):

      (Foo) proxy

    • 每个代理实例都有一个关联的调用处理程序,它被传递给它的构造函数。 静态Proxy.getInvocationHandler方法将返回与作为其参数传递的代理实例关联的调用处理程序。

    • 代理实例上的接口方法调用将被编码并分派到调用处理程序的invoke方法,如该方法的文档所述。

    • 的调用hashCodeequals ,或toString中声明的方法java.lang.Object上的代理实例将被编码并分派到调用处理程序的invoke中相同的方式方法,接口方法调用进行编码和调度,如上所述。 传递给invoke方法对象的声明类将为java.lang.Object 。 继承自java.lang.Object的代理实例的其他公共方法不会被代理类覆盖,因此这些方法的调用与java.lang.Object实例java.lang.Object

基于接口的动态代理

  • 出租接口Rent
public interface Rent {
    public void rent();
}
  • 房东(代理类)
public class Host implements Rent {
    @Override
    public void rent() {
        System.out.println("房东出租房子");
    }
}
  • 代理实例 实现接口 InvocationHandler
public class ProxyInvocationHandler implements InvocationHandler {
    private Rent rent;

    public void setRent(Rent rent) {
        this.rent = rent;
    }

    /**
     * 获取代理对象 rent的实现类
     * @return
     */
    public Rent getProxy(){
        return (Rent) Proxy.newProxyInstance(rent.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //执行接口
        seeHouse();
        Object invoke = method.invoke(rent, args);
        tseHouse();
        pay();
        return invoke ;
    }

    public void seeHouse(){
        System.out.println("看房子!!");
    }
    public void pay(){
        System.out.println("支付");
    }
    public void tseHouse(){
        System.out.println("看房子!!!");
    }
}
  • 用户 客户端对象
public class Client {
    public static void main(String[] args) {
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        pih.setRent(new Host());
        Rent proxy = pih.getProxy();
        proxy.rent();
    }
}

代理模式在实际开发中的应用

在使用分布式应用的时候可能会用到分库分表的操作 使用java操作时可能需要配置多个数据源,我们可以通过数据源路由来动态切换数据源。首先创建Order订单类.

静态代理

  • 订单类
public class Order {
    private Object orderInfo;
    private Long createTime;
    private String id;

    @Override
    public String toString() {
        return "Order{" +
                "orderInfo=" + orderInfo +
                ", createTime=" + createTime +
                ", id='" + 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;
    }
}
  • dao
public class OrderDao {
    public int insert (Order order){
        System.out.println("OrderDao创建order成功");
        return 1;
    }
}
  • service层接口
public interface IOrderService {
    int createOrder(Order order);
}
  • service实现类
public class OrderService implements IOrderService {
    private OrderDao orderDao;
    public OrderService(){
        orderDao=new OrderDao();
    }
    @Override
    public int createOrder(Order order) {
        System.out.println("service调用orderDao insert方法创建订单成功!!");
        return orderDao.insert(order);
    }
}
  • 数据源切换类
/**
 * @program: DesignPattern
 * @description: 动态切换数据源
 * @author: ZGrey
 * @create: 2021-03-29 22:01
 **/
public class DynamicDataSourceEntry {

    public final static  String DEFAULT_SOURCE= null;
    //使用threadLocal进行数据源切换
    private final static 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);
    }

}
  • 动态切换数据源代理类
    • 使用静态代理主要完成的任务就是:根据订单创建时间自动按年份进行分表。根据开闭原则,我们修改原来已经写好的代码逻辑,通过代理来完成。创建数据源路由对象,使用ThreadLocal的单例实现DynamicDataSourceEntry
/**
 * @program: DesignPattern
 * @description: 动态切换数据源代理类
 * @author: ZGrey
 * @create: 2021-03-29 22:06
 **/
public class OrderServiceStaticProxy {
    private  SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");
    private IOrderService orderService;

    public OrderServiceStaticProxy(OrderService orderService){
        this.orderService=orderService;
    }
    public  int createOrder(Order order){
        before();
        Long createTime = order.getCreateTime();
        Integer dbRouter = Integer.valueOf(yearFormat.format(new Date(createTime)));
        System.out.println("静态代理类自动分配到【DB_"+dbRouter+"】数据源处理数据");
        DynamicDataSourceEntry.set(dbRouter);
        orderService.createOrder(order);
        after();
        return 0;
    }

    private  void before(){
        System.out.println("proxy before method.");
    }
    private void after(){
        System.out.println("proxy after method.");
    }
}
  • 测试类
public class Test02 {
    public static void main(String[] args) {
        try{
            Order order = new Order();
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd");
            Date parse = simpleDateFormat.parse("2019/02/01");
            order.setCreateTime(parse.getTime());
            OrderServiceStaticProxy orderServiceStaticProxy = new OrderServiceStaticProxy(new OrderService());
            orderServiceStaticProxy.createOrder(order);
        }catch (Exception e){
        }
    }
}
  • 执行结果

image-20210329231524710

动态代理

public class OrderServiceStaticProxy  implements InvocationHandler {
    private SimpleDateFormat yearFormat =  new SimpleDateFormat("yyyy");
    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 {
        //参数0为order对象的引用
        before(args[0]);
        Object invoke = method.invoke(target, args);
        after();
        return invoke;
    }

    public void before(Object target){
        try{
            System.out.println("Proxy before method  "+target);
            Long time = (Long) target.getClass().getMethod("getCreateTime").invoke(target);
            Integer dbRouter = Integer.valueOf(yearFormat.format(new Date(time)));
            System.out.println("静态代理自动分配到【DB_"+dbRouter+"】数据源处理数据");
            DynamicDataSourceEntry.set(dbRouter);

        }catch (Exception e){

        }
    }
    public void after(){
        System.out.println("Proxy after method!!");
    }
}
  • 测试
public class Test03 {
    public static void main(String[] args) throws ParseException {
        Order order = new Order();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd");
        Date parse = simpleDateFormat.parse("2019/12/02");
        order.setCreateTime(parse.getTime());
        IOrderService orderService = (IOrderService) new OrderServiceStaticProxy()
                .getInstance(new OrderService());
        orderService.createOrder(order);
    }
}
  • 结果

image-20210329231615228

从上面的代码可以看出,依然可以达到相同的效果,但是,使用动态代理实现之后,不仅能实现Order的数据源动态路由,还可以实现其他任何类的数据源路由,当然,有一个重要的约定就是必须实现getCreateTime(),因为路由规则是根据时间来运算。可以通过接口规范达到约束的目的。

posted @ 2021-03-29 23:22  錵開や落幕  阅读(62)  评论(0编辑  收藏  举报