静态代理和动态代理

静态代理

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

静态代理的基本结构

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

  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编程中非常重要的技术手段。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值