代理模式详解

代理模式是一种结构型设计模式,其核心思想是通过引入一个代理对象来控制对原对象的访问,从而实现对目标对象的间接操作。以下是对代理模式的进一步补充和优化:

代理模式的角色

  • 抽象对象(Subject):

    定义了代理对象和目标对象的共同接口。

    使得客户端可以通过统一的接口与不同的具体实现进行交互。

  • 目标对象(Target):

    实际需要被代理的对象。

    负责处理具体的业务逻辑。

  • 代理对象(Proxy):

    持有对目标对象的引用。

    控制对目标对象的访问,可以在访问前后添加额外的处理逻辑,如权限检查、延迟加载、日志记录等。

    代理模式在多种场景中都能得到广泛应用。以下是一些典型的应用场景:

代理模式应用场景

  1. 权限控制

    • 通过代理对象对目标对象的访问进行权限检查,确保只有授权用户才能执行特定操作。
  2. 远程代理

    • 在需要与远程对象进行交互时,使用代理对象来处理网络通信、序列化和反序列化等细节。
  3. 虚拟代理

    • 在需要延迟加载资源密集型对象时,通过代理对象控制其创建和初始化,以节省系统资源。
  4. 缓存代理

    • 在频繁访问的数据上应用缓存策略,使用代理对象存储数据的副本,以减少对目标对象的直接访问,提高性能。
  5. 日志记录

    • 通过代理对象记录对目标对象的操作日志,帮助进行调试和审计。
  6. 智能引用

    • 通过代理实现引用计数或资源管理,以便在不再使用目标对象时自动释放资源。

实际例子

  • Web服务代理:在Web服务中,客户端通常通过代理类与远程服务器进行交互。
  • 虚拟文件系统:文件系统可能使用虚拟代理来表示大文件,只有在真正需要时才加载文件内容。
  • 数据库连接池 :数据库连接池管理器可以使用代理来控制对数据库连接的分配和释放。
    通过这些应用场景,我们可以看到代理模式的重要性,它不仅提高了系统的灵活性,还增强了对复杂操作的控制能力。

静态代理

静态代理模式可以在不修改目标对象的功能前提下,对目标功能扩展。

代码实现:

java 复制代码
/* 抽象角色 */
public interface Subject {
    void method1();
}

/* 目标(委托类) */
public class Target implements Subject {
    @Override
    public void method1() {
        System.out.println("method 1");
    }
}

/* 代理 */
public class Proxy implements Subject {

    Target target; // 包装一个目标类型对象

    public Proxy(Target target) {
        this.target = target;
    }

    @Override
    public void method1() {
        System.out.println("===start==="); // 目标方法执行前的操作
        target.method1();
        System.out.println("=== end ==="); // 目标方法执行后的操作
    }
    //测试
public static void main(String[] args) {
        //使用多态代理
        Subject subject = new Proxy(new Target());
        //动态绑定 实际上是使用的是Proxy 中的method1
        subject.method1();

        //代理指定类
        Proxy proxy =new Proxy(new Target());
        proxy.method1();
    }
}
}

测试结果:

jdk动态代理机制详解

Java 动态代理是指在程序运行期间,JVM根据反射机制动态生成代理类。这意味着代理类的字节码文件并不存在于编译时,而是在运行时创建的。这种机制使得我们可以在不修改目标类(委托类)代码的情况下,为其提供增强功能,如日志记录、权限控制等。

1. 动态代理的基本结构
  • 目标类(委托类):我们希望对其方法进行增强的实际对象。
  • 代理类:在运行时生成的,负责转发方法调用到目标类的对象。
  • 调用处理器(InvocationHandler) :实现了 java.lang.reflect.InvocationHandler 接口的类,用于集中处理目标对象的方法调用。
2. java.lang.reflect.Proxy

java.lang.reflect.Proxy 是所有动态代理类的父类。它提供了一组静态方法,用于为一组接口动态生成代理类及其对象。具体来说,使用 newProxyInstance() 方法可以创建一个新的代理实例。

newProxyInstance() 方法

该方法接受三个参数:

  1. ClassLoader:用于加载代理类的类加载器。
  2. Interfaces:一个包含目标类实现的接口数组。动态代理只能针对接口进行实现,因此目标类必须实现这些接口。
  3. InvocationHandler :一个实现了 InvocationHandler 接口的实例。该实例中的 invoke() 方法会处理所有通过代理对象调用的方法。
3. InvocationHandler 接口

InvocationHandler 接口定义了一个重要的方法:

java 复制代码
Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
  • proxy:代表当前代理对象。
  • method:被调用的方法对象。
  • args:被调用方法的参数。

invoke() 方法中,可以添加额外的逻辑,例如:

  • 前置处理(如日志记录、权限检查)
  • 调用目标方法
  • 后置处理(如结果处理、性能监控)
