静态代理和动态代理

静态代理

静态代理是一种在编译时就确定代理类的设计模式。在静态代理中,代理类和被代理类(即目标类)在编译时就已经确定,并且代理类实现了与目标类相同的接口或继承了相同的类,从而能够在代理类中控制对目标类方法的访问。

静态代理的基本结构

静态代理通常涉及三个主要角色:

  1. 接口(或父类):定义了目标类和代理类的共同方法。
  2. 目标类:实现接口的具体业务逻辑类,即实际执行操作的类。
  3. 代理类:实现接口并包含对目标类的引用,用于在执行目标类方法前后添加额外的功能。

静态代理的实现示例

假设我们有一个接口 Service,目标类 ServiceImpl 实现了该接口,并且代理类 ServiceProxy 也实现了该接口。

// 接口
public interface Service {
    void performAction();
}

// 目标类
public class ServiceImpl implements Service {
    @Override
    public void performAction() {
        System.out.println("执行 ServiceImpl 类中的 performAction 方法.");
    }
}

// 代理类
public class ServiceProxy implements Service {
    private ServiceImpl serviceImpl;

    public ServiceProxy(ServiceImpl serviceImpl) {
        this.serviceImpl = serviceImpl;
    }

    @Override
    public void performAction() {
        System.out.println("在执行 ServiceImpl 类中的 performAction 方法之前.");
        serviceImpl.performAction();
        System.out.println("在执行 ServiceImpl 类中的 performAction 方法之后.");
    }
}

// 使用代理
public class Main {
    public static void main(String[] args) {
        ServiceImpl serviceImpl = new ServiceImpl();
        Service serviceProxy = new ServiceProxy(serviceImpl);
        serviceProxy.performAction();
    }
}

输出:

在执行 ServiceImpl 类中的 performAction 方法之前
执行 ServiceImpl 类中的 performAction 方法
在执行 ServiceImpl 类中的 performAction 方法之前

静态代理的特点

  1. 预定义性:代理类在编译时就确定了,且代理类的代码是手动编写的,因此它的功能是预定义的。
  2. 扩展性:静态代理可以在不修改目标类的情况下,为目标类的方法添加一些额外的功能,如日志记录、权限控制等。
  3. 代码冗余:如果有多个目标类需要代理,可能会导致大量的重复代码,因为每个目标类都需要对应的代理类。
  4. 耦合性:代理类需要依赖具体的目标类,这使得静态代理在某种程度上增加了代码的耦合度。

静态代理的优缺点

  • 优点

    • 控制性:可以在方法调用的前后进行额外操作,适合对一些特定行为进行控制。
    • 简洁性:代理类的行为是明确且可控的,易于理解。
  • 缺点

    • 代码冗余:如果有很多接口或类需要代理,会产生大量的代理类,导致代码量增大。
    • 不够灵活:由于代理类在编译时就确定,无法根据运行时的需求动态生成代理类。

动态代理

动态代理是一种在运行时生成代理类的设计模式,代理类不需要在编译时就确定。Java 中的动态代理通过反射机制来实现,它允许在运行时动态地为接口生成代理类,从而在方法调用时添加额外的行为,例如日志记录、事务管理、权限控制等。

动态代理的基本概念

在动态代理中,有两个主要角色:

  1. 代理对象(Proxy Object):这是在运行时生成的代理类实例,负责调用目标对象的方法并可以在调用前后执行额外的逻辑。
  2. Invocation Handler:这是一个处理器接口,定义了代理对象在方法调用时需要执行的逻辑。InvocationHandler 接口只有一个 invoke 方法,负责处理对代理对象方法的调用。

动态代理的实现

Java 中的动态代理主要通过 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口来实现。

示例

假设我们有一个接口 Service,及其实现类 ServiceImpl

public interface Service {
    void performAction();
}

public class ServiceImpl implements Service {
    @Override
    public void performAction() {
        System.out.println("执行 ServiceImpl 类中的 performAction 方法.");
    }
}

现在,我们通过动态代理来为 Service 接口生成一个代理对象,在方法调用前后添加一些额外的逻辑。

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

// 创建 InvocationHandler 实现类
public class ServiceInvocationHandler implements InvocationHandler {
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("先执行于" + target.getClass().getSimpleName());
        
        // 调用目标对象的方法
        Object result = method.invoke(target, args);
        
        System.out.println("后执行于" + target.getClass().getSimpleName());
        
        return result;
    }
}

// 使用动态代理生成代理对象
public class Main {
    public static void main(String[] args) {
        // 创建目标对象
        Service service = new ServiceImpl();
        
        // 创建代理对象
        Service proxyInstance = (Service) Proxy.newProxyInstance(
                service.getClass().getClassLoader(),
                service.getClass().getInterfaces(),
                new ServiceInvocationHandler(service)
        );
        
        // 调用代理对象的方法
        proxyInstance.performAction();
    }
}
输出:
先执行于 ServiceImpl
执行 ServiceImpl 类中的 performAction 方法.
后执行于 ServiceImpl

