什么是代理模式?

Java中的代理模式是一种结构型设计模式,它允许通过代理对象来控制对另一个对象的访问。这种模式在Java中有着广泛的应用,特别是在需要增强对象功能、控制访问权限或实现某些特定行为时。下面会对Java代理模式进行详细解释。

一、基本概念

代理模式涉及三个主要角色:

抽象角色: 通过接口或抽象类声明真实角色实现的业务方法。
代理角色: 实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
**真实角色:**实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。

二、静态代理

定义:静态代理是在编译时就已经确定代理类和被代理类的关系,代理类需要手动实现和被代理类相同的接口,并调用被代理类的方法。

特点:

代理类和被代理类都实现了相同的接口。

代理类的对象在程序运行之前就已经确定。

代理对象中会包含目标对象的引用。

优点:

可以在不修改目标对象的情况下扩展目标对象的功能。

由于代理类是预先编写好的,所以运行时效率较高。

缺点:

需要为每一个目标对象创建一个对应的代理类,如果目标对象很多,则代理类也会很多,增加了代码的复杂性。

如果目标接口增加了新的方法,则代理类和目标类都需要进行相应的修改,增加了维护成本。

三、动态代理

定义:动态代理是在运行时动态生成代理类,不需要手动实现接口,可以在运行时为任意一个类创建代理对象。

分类:
基于接口的动态代理(JDK动态代理): JDK动态代理是Java自带的动态代理机制,它是在运行时通过反射机制生成代理类,代理类实现了指定接口,从而实现对该接口的代理操作。被代理的类必须实现一个接口。
**基于类的动态代理(CGLIB动态代理):**CGLIB动态代理是基于类的代理,它不需要被代理的类实现一个接口,而是直接继承被代理类,通过子类代理的方式实现对被代理类的代理操作。CGLIB是一个优秀的字节码生成库,通过在运行时动态生成字节码,实现对被代理类的代理操作。

优点:

灵活性高,可以动态地为不同的目标对象创建代理,而无需预先知道目标对象的具体类型。

只需要编写一次代理逻辑,就可以适用于所有的代理对象,降低了代码的重复性。

缺点:

JDK动态代理要求目标对象必须实现一个接口,否则无法使用。

由于涉及到反射,运行时效率相对较低。

CGLIB动态代理只能代理非final类和非final方法,且由于是通过继承实现的,可能会受到Java单继承的限制。

四、应用场景

远程代理: 在远程方法调用中,代理模式可以用于隐藏客户端和服务器之间的网络细节。客户端通过代理对象调用远程服务器的方法,代理对象负责处理网络通信、序列化和反序列化等细节。
虚拟代理: 在创建开销较大的对象时,可以使用代理模式延迟对象的创建,直到真正需要使用它时才进行创建。例如,在图像加载时,可以使用虚拟代理来延迟加载图像资源,避免长时间的等待。
安全代理: 代理模式可以用于控制对真实对象的访问权限。代理对象可以在调用真实对象的方法前进行权限检查,确保只有具有相应权限的用户才能访问真实对象。
缓存代理: 代理模式可以用于缓存对象的方法调用结果。当多个客户端需要调用相同的方法时,代理对象可以在第一次调用时缓存方法的结果,以后的调用都直接返回缓存的结果,避免重复计算。
**日志记录:**代理模式可以用于记录方法的调用日志。代理对象可以在调用真实对象的方法前后记录日志信息,用于调试和分析应用程序的运行情况。

五、示例代码

以下是基于JDK动态代理的一个简单例子:

java 复制代码
import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.Method;  
import java.lang.reflect.Proxy;  
  
// 接口  
interface UserService {  
    void addUser();  
}  
  
// 实现类  
class UserServiceImpl implements UserService {  
    @Override  
    public void addUser() {  
        System.out.println("添加用户");  
    }  
}  
  
// 代理类  
class UserServiceProxy implements InvocationHandler {  
    private Object target;  
  
    public Object getProxy(Object target) {  
        this.target = target;  
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);  
    }  
  
    @Override  
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
        System.out.println("方法调用前");  
        Object result = method.invoke(target, args);  
        System.out.println("方法调用后");  
        return result;  
    }  
}  
  
// 测试类  
public class DynamicProxyTest {  
    public static void main(String[] args) {  
        UserService userService = new UserServiceImpl();  
        UserService proxy = (UserService) new UserServiceProxy().getProxy(userService);  
        proxy.addUser();  
    }  
}

运行测试类DynamicProxyTest,你将看到以下输出:

方法调用前

添加用户

方法调用后

我们定义了一个接口UserService和其实现类UserServiceImpl,然后定义了一个代理类UserServiceProxy,该类实现了InvocationHandler接口,并重写了其中的invoke方法。在invoke方法中,我们可以实现对真实对象的代理操作,如添加日志、权限检查等。最后,通过调用Proxy.newProxyInstance方法创建代理对象,并返回该代理对象。

相关推荐
BLOB_1010015 分钟前
【折腾一上午】Java POI 导出 Excel 自适应列宽行高
java·excel
布值倒区什么name16 分钟前
日常记录,使用springboot,vue2,easyexcel使实现字段的匹配导入
java·spring boot·后端
single59420 分钟前
【综合算法学习】(第十篇)
java·数据结构·c++·vscode·学习·算法·leetcode
wclass-zhengge22 分钟前
SpringBoot篇(自动装配原理)
java·spring boot·后端
哎呦没29 分钟前
中小企业设备管理效率提升:Spring Boot系统设计
java·spring boot·后端
ZWZhangYu32 分钟前
【MyBatis源码】SqlSource对象创建流程
java·tomcat·mybatis
Yaml434 分钟前
Spring Boot整合EasyExcel:实现大规模数据的并行导出与压缩下载
java·开发语言·spring boot
Kobebryant-Manba35 分钟前
sqlyog软件
java·sql
free_girl_fang39 分钟前
夯实根基之MySql从入门到精通(一)
java·数据结构·数据库·mysql
编程修仙40 分钟前
Java三大特性之继承
java·开发语言