设计模式之不愿被代理的代理模式,到底是怎么被代理的?

简述

        代理模式,顾名思义,首先一定是有一个代理方,和一个被代理方的区别。而我们日常生活中,中介找房租房就是一个非常直观的代理案例,中介代理房东的房子展示给客户,客户通过中介就能够直接进行看房、选房等一系列操作,非常方便。

        如图所示,客户想看什么房子,直接找中介就行了。完事Java里面,代理模式下,我们要找什么对象,找代理类(目标对象)就行了。

适用场景

        代理模式很明显的是一个代理方可以代理多个目标对象,如同中介可以托管多个房东的房子,那么使用场景:

1.Spring AOP切面,Spring两大核心之一,面向切面编程奠基者。

2.事务@Transaction 注解,通过代理模式实现SQL的提交与回滚。

3.项目接口监控日志Log打印,最简单的一种项目日志打印方法,直接将Request与Response打印在日志文件中。当然输出源可以有多种:日志文件、输出文件通过Filebeat监听收集或者通过消息中间件Kafka存储在ES中(ELK日志收集)、数据库MySQL、缓存等等。

4.MyBatis的Mapper接口代理

5.全局异常捕获 GlobalException

6.LCN、seata等分布式事务框架 小伙伴最熟悉的应该就是@GlobalTransaction 注解了

PS:一个注解解决分布式事务问题!

7.分库分表框架shadingjdbc 代理数据源

8.自定义注解(反射+AOP)

9.RPC远程调用

        以上列举了一系列我们实际开发的项目中经常会用到的技术以及碰到的场景,这时候我们会发现,原来代理模式可以做这么多的事情。 代理模式:我真能干!

 特性与分类

优点:实现方法扩展或增强,提高代码复用性冗余性、扩展性、智能型,各层代码职责清晰

缺点:在客户端和访问对象中增加了代理对象,代理对象的生成是需要时间的,并且有些类型的代理可能造成请求的速度变慢。且代理模式需要额外的工作,有些代理模式实现十分复杂。

分类:代理模式分为JDK代理(接口代理)Cglib代理(类代理),双方是取长补短,灵活分工。追求性能极致。

        当然,代理模式亦可动静自如,分为静态代理动态代理,静态代理编写一个代理类代理目标类,而动态代理可以理解为一次编译,N次代理

下面我们对于种种代理情况进行编码分析。

源码地址

        代理模式相关源码码云地址:https://gitee.com/yiang-hz/blog

路径:blog -> design -> proxy

静态代理

        静态代理缺点非常明显,每出现一个目标对象就需要定义一个代理对象,非常繁琐。

接口代理

        接口代理,一个类可以实现多个接口,当然如果没有实现接口则无法被JDK的这种形式代理,则需要使用类代理。

 步骤1:创建一个接口

public interface OrderService {

    String addOrder(String id, String name);
}

步骤2:创建该接口的实现类

public class OrderImpl implements OrderService {

    @Override
    public String addOrder(String id, String name) {
        System.out.println("addOrder-- id:" + id + " name:" + name);
        return "执行结束";
    }
}

步骤3:创建代理类

public class OrderListenerImpl implements OrderService {

    private final OrderImpl order;

    public OrderListenerImpl(OrderImpl order) {
        this.order = order;
    }

    @Override
    public String addOrder(String id, String name) {
        System.out.println("OrderService... 接口代理开始");
        return order.addOrder(id, name);
    }

}

 步骤4:

public class TestProxy {

    public static void main(String[] args) {
        inFaceProxy();
    }

    /**
     * 静态代理,通过接口实现 (JavaJDK动态代理的实现原理)
     */
    private static void inFaceProxy() {
        OrderListenerImpl orderListenerImpl = new OrderListenerImpl(new OrderImpl());
        orderListenerImpl.addOrder("1短袜", "小米18");
    }
    
}

当被请求或者被调用时,使用代理类对象OrderListenerImpl来代理 OrderImpl类的对象调用

执行输出:

OrderListenerImpl... 接口代理开始
addOrder-- id:1短袜 name:小米18

类代理

        类代理,一个类只能继承一个类,默认会继承Object类。

步骤1:编写代理类,继承前面写到的的OrderImpl实现类

public class OrderClassListenerImpl extends OrderImpl {

    @Override
    public String addOrder(String id, String name) {
        System.out.println("OrderClassListenerImpl... 类代理开始");
        return super.addOrder(id, name);
    }
}

 步骤2:测试类添加该方法,main方法添加对其调用进行测试

/**
 * 静态代理,通过类实现 (Cglib代理的实现原理)
 * 缺点:因为是继承实现,Java不能多继承
 */
private static void inClassProxy() {
    OrderClassListenerImpl orderClassListenerImpl = new OrderClassListenerImpl();
    orderClassListenerImpl.addOrder("1", "小米18");
}

 当被请求或者被调用时,使用代理类对象OrderListenerImpl获取OrderImpl实现

执行输出:

OrderClassListenerImpl... 类代理开始
addOrder-- id:1 name:小米18

        静态代理我们分析完成之后,继续分析动态代理,在动态代理我们直接就演变成了项目中的JDK代理实现以及Cglib代理实现。

 动态代理

        动态代理完美解决了静态代理编码繁琐的缺点,我们依赖的jar就有很多对代理进行了封装,如最熟悉的Spring框架,然后是Java开发核心的JDK,再者Hutool工具类框架等等。

JDK动态代理

        JDK动态代理实现原理是通过反射去执行方法,再走回调拦截。根据类的接口生成回调类。

实现思路:

1.需要拼接Java的源代码文件,JDK默认生成的文件命名格式为  $Proxy*.java 其中*是自增长数值

2.将拼接好的Java源代码文件进行编译,编译成 $Proxy*.class文件

3.使用类加载器将class文件加载到内存中

4.使用Java反射机制,赋值并且调用目标方法

 具体实现:

由于篇幅问题,指定在码云的项目中了,根据文章头部的代码地址拉取查看即可。

Cglib代理

        Cglib代理实现原理则是通过类代理,基于字节码技术(ASM)。并且提供FastClass索引机制提升效率,调用速度是远快于JDK动态代理的。但是会生成相关的索引文件,生成效率要低于JDK动态代理。

实现思路:

1.直接采用ASM实现生成.class文件

2.采用类加载器读取.class文件至内存中

3.采用FastClass机制调用目标方法(索引机制)

 具体实现:

由于篇幅问题,指定在码云的项目中了,根据文章头部的代码地址拉取查看即可。

总结

        总体来说,JDK动态代理,在创建类的过程中是较快的,但是生成类之后通过反射调用速度会慢很多,且必须实现接口才能应用该代理。而Cglib在生成的时候会较慢,固一般采用缓存机制来优化该问题。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值