动态代理的特点

  1. 灵活性

    • 代理类在运行时动态生成,意味着你可以在运行时决定代理的行为,而不需要在编译时确定。
  2. 减少重复代码

    • 动态代理可以复用相同的 InvocationHandler 来代理不同的目标对象,避免了静态代理中为每个目标对象创建一个代理类的重复代码。
  3. 解耦

    • 通过动态代理,可以将横切关注点(如日志记录、事务管理)与业务逻辑解耦,使代码更加模块化、易于维护。
  4. 适用于接口

    • 在 Java 的标准动态代理机制下,只有实现接口的类才能使用动态代理。如果没有接口,可以使用字节码操作库(如 CGLIB)实现对类的动态代理。

动态代理的应用场景

  1. AOP(面向切面编程):动态代理是 Spring AOP 的核心技术,通过代理为目标方法添加切面逻辑。
  2. RPC(远程过程调用):在一些 RPC 框架中,动态代理用于封装远程调用的细节,使得调用远程方法像调用本地方法一样简单。
  3. 拦截器:动态代理可以用于在方法执行前后执行一些拦截逻辑,比如权限验证、日志记录等。

动态代理与静态代理的对比

特点静态代理动态代理
代理类的生成时间编译时生成运行时动态生成
代码实现代理类代码手动编写代理类通过反射机制动态生成
代理对象的数量每个目标类都需要一个代理类通过同一个代理类处理多个目标对象
扩展性扩展性较差,添加新功能时需要修改代理类代码扩展性好,可以在运行时灵活添加功能
适用范围适用于结构简单、功能明确的场景适用于复杂的场景,如AOP、RPC等

总结

静态代理适用于简单的场景,但在面对复杂需求时,动态代理更为合适。动态代理通过在运行时生成代理类,提供了极大的灵活性和扩展性,使得代码更具模块化和可维护性。它广泛应用于框架开发、AOP编程、RPC调用等领域,是Java编程中非常重要的技术手段。

### 静态代理动态代理的概念、区别及使用场景 #### 1. 概念 静态代理动态代理代理模式的两种实现方式。代理模式的核心思想是通过一个代理对象来控制对目标对象的访问,从而实现功能扩展或访问控制。 - **静态代理**是指在编译时就已经确定了代理代理类的关系[^1]。代理类需要手动编写,并且必须实现与被代理类相同的接口。 - **动态代理**则是在运行时动态生成代理类[^2],无需在编译前手动编写代理类代码。动态代理通常通过 Java 的 `java.lang.reflect.Proxy` 类或其他框架(如 CGLIB)实现。 #### 2. 区别 静态代理动态代理的主要区别体现在以下几个方面: - **定义时间**: - 静态代理代理类在编译时就已经确定[^3]。 - 动态代理代理类是在运行时动态生成的[^1]。 - **灵活性**: - 静态代理由于代理类是固定的,因此不够灵活,每次新增接口或修改接口时都需要重新编写代理类。 - 动态代理可以适应多种接口的变化,具有更高的灵活性可扩展性[^3]。 - **性能**: - 静态代理因为代理类是提前编译好的,所以在执行效率上可能略高于动态代理。 - 动态代理由于需要在运行时生成代理类,可能会引入一定的性能开销,但现代 JVM 已经对此进行了优化[^1]。 - **适用场景**: - 静态代理适合于接口较少且相对固定的情况。 - 动态代理更适合于接口较多或经常变化的场景,能够减少代码冗余并提高开发效率[^3]。 #### 3. 使用场景 - **静态代理**: - 当代理类的功能较为简单,且接口不常变化时,可以使用静态代理[^1]。 - 示例:日志记录、权限校验等简单的功能增强[^2]。 - **动态代理**: - 在需要为多个类提供统一的代理逻辑时,动态代理更加合适[^3]。 - 示例:AOP(面向切面编程)、事务管理、远程方法调用等复杂场景。 #### 示例代码 以下分别展示了静态代理动态代理实现方式。 ##### 静态代理示例 ```java // 定义接口 interface Rent { void rent(); } // 真实主题类 class RealRent implements Rent { @Override public void rent() { System.out.println("房东出租房子"); } } // 静态代理类 class ProxyRent implements Rent { private Rent realRent; public ProxyRent(Rent realRent) { this.realRent = realRent; } @Override public void rent() { System.out.println("中介开始找房..."); realRent.rent(); System.out.println("中介收取佣金"); } } // 测试 public class StaticProxyTest { public static void main(String[] args) { Rent realRent = new RealRent(); Rent proxyRent = new ProxyRent(realRent); proxyRent.rent(); // 输出:中介开始找房... 房东出租房子 中介收取佣金 } } ``` ##### 动态代理示例 ```java import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; // 定义接口 interface Rent { void rent(); } // 真实主题类 class RealRent implements Rent { @Override public void rent() { System.out.println("房东出租房子"); } } // 动态代理类 class DynamicProxyHandler implements InvocationHandler { private Object target; public DynamicProxyHandler(Object target) { this.target = target; } @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) { Rent realRent = new RealRent(); InvocationHandler handler = new DynamicProxyHandler(realRent); Rent proxyRent = (Rent) Proxy.newProxyInstance( realRent.getClass().getClassLoader(), realRent.getClass().getInterfaces(), handler ); proxyRent.rent(); // 输出:中介开始找房... 房东出租房子 中介收取佣金 } } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值