[特殊字符]代理模式?代码界的贴心小管家!一文吃透

🔑代理模式?代码界的贴心小管家!一文吃透

一、生活里的代理,代码中的门道

✨宝子们,在生活中找人代取快递、请中介找房子,这些都是代理行为。在代码世界里,代理模式同样能帮我们解决不少问题。它就像一位贴心小管家,代替对象完成各种操作,隐藏背后的复杂细节!

先来看个简单的生活场景🌰:你想买周杰伦演唱会门票,但官网太难抢,这时黄牛(代理)出现了,他帮你买票,你只需给黄牛钱和个人信息,等着拿票就行。这里,黄牛就是代理,帮你完成购票操作,你不用操心复杂的抢票过程。

在代码里,代理模式也是类似原理。当我们不想或不能直接访问某个对象时,就可以找个代理对象帮忙。代理对象和真实对象实现相同接口,代理对象持有真实对象的引用,在调用真实对象方法前后,还能添加自己的逻辑,实现功能增强或控制访问。

二、代理模式 360° 剖析

2.1 模式定义与原理

代理模式,简单来说,就是为其他对象提供一种代理,以控制对这个对象的访问。就像你要访问某个对象,但不想直接访问,或者不能直接访问,这时就可以通过代理对象来间接访问目标对象。在代理对象中,我们可以在访问目标对象前后添加一些额外操作,比如日志记录、权限验证等。

从原理上讲,代理模式在访问对象和目标对象之间设置了一个代理层,所有对目标对象的访问都通过代理对象进行。代理对象和目标对象实现相同的接口,这样在使用时,客户端无需关心是在访问代理对象还是目标对象,就像调用同一个接口的不同实现一样。

为了更形象地理解,我们把它类比成租房场景。在租房时,租客往往不会直接联系房东,而是通过房屋中介来找房、看房 。中介就相当于代理对象,房东是目标对象,租客则是访问对象。中介会提前过滤筛选各种房源信息,还会帮你安排看房时间,这些额外操作就是代理模式中在访问目标对象之前执行的操作。

2.2 三大关键角色

代理模式中有三个关键角色,它们各自承担着不同的职责,共同构成了代理模式的核心结构:

抽象主题:它定义了真实主题和代理主题的共同接口,就像租房时约定的租房流程和沟通规范,不管是房东还是中介,都得遵循这些规范与租客交流。通过这个共同接口,客户端可以用统一的方式访问真实主题和代理主题,无需关心它们具体的实现细节。这样一来,在任何使用真实主题的地方,都可以无缝替换成代理主题,极大地提高了代码的灵活性和可维护性。

真实主题:这就是代理对象所代表的真实对象,在租房案例中,房东就是真实主题。房东拥有房子的实际出租权,负责处理租房的核心业务逻辑,比如确定租金、签订合同等。真实主题实现了抽象主题接口中定义的具体方法,是整个业务流程的实际执行者。

代理主题:代理主题持有对真实主题的引用,就像房屋中介手里掌握着房东的信息。在租客访问房东(真实主题)之前,中介(代理主题)会先进行一些操作,比如筛选合适的房源、安排看房时间等。在租客与房东达成租房协议后,中介还可能会收取中介费等后续操作。代理主题通过这种方式,在访问真实主题之前或之后执行一些额外操作,实现对真实主题的访问控制和功能增强。

三、Java 实战:代理模式初体验

了解了代理模式的概念和原理后,我们通过 Java 代码来具体实现代理模式,感受它在实际编程中的魅力。在 Java 中,代理模式主要有静态代理和动态代理两种实现方式 ,下面分别来看它们的实现。

3.1 静态代理

静态代理是在编译时就已经确定代理类和目标类的关系,代理类是通过手动编写代码来实现的。我们以租房为例,实现一个简单的静态代理。

首先,定义一个租房接口RentHouse

// 租房接口
public interface RentHouse {
    void rent();
}

然后,实现真实的房东类Landlord,它实现了RentHouse接口:

// 房东类,实现RentHouse接口
public class Landlord implements RentHouse {
    @Override
    public void rent() {
        System.out.println("房东出租房子");
    }
}

接着,创建代理类Agent,同样实现RentHouse接口,并持有房东类的引用:

// 中介代理类,实现RentHouse接口
public class Agent implements RentHouse {
    // 持有房东类的引用
    private Landlord landlord;

    public Agent(Landlord landlord) {
        this.landlord = landlord;
    }

    @Override
    public void rent() {
        System.out.println("中介带租客看房");
        // 调用房东的出租方法
        landlord.rent();
        System.out.println("中介收取中介费");
    }
}

最后,在测试类中使用代理:

public class Test {
    public static void main(String[] args) {
        // 创建房东对象
        Landlord landlord = new Landlord();
        // 创建中介代理对象,传入房东对象
        Agent agent = new Agent(landlord);
        // 通过代理对象调用租房方法
        agent.rent();
    }
}

