《Java 设计模式精讲》笔记——第16章 代理模式

声明:

本博客是本人在学习《Java 设计模式精讲》后整理的笔记,旨在方便复习和回顾,并非用作商业用途。

本博客已标明出处,如有侵权请告知,马上删除。

1. 代理模式讲解

  • 定义:为其他对象提供一种代理,以控制对这个对象的访问

  • 代理对象在客户端和目标对象之间起到中介的作用

  • 类型:结构型

  • 适用场景

    • 保护目标对象
    • 增强目标对象
  • 优点

    • 代理模式能将代理对象与真实被调用的目标对象分离
    • 一定程度上降低了系统的耦合度,扩展性好
    • 保护目标对象
    • 增强目标对象
  • 缺点

    • 代理模式会造成系统设计中类的数目增加
    • 在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢
    • 增加系统的复杂度
  • 扩展

    • 静态代理:在代码中显示指定的代理
    • 动态代理:动态代理无法代理类,但是可以代理接口,所以被代理类最少实现一个接口,并且接口中没有声明的方法不能被代理。动态代理用到的代理类是在程序调用到代理类对象时,才由 JVM 真正创建的。
    • CGlib 代理:CGlib 是可以代理类的,它是针对类实现进行代理。如果我们代理一个类,CGlib 会生成被代理类的子类,并覆盖其中的方法。在使用 CGLib 代理的时候,因为要用到继承,还有重写,所以被代理类是不能用 final 修饰的类,被代理类中用 final 修饰的方法也不能被代理。
  • Spring 代理选择

    • 当 Bean 有实现接口时,Spring 就会用 JDK 的动态代理
    • 当 Bean 没有实现接口时,Spring 使用 CGlib
    • 可以在 Spring 中配置,强制使用 CGlib
  • 相关设计模式

    • 代理模式和装饰者模式

      装饰者模式是为对象加上行为,而代理模式是控制访问,代理模式更加注重通过设置代理人的方式,来增强目标对象。

    • 代理模式和适配器模式

      适配器模式主要改变所考虑对象的接口,而代理模式是不能改变被代理类的接口的。

2. 静态代理 Coding

下面引入一个根据用户 Id 进行分库的业务场景

  1. 创建订单类

    public class Order {
        private Object orderInfo;
        private Integer userId;
    
        public Object getOrderInfo() {
            return orderInfo;
        }
    
        public void setOrderInfo(Object orderInfo) {
            this.orderInfo = orderInfo;
        }
    
        public Integer getUserId() {
            return userId;
        }
    
        public void setUserId(Integer userId) {
            this.userId = userId;
        }
    }
    
  2. 创建 Dao 层接口和实现类

    public interface IOrderDao {
        int insert(Order order);
    }
    
    public class OrderDaoImpl implements IOrderDao {
        @Override
        public int insert(Order order) {
            System.out.println("Dao层添加order成功");
            return 1;
        }
    }
    
  3. 创建 Service 层接口和实现类

    public interface IOrderService {
        int saveOrder(Order order);
    }
    
    public class OrderServiceImpl implements IOrderService {
    
        private IOrderDao iOrderDao;
    
        @Override
        public int saveOrder(Order order) {
            iOrderDao = new OrderDaoImpl();
            System.out.println("Service调用Dao层添加Order层");
            return iOrderDao.insert(order);
        }
    }
    
  4. 创建静态代理类,对 OrderService 中的 saveOrder 方法进行增强,实现分库功能

    package princinple.structural.proxy.staticproxy;
    
    import princinple.structural.proxy.IOrderService;
    import princinple.structural.proxy.Order;
    import princinple.structural.proxy.OrderServiceImpl;
    
    public class OrderServiceStaticProxy {
        private IOrderService iOrderService;
    
        public int saveOrder(Order order) {
            beforeMethod(order);
            int result = iOrderService.saveOrder(order);
            afterMethod();
            return result;
        }
    
        private void beforeMethod(Order order) {
            System.out.println("静态代理 before code");
            iOrderService = new OrderServiceImpl();
            int userId = order.getUserId();
            // 实现分库的功能
            int dbRouter = userId % 2;
            System.out.println("静态代理分配到【db" + dbRouter + "】处理数据");
        }
    
        private void afterMethod() {
            System.out.println("静态代理 after code");
        }
    }
    
    
  5. 测试类

    public class Test {
        public static void main(String[] args) {
            Order order = new Order();
            order.setUserId(1);
    
            OrderServiceStaticProxy orderServiceStaticProxy = new OrderServiceStaticProxy();
            orderServiceStaticProxy.saveOrder(order);
        }
    }
    

    运行结果:

    静态代理 before code
    静态代理分配到【db1】处理数据
    Service调用Dao层添加Order层
    Dao层添加order成功
    静态代理 after code
    

    将 userId 改为 2 后,运行结果:

    静态代理 before code
    静态代理分配到【db0】处理数据
    Service调用Dao层添加Order层
    Dao层添加order成功
    静态代理 after code
    

现在类图如下所示:

在这里插入图片描述

3. 动态代理 Coding

还是上面的业务场景,我们使用动态代理实现。

  1. 创建一个动态代理类

    public class OrderServiceDynamicProxy implements InvocationHandler {
        private Object target;
    
        public OrderServiceDynamicProxy(Object target) {
            this.target = target;
        }
    
        public Object bind() {
            Class cls = target.getClass();
            return Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), this);
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object argObject = args[0];
            beforeMethod(argObject);
            Object object = method.invoke(target, args);
            afterMethod();
            return object;
        }
    
        public void beforeMethod(Object obj) {
            int userId = 0;
            System.out.println("动态代理before code");
            if (obj instanceof Order) {
                Order order = (Order) obj;
                userId = order.getUserId();
            }
            int dbRouter = userId % 2;
            System.out.println("动态代理分配到【db" + dbRouter + "】处理数据");
        }
    
        public void afterMethod() {
            System.out.println("动态代理after code");
        }
    }
    
  2. 测试类

    public class Test {
        public static void main(String[] args) {
            Order order = new Order();
            order.setUserId(1);
    
            IOrderService orderService = (IOrderService) new OrderServiceDynamicProxy(new OrderServiceImpl()).bind();
            orderService.saveOrder(order);
        }
    }
    

    运行结果:

    动态代理before code
    动态代理分配到【db1】处理数据
    Service调用Dao层添加Order层
    Dao层添加order成功
    动态代理after code
    

    将 userId 改为 2 后,运行结果:

    动态代理before code
    动态代理分配到【db0】处理数据
    Service调用Dao层添加Order层
    Dao层添加order成功
    动态代理after code
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bm1998

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值