Java设计模式:代理模式的静态和动态之分(八)-六、代理模式的三种实现

时间:2024-04-08 08:42:06

6.1 静态代理模式

Java中的静态代理模式是一种相对简单的设计模式,它要求代理类和被代理类实现相同的接口或继承自相同的父类。代理类在内部持有被代理类的引用,并在需要时调用被代理类的方法,同时可以在调用前后添加额外的逻辑。

在这里插入图片描述

下面是一个简单的静态代理模式的实现:

首先,定义一个接口:

public interface Service {
    void performTask();
}

然后,创建被代理类,实现该接口:

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

接下来,创建代理类,同样实现该接口,并在构造函数中接收一个被代理类的实例:

public class ProxyService implements Service {
    private final Service realService;

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

    @Override
    public void performTask() {
        System.out.println("ProxyService: Before task.");
        realService.performTask(); // 调用被代理类的方法
        System.out.println("ProxyService: After task.");
    }
}

最后,在客户端代码中使用代理类:

public class Client {
    public static void main(String[] args) {
        // 创建被代理对象
        Service realService = new RealService();
        
        // 创建代理对象,将被代理对象作为参数传入
        Service proxyService = new ProxyService(realService);
        
        // 通过代理对象执行方法,会触发代理类中的额外逻辑
        proxyService.performTask();
    }
}

当运行客户端代码时,输出将会是:

ProxyService: Before task.
RealService is performing the task.
ProxyService: After task.

在这个例子中,ProxyService是代理类,它增强了RealService的功能,即在执行任务前后输出了额外的日志信息。客户端代码通过代理类ProxyService来调用performTask方法,而不需要直接与被代理类RealService交互。这种方式允许在不修改原始类的情况下增加新的行为或控制访问。

6.2 JDK动态代理的实现

在这里插入图片描述

JDK动态代理是Java提供的一种在运行时创建代理类和对象的方式。它主要利用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现。下面是一个使用JDK动态代理模式的实现:

首先,定义一个接口:

public interface Service {
    void performTask();
}

然后,创建被代理类,实现该接口:

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

接下来,创建一个实现了InvocationHandler接口的类。这个类的invoke方法会在代理对象的方法被调用时被执行:

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

public class ServiceInvocationHandler implements InvocationHandler {
    private final Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before invoking method: " + method.getName());
        Object result = method.invoke(target, args); // 调用被代理对象的方法
        System.out.println("After invoking method: " + method.getName());
        return result;
    }
}

最后,在客户端代码中使用Proxy.newProxyInstance方法创建代理对象,并通过该代理对象调用方法:

import java.lang.reflect.Proxy;

public class Client {
    public static void main(String[] args) {
        // 创建被代理对象
        Service realService = new RealService();
        
        // 创建InvocationHandler实例,将被代理对象传入
        InvocationHandler handler = new ServiceInvocationHandler(realService);
        
        // 使用Proxy.newProxyInstance创建代理对象
        Service proxyService = (Service) Proxy.newProxyInstance(
                Service.class.getClassLoader(), // 类加载器
                new Class<?>[] { Service.class }, // 代理类要实现的接口列表
                handler // 关联调用处理器
        );
        
        // 通过代理对象执行方法,会触发InvocationHandler中的invoke方法
        proxyService.performTask();
    }
}

当运行客户端代码时,输出将会是:

Before invoking method: performTask
RealService is performing the task.
After invoking method: performTask

在这个例子中,ServiceInvocationHandler是实现了InvocationHandler接口的调用处理器。当通过代理对象proxyService调用performTask方法时,实际上会调用ServiceInvocationHandler中的invoke方法。在invoke方法内部,我们可以添加任何额外的逻辑,比如方法调用前后的日志输出、权限检查等。通过method.invoke(target, args)语句,我们实际上是在调用被代理对象realServiceperformTask方法。

6.3 Cglib代理

cglib 是一个强大的、高性能的、高质量的代码生成库,它可以在运行时为 Java 类或接口生成子类或代理类。在 Java 代理模式中,当需要代理的类没有实现接口时,可以使用 cglib 来创建一个该类的子类作为代理。