在上述代码中,Agent类就是代理类,它持有Landlord类的引用。在rent方法中,代理类在调用房东的rent方法前后添加了自己的逻辑,如带租客看房和收取中介费。这就是静态代理的基本实现方式,通过代理类控制对目标类的访问,并在访问前后添加额外操作。

3.2 动态代理

动态代理是在运行时生成代理对象,而无需手动编写代理类。Java 中主要有 JDK 动态代理和 CGLIB 动态代理两种方式,下面分别介绍。

JDK 动态代理

JDK 动态代理主要使用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现。继续以租房为例,我们使用 JDK 动态代理实现相同的功能。

首先,还是定义租房接口RentHouse和房东类Landlord,代码与静态代理中的相同。

然后,创建一个实现InvocationHandler接口的处理器类RentHandler

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

// 实现InvocationHandler接口的处理器类
public class RentHandler implements InvocationHandler {
    // 目标对象
    private Object target;

    public RentHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("中介带租客看房(JDK动态代理)");
        // 调用目标对象的方法
        Object result = method.invoke(target, args);
        System.out.println("中介收取中介费(JDK动态代理)");
        return result;
    }
}

最后,在测试类中使用 JDK 动态代理创建代理对象并调用方法:

import java.lang.reflect.Proxy;

public class JDKProxyTest {
    public static void main(String[] args) {
        // 创建房东对象
        Landlord landlord = new Landlord();
        // 创建RentHandler对象,传入房东对象
        RentHandler rentHandler = new RentHandler(landlord);
        // 使用Proxy类的静态方法创建代理对象
        RentHouse proxy = (RentHouse) Proxy.newProxyInstance(
                landlord.getClass().getClassLoader(),
                landlord.getClass().getInterfaces(),
                rentHandler);
        // 通过代理对象调用租房方法
        proxy.rent();
    }
}

在上述代码中,RentHandler类实现了InvocationHandler接口,在invoke方法中定义了代理逻辑。通过Proxy.newProxyInstance方法创建代理对象,该方法接收三个参数:目标对象的类加载器、目标对象实现的接口数组以及InvocationHandler实现类的实例。这样就实现了 JDK 动态代理,在运行时动态生成代理对象并添加额外操作。

CGLIB 动态代理

CGLIB(Code Generation Library)是一个开源的第三方库,用于在 Java 运行时生成字节码并创建代理类。与 JDK 动态代理不同,CGLIB 代理可以代理普通类,即使它们没有实现任何接口 。我们同样以租房为例来展示 CGLIB 动态代理的实现。

首先,创建房东类Landlord,这次房东类不需要实现任何接口:

// 房东类
public class Landlord {
    public void rent() {
        System.out.println("房东出租房子");
    }
}

然后,引入 CGLIB 库(如果使用 Maven,可以在pom.xml文件中添加依赖):

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

接着,创建一个实现MethodInterceptor接口的代理类CglibAgent

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

// 实现MethodInterceptor接口的代理类
public class CglibAgent implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("中介带租客看房(CGLIB动态代理)");
        // 调用父类的方法
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("中介收取中介费(CGLIB动态代理)");
        return result;
    }
}

最后,在测试类中使用 CGLIB 动态代理创建代理对象并调用方法:

import net.sf.cglib.proxy.Enhancer;

public class CglibProxyTest {
    public static void main(String[] args) {
        // 创建Enhancer对象
        Enhancer enhancer = new Enhancer();
        // 设置父类为房东类
        enhancer.setSuperclass(Landlord.class);
        // 设置回调对象为CglibAgent
        enhancer.setCallback(new CglibAgent());
        // 创建代理对象
        Landlord proxy = (Landlord) enhancer.create();
        // 通过代理对象调用租房方法
        proxy.rent();
    }
}

在上述代码中,CglibAgent类实现了MethodInterceptor接口,在intercept方法中定义了代理逻辑。通过Enhancer类创建代理对象,设置父类为目标类Landlord,并设置回调对象为CglibAgent。这样就实现了 CGLIB 动态代理,即使目标类没有实现接口,也能在运行时动态生成代理类并添加额外操作。

四、代理模式应用场景大赏

代理模式在实际开发中应用广泛,下面通过几个常见场景来看看它的强大之处。

4.1 远程代理:跨越网络的桥梁

远程代理主要用于访问远程服务器资源,比如通过 HTTP 代理访问网站。在分布式系统中,客户端可能需要调用远程服务器上的服务 ,这时就可以使用远程代理。它就像一个网络快递员,帮你把请求送到远程服务器,再把结果带回来。

以访问国外网站为例,由于网络限制,直接访问可能无法成功,这时可以通过 HTTP 代理服务器来访问。代理服务器会代替你向目标网站发送请求,并将响应结果返回给你,你无需关心复杂的网络通信过程。

4.2 虚拟代理:资源加载的小助手

虚拟代理用于在需要时才创建对象,能有效提高系统性能。比如加载大图片时,先显示占位图,等图片加载完成再显示真实图片,这就是虚拟代理的应用。

