静态代理和动态代理是面向对象编程中常用的代理模式的两种实现方式。它们的区别主要体现在代理类的生成方式、使用场景和灵活性上。下面将详细介绍这两种代理方式的不同之处:
1. 代理类的生成方式
-
静态代理:静态代理是在编译时就确定了代理类的实现,通常通过手动编写代理类或者由工具在编译期生成。这些代理类在程序运行前就已经存在,并且与目标类实现了相同的接口。
- 例子:假设你有一个接口
Subject
和它的实现类RealSubject
,静态代理通常会创建一个SubjectProxy
类,这个代理类会持有RealSubject
的引用,重写Subject
接口的方法,并在其中调用RealSubject
对应的方法
- 例子:假设你有一个接口
public interface Subject {
void request();
}
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject handling request.");
}
}
public class SubjectProxy implements Subject {
private RealSubject realSubject;
public SubjectProxy(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public void request() {
// 执行一些额外的操作
System.out.println("Proxy handling pre-processing.");
realSubject.request(); // 调用真实对象的方法
}
}
-
动态代理:动态代理是在运行时生成代理类,它不需要手动编写代理类。动态代理可以根据需求在运行时动态创建代理对象,通常通过反射机制实现。Java中的
java.lang.reflect.Proxy
类和InvocationHandler
接口就是实现动态代理的常用方式。 - 例子:使用 Java 动态代理,我们可以在运行时创建代理对象,无需手动编写代理类。通过
Proxy.newProxyInstance
创建代理对象,并提供一个InvocationHandler
实现。public class DynamicProxyExample { public static void main(String[] args) { Subject realSubject = new RealSubject(); InvocationHandler handler = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Proxy handling pre-processing."); return method.invoke(realSubject, args); } }; Subject proxy = (Subject) Proxy.newProxyInstance( realSubject.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler); proxy.request(); // 通过代理调用 } }
2. 使用场景
-
静态代理:适用于代理类和目标类实现相同接口的场景。静态代理需要提前确定好所有代理的类,因此比较适合代理逻辑相对简单,且代理类不需要太多变化的场景。
-
动态代理:适用于需要根据不同的需求生成不同的代理类,或者代理的类的数量较大,且不可能在编译时知道具体的代理类的场景。动态代理可以更加灵活地根据需求生成代理。
3. 灵活性与扩展性
-
静态代理:静态代理的灵活性较差。由于代理类在编译时就已经确定,一旦需要增加新的代理逻辑或者修改代理行为,通常需要手动修改代理类。如果有多个接口需要代理,可能需要为每个接口都写一个代理类,维护起来比较麻烦。
-
动态代理:动态代理的灵活性和扩展性较好。它通过反射机制,可以在运行时根据需要创建不同的代理对象,可以减少代码的冗余,适合于需要高度复用和解耦的场景。如果需要添加新的代理行为,只需实现新的
InvocationHandler
或者调整现有的处理逻辑即可。
4. 性能
-
静态代理:由于静态代理类是预先编写好的,在调用时没有额外的反射操作,因此性能较高。
-
动态代理:动态代理通过反射机制生成代理类和方法调用,相比静态代理来说,会带来一些性能开销。因此,在性能要求较高的场景中,可能需要谨慎使用动态代理。
5. 代码量
-
静态代理:如果有多个接口或多个实现类需要代理,则每个接口或实现类都需要创建一个对应的代理类,可能会导致大量的代码重复。
-
动态代理:动态代理通过使用一个
InvocationHandler
实现可以代理多个不同的接口,减少了代码重复量。如果需要增加代理功能,只需增加不同的处理逻辑,无需修改代理类的代码。
6. 总结
- 静态代理:编译时生成代理类,代码较为固定,灵活性差,适用于代理行为简单的场景。
- 动态代理:运行时生成代理类,灵活性高,可以动态代理多个接口,适用于复杂的代理需求。