要使用 cglib,首先需要将其添加到项目的依赖中。如果你使用 Maven,可以在 pom.xml 文件中添加以下依赖:

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version> <!-- 请检查是否有更新的版本 -->
</dependency>

下面是一个使用 cglib 创建代理类的示例:

首先,定义一个普通的类(注意,这个类没有实现任何接口):

public class RealService {
    public void performTask() {
        System.out.println("RealService is performing the task.");
    }
}

然后,创建一个实现了 MethodInterceptor 接口的类,该类将用于拦截对代理类方法的调用:

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class ServiceMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("Before invoking method: " + method.getName());
        Object result = proxy.invokeSuper(obj, args); // 调用父类(被代理类)的方法
        System.out.println("After invoking method: " + method.getName());
        return result;
    }
}

最后,在客户端代码中使用 Enhancer 类来创建代理对象:

import net.sf.cglib.proxy.Enhancer;

public class Client {
    public static void main(String[] args) {
        // 创建被代理对象,实际上这里并不需要直接创建,因为cglib会创建它的子类
        // RealService realService = new RealService();
        
        // 创建Enhancer实例,并设置父类为RealService
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(RealService.class);
        
        // 设置回调,即方法拦截器
        enhancer.setCallback(new ServiceMethodInterceptor());
        
        // 创建代理对象
        RealService proxyService = (RealService) enhancer.create();
        
        // 通过代理对象执行方法,会触发ServiceMethodInterceptor中的intercept方法
        proxyService.performTask();
    }
}

当运行客户端代码时,输出将会是:

Before invoking method: performTask
RealService is performing the task.
After invoking method: performTask

在这个例子中,ServiceMethodInterceptor 实现了 MethodInterceptor 接口,用于拦截对代理类方法的调用。在 intercept 方法中,我们可以添加任何额外的逻辑,比如方法调用前后的日志输出。通过 proxy.invokeSuper(obj, args) 语句,我们实际上是在调用被代理类 RealServiceperformTask 方法。Enhancer 类用于创建代理类,并通过 create 方法实例化代理对象。

6.4 三种实现的区别和优缺点

代理模式主要有三种实现方式(静态代理、JDK动态代理和cglib动态代理)中由于静态代理通常针对每个具体类编写,不具有通用性,因此这里主要讨论JDK动态代理和cglib动态代理的区别和优缺点。

6.4.1 JDK动态代理

实现方式
JDK动态代理要求目标类必须实现至少一个接口。代理类是在运行时动态生成的,实现了目标类所实现的所有接口,并通过反射调用目标类的方法。

优点

  • 无需为每个目标类编写具体的代理类,提高了代码的复用性和可维护性。
  • 由于代理类实现了接口,因此系统的耦合度较低,更加灵活。

缺点

  • 目标类必须实现至少一个接口,否则无法使用JDK动态代理。
  • 在方法调用频繁的情况下,由于使用了反射机制,性能可能较低。
6.4.2 cglib动态代理

实现方式
cglib动态代理是通过生成目标类的子类来实现的。它不需要目标类实现任何接口,而是通过继承目标类并重写其方法来创建代理类。在方法调用时,通过方法拦截器来增强方法的调用。

优点

  • 目标类无需实现任何接口,更加灵活。
  • 在某些场景下,由于直接调用子类方法而无需经过反射,性能可能优于JDK动态代理。

缺点

  • 由于代理类是通过继承目标类来实现的,因此目标类和方法不能声明为final类型。
  • 引入了额外的cglib库依赖,增加了项目的复杂性。
  • 系统的耦合度可能较高,因为代理类与目标类是继承关系。

JDK动态代理和cglib动态代理各有优缺点,选择哪种方式取决于具体的需求和场景。如果目标类已经实现了接口,那么JDK动态代理是一个不错的选择。如果目标类没有实现接口,或者需要更高的性能,那么可以考虑使用cglib动态代理。不过,在使用cglib时需要注意目标类和方法不能声明为final类型,以及引入额外依赖的问题。