Java中的动态代理与静态代理性能对比实战

技术解码器 2020-09-20 ⋅ 12 阅读

在Java中,代理模式是一种常见的设计模式,用于在目标对象的前后添加一些额外的逻辑。在代理模式中,有两种常见的实现方式:静态代理和动态代理。在本文中,我们将对这两种代理模式的性能进行对比,并进行实战演示。

什么是静态代理和动态代理

静态代理

静态代理是指在编译期间就已经确定被代理的对象,并且代理对象与被代理对象是一对一的关系。在静态代理中,代理对象实现了与被代理对象相同的接口,并在方法调用前后添加一些额外的逻辑。

动态代理

动态代理是指在运行时通过反射机制动态生成代理对象,无需事先知道被代理的对象是谁。在动态代理中,代理对象并不是具体的类,而是通过接口和InvocationHandler动态生成的。

性能对比实战

为了对比静态代理和动态代理的性能,我们创建一个简单的示例场景:一个计算器接口,包含了四个基本的运算方法:加、减、乘、除。

首先,我们来实现静态代理。

静态代理示例

// 计算器接口
public interface Calculator {
    int add(int a, int b);
    int subtract(int a, int b);
    int multiply(int a, int b);
    int divide(int a, int b);
}

// 目标对象
public class CalculatorImpl implements Calculator {
    @Override
    public int add(int a, int b) {
        return a + b;
    }

    @Override
    public int subtract(int a, int b) {
        return a - b;
    }

    @Override
    public int multiply(int a, int b) {
        return a * b;
    }

    @Override
    public int divide(int a, int b) {
        return a / b;
    }
}

// 静态代理对象
public class CalculatorProxy implements Calculator {
    Calculator calculator;

    public CalculatorProxy(Calculator calculator) {
        this.calculator = calculator;
    }

    @Override
    public int add(int a, int b) {
        // 添加日志逻辑
        System.out.println("开始执行加法运算");
        int result = calculator.add(a, b);
        System.out.println("加法运算结果为:" + result);
        return result;
    }

    @Override
    public int subtract(int a, int b) {
        // 添加日志逻辑
        System.out.println("开始执行减法运算");
        int result = calculator.subtract(a, b);
        System.out.println("减法运算结果为:" + result);
        return result;
    }

    @Override
    public int multiply(int a, int b) {
        // 添加日志逻辑
        System.out.println("开始执行乘法运算");
        int result = calculator.multiply(a, b);
        System.out.println("乘法运算结果为:" + result);
        return result;
    }

    @Override
    public int divide(int a, int b) {
        // 添加日志逻辑
        System.out.println("开始执行除法运算");
        int result = calculator.divide(a, b);
        System.out.println("除法运算结果为:" + result);
        return result;
    }
}

// 使用静态代理进行计算
public class Main {
    public static void main(String[] args) {
        Calculator calculator = new CalculatorImpl();
        Calculator proxy = new CalculatorProxy(calculator);
        proxy.add(1, 2);
        proxy.subtract(4, 2);
        proxy.multiply(3, 4);
        proxy.divide(8, 2);
    }
}

在静态代理中,代理对象CalculatorProxy在add、subtract、multiply和divide方法前后分别添加了日志输出的逻辑。

接下来,我们来实现动态代理。

动态代理示例

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

// 计算器接口
public interface Calculator {
    int add(int a, int b);
    int subtract(int a, int b);
    int multiply(int a, int b);
    int divide(int a, int b);
}

// 目标对象
public class CalculatorImpl implements Calculator {
    @Override
    public int add(int a, int b) {
        return a + b;
    }

    @Override
    public int subtract(int a, int b) {
        return a - b;
    }

    @Override
    public int multiply(int a, int b) {
        return a * b;
    }

    @Override
    public int divide(int a, int b) {
        return a / b;
    }
}

// 动态代理对象
public class DynamicProxy implements InvocationHandler {
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("开始执行" + method.getName() + "方法");
        Object result = method.invoke(target, args);
        System.out.println(method.getName() + "方法执行结果为:" + result);
        return result;
    }

    // 创建代理对象
    public static Object createProxy(Object target) {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new DynamicProxy(target));
    }
}

// 使用动态代理进行计算
public class Main {
    public static void main(String[] args) {
        Calculator calculator = new CalculatorImpl();
        Calculator proxy = (Calculator) DynamicProxy.createProxy(calculator);
        proxy.add(1, 2);
        proxy.subtract(4, 2);
        proxy.multiply(3, 4);
        proxy.divide(8, 2);
    }
}

在动态代理中,通过InvocationHandler的invoke方法,在目标方法执行前后添加了日志输出的逻辑。动态代理通过Proxy的newProxyInstance方法生成代理对象。

性能对比

为了对比静态代理和动态代理的性能差异,我们使用JMH进行性能测试。

首先,我们需要添加依赖和配置JMH插件。在pom.xml中添加如下依赖:

<dependencies>
    ...
    <dependency>
        <groupId>org.openjdk.jmh</groupId>
        <artifactId>jmh-core</artifactId>
        <version>1.21</version>
    </dependency>
    <dependency>
        <groupId>org.openjdk.jmh</groupId>
        <artifactId>jmh-generator-annprocess</artifactId>
        <version>1.21</version>
        <scope>provided</scope>
    </dependency>
    ...
</dependencies>
<build>
    <plugins>
        ...
        <plugin>
            <groupId>org.openjdk.jmh</groupId>
            <artifactId>jmh-maven-plugin</artifactId>
            <version>1.21</version>
            <configuration>
                <mainClass>com.example.Main</mainClass>
            </configuration>
        </plugin>
        ...
    </plugins>
</build>

接下来,我们创建性能测试类ProxyBenchmark.java。该类用于测试静态代理和动态代理的性能。

import org.openjdk.jmh.annotations.*;

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class ProxyBenchmark {

    private Calculator calculator;
    private Calculator staticProxy;
    private Calculator dynamicProxy;

    @Setup
    public void setup() {
        calculator = new CalculatorImpl();
        staticProxy = new CalculatorProxy(calculator);
        dynamicProxy = (Calculator) DynamicProxy.createProxy(calculator);
    }

    @Benchmark
    public int testStaticProxy() {
        return staticProxy.add(1, 2);
    }

    @Benchmark
    public int testDynamicProxy() {
        return dynamicProxy.add(1, 2);
    }

    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(ProxyBenchmark.class.getSimpleName())
                .forks(1)
                .warmupIterations(3)
                .measurementIterations(5)
                .build();

        new Runner(opt).run();
    }
}

在ProxyBenchmark中,我们使用静态代理和动态代理实现了相同的加法运算,并使用@Benchmark注解标记需要进行性能测试的方法。在main方法中,我们通过JMH的Runner来运行性能测试。

运行该测试类,我们可以得到静态代理和动态代理的性能对比结果。

性能测试结果分析

在我的测试环境中,静态代理和动态代理的性能差异并不明显。根据不同的场景和应用需求,性能的优劣可能会有所差异。在一些需要动态生成代理对象的场景下,动态代理通常更为灵活,并且可以提高代码的可维护性。

需要注意的是,动态代理在生成代理对象的过程中会消耗一定的时间,因此在某些性能要求非常高的场景下,静态代理可能更适合。

综上所述,静态代理和动态代理在不同的场景下都有各自的优劣势。开发者需要根据具体的需求和性能要求选择合适的代理方式。

结论

本文对Java中的静态代理和动态代理进行了性能对比实战,并讨论了它们在不同场景下的优劣势。根据实际需求和性能要求,开发者可以选择适合自己的代理方式。


全部评论: 0

    我有话说: