静态代理和动态代理的区别及命名原因

在 Java 中,代理模式可以分为静态代理动态代理,它们都是通过代理对象来间接调用目标对象的方法,但它们的实现方式和使用场景有所不同。本文将详细阐述静态代理与动态代理的区别,并解释为什么要称之为“静态”和“动态”。


1. 静态代理和动态代理的区别

1.1 静态代理

静态代理是指在编译期间代理类已经存在,并且代理类与被代理类实现相同的接口。在静态代理中,开发者需要手动编写代理类代码,并显式地调用被代理对象的方法。

  • 实现方式:手动创建代理类,并通过组合被代理对象的方式代理其方法。
  • 代理类生成时机:编译时,代理类已经存在于代码中。
  • 使用场景:适用于较简单且方法数量有限的场景。

示例代码:

public interface Service {
    void performTask();
}

public class RealService implements Service {
    @Override
    public void performTask() {
        System.out.println("Performing task in RealService");
    }
}

public class ProxyService implements Service {
    private RealService realService;

    public ProxyService(RealService realService) {
        this.realService = realService;
    }

    @Override
    public void performTask() {
        System.out.println("Before task");
        realService.performTask();
        System.out.println("After task");
    }
}
1.2 动态代理

动态代理则不同,它是在运行时动态生成代理类,开发者不需要手动编写代理类代码。Java 提供了两种主要的动态代理方式:JDK 动态代理和 CGLIB 动态代理。

  • 实现方式

    • JDK 动态代理:通过 Proxy 类和 InvocationHandler 接口动态生成代理类,仅支持代理接口。
    • CGLIB 动态代理:通过字节码生成技术代理类,支持代理普通类。
  • 代理类生成时机:运行时通过反射或字节码生成技术创建代理类。

  • 使用场景:适用于复杂场景,特别是需要代理大量类或接口时,动态代理更为灵活。

JDK 动态代理示例:

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

// 代理接口
public interface Service {
    void performTask();
}

// 被代理类
public class RealService implements Service {
    @Override
    public void performTask() {
        System.out.println("Performing task in RealService");
    }
}

// 动态代理处理器
public class ProxyInvocationHandler implements InvocationHandler {
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before task");
        Object result = method.invoke(target, args);  // 调用实际方法
        System.out.println("After task");
        return result;
    }
}

// 客户端使用
public class Client {
    public static void main(String[] args) {
        Service realService = new RealService();
        // 创建动态代理实例
        Service proxyInstance = (Service) Proxy.newProxyInstance(
                realService.getClass().getClassLoader(),
                realService.getClass().getInterfaces(),
                new ProxyInvocationHandler(realService)
        );
        proxyInstance.performTask();
    }
}
创建动态代理参数解释
  1. realService.getClass().getClassLoader() - 类加载器
  • 这个参数用于指定代理类的类加载器。由于动态代理是运行时生成代理类的字节码,在创建代理类时,Java 需要通过某个类加载器将代理类加载到内存中。在这里,我们选择使用被代理对象 realService 的类加载器,即 realService.getClass().getClassLoader()

    为什么要用被代理类的类加载器?

  • 代理类是为特定对象(realService)动态生成的,代理类与被代理类的运行环境(类加载器)应该一致,以避免命名空间或类加载问题。

  • 使用被代理对象的类加载器可以确保代理类和被代理类在同一上下文中运行,避免类加载冲突或访问不到需要的资源。

  1. realService.getClass().getInterfaces() - 接口列表
  • 动态代理要求代理类实现与被代理类相同的接口。在这里,通过 realService.getClass().getInterfaces() 获取的是被代理类 RealService 实现的所有接口。

  • JDK 动态代理是基于接口的代理,这意味着只有实现了接口的类才能使用 JDK 动态代理。如果被代理类没有实现任何接口,那么 JDK 动态代理是无效的,这时可以考虑使用 CGLIB 等其他代理机制,它们可以代理没有实现接口的类。这个参数定义了代理类的“行为规范”,即代理类必须能替代被代理类的接口,从而保证在客户端看来,代理对象和被代理对象是可以互换的。

  1. new ProxyInvocationHandler(realService) - 调用处理器
  • InvocationHandler 是一个接口,它定义了代理类中方法的调用逻辑。这个参数传入的是一个实现了 InvocationHandler 接口的对象——ProxyInvocationHandler,它接收了 realService 作为参数,表示在代理类中,当某个方法被调用时,实际的处理会委托给 ProxyInvocationHandler,而 InvocationHandler 会通过反射机制调用被代理对象(realService)的相应方法。
  • 在每次调用代理对象的方法时,都会触发 InvocationHandlerinvoke 方法,在这个方法中我们可以做额外的逻辑处理,比如日志记录、权限验证、事务管理等。
1.3 静态代理与动态代理的对比
对比项静态代理动态代理
代理类生成时机编译时,手动编写代理类运行时,动态生成代理类
代理方式需要实现相同的接口,手动调用被代理对象的方法通过反射或字节码生成,自动调用被代理对象的方法
实现复杂度相对简单,但需要手动创建代理类相对复杂,但代理过程更灵活
适用场景方法少、逻辑简单的场景方法多、需要灵活代理的场景
性能性能稍高,代理类提前生成性能稍低,代理类在运行时生成
扩展性不易扩展,每个接口都需要一个对应的代理类容易扩展,代理多个类或接口更加灵活

2. 为什么叫“静态”和“动态”?

2.1 静态代理

静态代理之所以称为“静态”,是因为代理类在编译期间已经固定,即代理类是显式定义的,开发者需要在代码中手动编写并维护这些代理类。每次如果要更改代理的行为,或者代理不同的类,开发者都需要重新编写或修改代理类。因此,静态代理具有静态固定的特征,代理的类和逻辑都在编译阶段确定。

  • 静态的含义:编译期确定,代理类是固定的,不会在运行时动态生成。
2.2 动态代理

动态代理则称为“动态”,是因为代理类并不是在编译期间定义的,而是在运行时通过反射或字节码生成技术动态生成。开发者不需要手动编写代理类,JDK 或 CGLIB 在运行时根据需要动态地创建代理类,并将方法调用委托给实际对象。这种方式非常灵活,可以在运行时根据需求动态决定代理的逻辑或代理的对象。

  • 动态的含义:运行期生成,代理类是动态生成的,具有高度的灵活性。

3. 总结

静态代理和动态代理是两种常用的代理模式,尽管它们的实现方式不同,但本质都是通过代理类控制对目标对象的访问。静态代理的代理类在编译期生成,比较简单易懂,但缺乏灵活性;而动态代理则是在运行时生成代理类,提供了更强的灵活性,适合于代理多个类或接口的场景。

因此,它们被称为“静态”和“动态”主要取决于代理类的生成时机。静态代理在编译时生成,表现为“静态”;而动态代理在运行时生成,表现为“动态”。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值