在图片展示应用中,当页面加载大量图片时,如果直接加载真实图片,可能会导致页面卡顿。使用虚拟代理,先加载一个小的占位图,用户可以快速看到页面内容,当用户真正需要查看某张图片时,再加载真实的大图片,这样既提高了用户体验,又节省了资源。

4.3 保护代理:系统安全的守护者

保护代理用于控制对对象的访问权限,确保只有授权用户才能访问某些功能。在系统中,不同用户有不同的权限,保护代理可以验证用户权限,限制特定用户访问敏感操作。

比如在一个电商系统中,普通用户只能查看商品信息和下单,而管理员用户可以进行商品管理、订单管理等操作。通过保护代理,在用户访问这些功能时,先验证用户权限,只有管理员用户才能执行管理操作,从而保证系统的安全性和稳定性。

五、优缺点大对决

代理模式就像一把双刃剑,既有闪闪发光的优点,也存在一些小小的不足,我们一起来看看~

5.1 优点大揭秘

职责清晰:代理模式让真实对象和代理对象各司其职,真实对象专注于核心业务逻辑,比如房东只需要负责出租房子;代理对象则专注于控制访问和其他辅助操作,像中介负责带租客看房、收取中介费。这样一来,每个对象的职责明确,代码结构也更加清晰,维护起来超轻松!就好比一个公司里,业务部门专心搞业务,后勤部门负责处理各种杂事,分工明确,效率更高。

扩展性强:在代理对象中添加新功能简直不要太容易!不管是日志记录,还是权限控制,只要有需求,随时都能加进去。而且,添加新功能的时候,根本不需要修改真实对象的代码,完全符合开闭原则 。比如我们想在租房流程中增加一个记录租客信息的功能,只需要在代理类中添加相应代码,房东类的代码完全不用动,是不是超方便?

方便解耦:客户端只和代理对象打交道,和真实对象之间的耦合度大大降低。这样一来,就算真实对象的实现发生了变化,只要代理对象的接口不变,客户端就完全不受影响。就像租客只需要和中介联系,房东那边不管有什么变动,租客都不用操心,只要能顺利租到房子就行。这种解耦的方式,让代码的灵活性和可维护性都得到了极大的提升 。

5.2 缺点小吐槽

增加系统复杂度:引入代理对象和相关逻辑,确实会让系统变得稍微复杂一些。不仅要多写一些代码来实现代理类,而且在理解和调试的时候,也需要同时考虑代理对象和真实对象之间的关系,不像直接调用真实对象那么简单直接。就好比租房时多了中介这一层,流程肯定会比直接和房东联系复杂一些。

可能影响性能:特别是在动态代理中,反射调用会带来一定的性能开销。因为反射机制需要在运行时动态获取类的信息和方法,这比直接调用方法要耗费更多的时间和资源。如果系统对性能要求非常高,在频繁调用的场景下,这种性能损耗可能就需要重点考虑了。不过,在大多数情况下,只要合理使用代理模式,这种性能影响还是可以接受的 。

六、使用小贴士

在使用代理模式时,掌握一些实用的小技巧可以让我们的代码更加高效和优雅~

6.1 合理选择代理类型

在实际应用中,静态代理和动态代理各有优势,要根据具体场景合理选择。如果代理对象较少,而且接口比较稳定,很少发生变化,像一些简单的业务模块,只需要对少数几个类进行代理操作,并且这些类的接口在项目周期内基本不会改变,这时静态代理就很合适,它的实现简单直接 。但要是代理对象比较多,或者需要在运行时动态生成代理,比如在一个大型的电商系统中,有很多不同类型的商品服务需要代理,而且根据业务需求可能随时要新增代理对象,这种情况下动态代理就更灵活,能大大减少代码量和维护成本 。

6.2 注意性能优化

动态代理虽然强大又灵活,但由于使用了反射机制,在性能要求较高的场景中,反射调用带来的性能开销就不能忽视啦 。比如在一个高并发的在线游戏服务器中,频繁地进行动态代理方法调用,可能会导致服务器响应变慢。所以在这种场景下,要谨慎使用动态代理,或者对动态代理进行性能优化,比如缓存代理对象、减少不必要的反射操作等。

6.3 确保逻辑清晰

在代理对象中添加功能时,一定要保证逻辑清晰明了 。代理对象的主要职责是控制对真实对象的访问和添加一些额外操作,如果添加的逻辑过于复杂,就会让代码变得难以理解和维护。比如在一个用户权限验证的代理中,如果在代理逻辑里又加入了大量与业务逻辑无关的日志记录、数据统计等操作,就会让这个代理类变得臃肿不堪,后续修改和调试都会很麻烦。所以要遵循单一职责原则,让每个代理对象的功能尽可能单一,这样代码的可读性和可维护性都会大大提高 。

七、总结

代理模式在代码世界中扮演着不可或缺的角色,它帮助我们实现访问控制、增强功能和解耦。无论是在大型项目还是日常开发中,都能发挥重要作用。宝子们,你们在项目中有没有用过代理模式?快来评论区分享吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

PGFA

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

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

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

打赏作者

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

抵扣说明:

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

余额充值