代理模式

2.代理模式

  • 代理模式(Proxy pattern) 指为其他对象提供一种代理,以控制对这个对象的访问,属于结构设计模式。
  • 在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端与目标对象之间起到中介的作用。

2.1 代理模式介绍

2.1.1 代理模式的应用场景

  • 婚姻中介,事务代理,日志坚挺。
  • 当不想直接引用某个对象或某个对象存在访问困难时,可以通过代理对象来间接访问。使用代理模式主要有两个目的:一、保护目标对象。二、增强目标对象。

2.1.2 代理模式的UML图

UML图.png

代理模式一般分三个角色

  1. 抽象角色(ISbject):抽象主题类的主要职责是生命真实主题和代理的共同接口方法,该类可以是接口,也可以是抽象类。
  2. 真实角色(RealSubject):也被称为被代理类,该类定义了代理所表示的真实对象,是负责执行系统的真正的逻辑业务对象。
  3. 代理角色(Proxy):代理类,其内部有RealSubject的引用,因此具备完全的对RealSubject的代理权,客户端调用代理对象的方法,也调用被代理对象的方法,但是会在代理对象前后增加一些逻辑代码。
  • 代理模式被分为静态代理和动态代理。代理一般可以理解位代码增强。

2.1.3 代理模式通用写法

public class Client {
    public static void main(String[] args) {
        Proxy proxy = new Proxy(new RealSubject());
        proxy.request();
    }

    interface ISbuject{
        void request();
    }

    //代理角色
    static class Proxy implements ISbuject{

        private ISbuject sbuject;

        public Proxy(ISbuject sbuject){
            this.sbuject = sbuject;
        }

        public void request() {
            before();
            sbuject.request();
            after();
        }

        private void after() {
            System.out.println("Proxy After");
        }

        private void before() {
            System.out.println("Proxy before");
        }
    }
    static class RealSubject implements ISbuject{

        public void request() {
            System.out.println("real subject");
        }
    }
}

QQ截图20201020151451.png

2.2 代理模式的实际问题解决

2.21 静态代理

一个代理对象对应一个被代理对象,不能代理其他被代理对象。

public interface IPerson {
    void find();

}

  • 被代理对象
public class Son implements IPerson {
    public void find() {
        System.out.println("符合要求");
    }
}
  • 代理对象
public class Monther implements IPerson {

    private Son son;
    public Monther(Son son){
       this.son = son;
    }

    public void find() {
        before();
        son.find();
        after();
    }

    private void after() {
        System.out.println("开始相亲");
    }

    private void before() {
        System.out.println("家长催婚,并寻找相亲对象");
    }
}

  • 结果:

静态代理.png

2.2.2 动态代理

静态代理太过于浪费成本,因此采用动态代理,只要实现了IPerson都可以进行代理,面前有JDK自带的代理以及CGLib代理。

public class JdkMeipo implements InvocationHandler {

    //获取字节码文件
    private IPerson instance;
    public IPerson getInstance(IPerson instance){
        this.instance = instance;
        Class<?> clazz = instance.getClass();
        return (IPerson) Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(this.instance,args);
        after();
        return null;
    }

    private void after() {
        System.out.println("找到对象,开始相亲");
    }

    private void before() {
        System.out.println("媒婆收到需求,开始寻找对象");
    }
}
public class Zhangsan  implements IPerson{
    public void find() {
        System.out.println("符合张三需求");
    }

    //其他功能
}
  • 测试
    @Test
    public void test1(){
        JdkMeipo jdkMeipo = new JdkMeipo();
        IPerson zhangsan = jdkMeipo.getInstance(new Zhangsan());
        zhangsan.find();
    }
  • 结果

动态代理.png

2.2.3 三层架构种的静态代理

  • 在分布式业务场景种,通常对数据库进行分库分表,分库分表之后会使用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);
}

  • Order实现类
public class OrderService implements IOrderService{
    private OrderDao orderDao;
    public OrderService(){
        //Spring自动注入OrderDao
        orderDao = new OrderDao();
    }

    public int createOrder(Order order) {
        System.out.println("OrderService调用OrderDao创建订单");

        return orderDao.insert(order);
    }
}
  • 静态代理,根据订单创建的时间自动由年进行分库,根据开闭原则,修改原来的代码逻辑,通过代理对象来完成。创建数据源路由对象,使用ThreadLocal的单例实现DynamicDataSourceEntry类
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
public class OrderServiceStaticProxy implements IOrderService {
    private SimpleDateFormat sdf = new SimpleDateFormat("yyyy");

    private IOrderService orderService;
    public OrderServiceStaticProxy(IOrderService orderService){
        this.orderService = orderService;
    }


    private void after() {
        System.out.println("Proxy after method");
    }

    private void before() {
        System.out.println("Proxy before method");
    }

    public int createOrder(Order order) {
        before();
        Long time = order.getCreateTime();
        Integer dbRouter = Integer.valueOf(sdf.format(new Date(time)));
        System.out.println("静态代理类自动分配到【DB_"+dbRouter+"】数据源处理数据");
        DynamicDataSourceEntry.set(dbRouter);
        orderService.createOrder(order);
        after();
        return 0;
    }
}
  • 客户端测试代码
public class Test1 {
    @Test
    public void test(){
        try{
            Order order = new Order();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
            Date date = sdf.parse("2011/11/11");
            order.setCreateTime(date.getTime());

            IOrderService orderService = new OrderServiceStaticProxy(new OrderService());
            orderService.createOrder(order);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
  • 结果:

切换数据源.png

  • UML图

静态切换数据源.png

2.2.4 三层架构动态代理

public class OrderServiceDynamicProxy implements InvocationHandler {
    private SimpleDateFormat sdf = 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);
    }

    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 after() {
    }

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

        }

    }
}
  • 结果

动态切换数据源.png

  • 使用动态代理之后,不仅可以实现Order的数据源动态路由,还可以实现其他任何类的数据源路由,但是需要实现getCreateTime()方法,因为路由规则是按照时间来运算的。

//TODO JDK源码分析,CGLib源码分析

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值