4. 动态代理的优点
  • 解耦:业务逻辑与横切关注点(如日志、事务)分离,提高代码可维护性。
  • 灵活性:可以在运行时决定要使用哪个目标对象,易于扩展和修改。
  • 无侵入性:不需要修改目标类,只需实现相应接口即可。
5. 示例代码

下面是一个简单的动态代理示例:

java 复制代码
import java.lang.reflect.*;

interface HelloService {
    void sayHello(String name);
}

class HelloServiceImpl implements HelloService {
    public void sayHello(String name) {
        System.out.println("Hello, " + name);
    }
}

class MyInvocationHandler implements InvocationHandler {
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before method: " + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("After method: " + method.getName());
        return result;
    }
}

public class Main {
    public static void main(String[] args) {
        HelloService helloService = new HelloServiceImpl();
        HelloService proxy = (HelloService) Proxy.newProxyInstance(
                helloService.getClass().getClassLoader(),
                new Class[]{HelloService.class},
                new MyInvocationHandler(helloService)
        );

        proxy.sayHello("World");
    }
}

这段代码展示了如何使用Java反射机制创建动态代理。它可以在不修改原有代码的情况下,为方法调用添加额外的功能(如日志记录、权限检查等)。通过这种方式,可以更灵活地扩展应用程序的功能。

Cglib动态代理

Cglib(Code Generation Library)是一种强大的高性能代码生成库,能够在运行时动态地生成类和接口。它通过在内存中创建目标类的子类来实现功能扩展,因此被称为"子类代理"。

与JDK动态代理的区别
  • JDK动态代理:要求目标对象至少实现一个接口。代理是基于接口的。
  • Cglib代理:不需要目标类实现任何接口,可以代理没有实现接口的类。这使得Cglib在处理没有接口的旧代码时特别有用。
工作原理

Cglib的底层使用了ASM(一个强大的字节码操作框架)来转换字节码并生成新的类。通过这种方式,Cglib可以在运行时创建子类,并在其中插入横切逻辑(如方法拦截)。

应用场景
  • 广泛用于AOP框架:例如Spring AOP,通过Cglib实现对目标对象的无侵入式增强。
  • 性能优化:由于直接操作字节码,Cglib在某些情况下比反射更高效。
使用说明

要使用Cglib,需要引入以下依赖:

  • cglib.jar
  • asm.jar

在Spring框架中,已经集成了Cglib功能,因此可以直接使用而无需额外配置。

注意事项
  • Cglib通过继承创建代理,因此不能代理final类,因为final类无法被继承。
  • 在处理大量动态类时,要注意内存消耗问题。
    代码样例;
java 复制代码
/* 目标类,不需要实现任何接口 */
public class Target {
    public void method1() {
        System.out.println("Target method 1 executed.");
    }
}

/* 代理工厂类,实现Cglib的MethodInterceptor接口 */
public class ProxyFactory implements MethodInterceptor {
    private final Object target; // 目标对象

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

    @Override
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        // 目标方法执行前的操作
        System.out.println("Before method execution: " + method.getName());
        Object result = method.invoke(target, args); // 调用目标方法
        // 目标方法执行后的操作
        System.out.println("After method execution: " + method.getName());
        return result; // 返回目标方法的返回值
    }

    // 创建代理对象的方法
    public Object getProxy() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass()); // 设置代理对象的父类为目标类
        enhancer.setCallback(this); // 设置方法拦截器
        return enhancer.create(); // 创建并返回代理对象
    }
}

/* 测试类 */
public class ProxyTest {
    public static void main(String[] args) {
        Target target = new Target(); // 创建目标对象
        Target proxy = (Target) new ProxyFactory(target).getProxy(); // 创建代理对象
        proxy.method1(); // 通过代理对象调用方法
    }
}
相关推荐
AiFlutter5 分钟前
Java实现简单的搜索引擎
java·搜索引擎·mybatis
飞升不如收破烂~26 分钟前
Spring boot常用注解和作用
java·spring boot·后端
秦老师Q27 分钟前
Java基础第九章-Java集合框架(超详细)!!!
java·开发语言
计算机毕设源码qq-383653104128 分钟前
(附项目源码)Java开发语言,215 springboot 大学生爱心互助代购网站,计算机毕设程序开发+文案(LW+PPT)
java·开发语言·spring boot·mysql·课程设计
ashane131431 分钟前
Java list
java·windows·list
袁庭新38 分钟前
Cannal实现MySQL主从同步环境搭建
java·数据库·mysql·计算机·java程序员·袁庭新
无尽的大道39 分钟前
深入理解 Java 阻塞队列:使用场景、原理与性能优化
java·开发语言·性能优化
岁岁岁平安1 小时前
springboot实战(15)(注解@JsonFormat(pattern=“?“)、@JsonIgnore)
java·spring boot·后端·idea
Oak Zhang1 小时前
TheadLocal出现的内存泄漏具体泄漏的是什么?弱引用在里面有什么作用?什么情景什么问题?
java·系统安全
数据小小爬虫1 小时前
如何利用Java爬虫获得1688店铺详情
java·开发语言