在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中的静态代理和动态代理进行了性能对比实战,并讨论了它们在不同场景下的优劣势。根据实际需求和性能要求,开发者可以选择适合自己的代理方式。
本文来自极简博客,作者:技术解码器,转载请注明原文链接:Java中的动态代理与静态代理性能对比实战