静态代理
先简单谈谈静态代理,静态代理是指在编译期间就已经确定代理类的具体实现方式的代理模式。在静态代理中,代理类和委托类(被代理类)的关系在编译时就已经确定,代理类是在编译期间就已经创建好的。
主要角色:
-
抽象角色(Subject):声明了代理类和委托类的共同接口,这样在任何使用委托类的地方都可以使用代理类。
-
具体委托角色(RealSubject):也叫真实角色,定义了实际的业务逻辑,代理类所代理的真实对象。
-
代理角色(Proxy):持有对委托类的引用,并且实现了抽象角色所定义的接口,在其内部包含了对真实对象的引用,并可以调用真实对象的方法,在调用前后进行一些额外的处理。
优点:
-
结构简单:相比动态代理,静态代理的结构较为简单,易于理解和实现。
-
编译时确定:静态代理在编译时就已经确定了代理类和委托类的关系,不需要在运行时动态生成代理类,因此执行效率较高。
缺点:
-
扩展性差:由于代理类和委托类之间的关系在编译时就已经确定,因此如果需要代理多个委托类,就需要为每个委托类都创建一个对应的代理类,导致代码量增加和维护困难。
-
不够灵活:在静态代理中,代理类需要提前知道委托类的类型,如果需要代理的委托类比较多或者在运行时动态变化,则需要不断修改代理类的代码,不够灵活。
示例:
下面是一个简单的 Java 示例,演示了静态代理的实现方式:
// 抽象角色
interface Subject {
void request();
}
// 具体委托角色
class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject: Handling request.");
}
}
// 代理角色
class Proxy implements Subject {
private RealSubject realSubject;
public Proxy() {
this.realSubject = new RealSubject();
}
@Override
public void request() {
System.out.println("Proxy: Pre-processing");
realSubject.request();
System.out.println("Proxy: Post-processing");
}
}
public class StaticProxyExample {
public static void main(String[] args) {
// 使用代理对象调用方法
Proxy proxy = new Proxy();
proxy.request();
}
}
在这个示例中,Subject
接口是抽象角色,RealSubject
类是具体委托角色,Proxy
类是代理角色。在 Proxy
类的 request()
方法中,先进行了一些前置处理,然后调用了真实对象 RealSubject
的 request()
方法,最后进行了后置处理。
动态代理
介绍:
Java 的动态代理机制允许在运行时创建动态代理类和对象,这些代理类和对象在运行时生成,而不是编译时。动态代理是通过 Java 反射机制实现的,它可以实现在不修改原始类的情况下,对方法进行增强、拦截或者修改。
Java 中的动态代理机制主要涉及两个类:Proxy
和 InvocationHandler
。
-
Proxy 类:
java.lang.reflect.Proxy
类是所有动态代理类的父类,它提供了创建动态代理对象的静态方法newProxyInstance()
。这个方法接受三个参数:一个类加载器(ClassLoader)、一个要实现的接口数组和一个InvocationHandler
接口的实现类对象。 -
InvocationHandler 接口:
java.lang.reflect.InvocationHandler
接口中只有一个方法invoke()
,用于在动态代理对象上执行方法,并实现方法的拦截、增强等逻辑。
实现步骤:
-
定义接口:首先需要定义一个接口,代理对象和被代理对象都需要实现这个接口。
-
编写 InvocationHandler 实现类:实现
InvocationHandler
接口的invoke()
方法,在这个方法中实现对被代理方法的拦截、增强等逻辑。 -
获取代理对象:通过
Proxy.newProxyInstance()
方法获取代理对象,传入类加载器、接口数组和InvocationHandler
实现类对象。
示例代码:
假设有一个接口 Subject
,以及一个实现类 RealSubject
,我们使用动态代理模式来创建代理对象,对 RealSubject
的方法进行增强。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 定义接口
interface Subject {
void request();
}
// 实现类
class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject: Handling request.");
}
}
// 实现 InvocationHandler 接口
class MyInvocationHandler implements InvocationHandler {
private Object realSubject;
public MyInvocationHandler(Object realSubject) {
this.realSubject = realSubject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method invocation.");
Object result = method.invoke(realSubject, args);
System.out.println("After method invocation.");
return result;
}
}
public class DynamicProxyExample {
public static void main(String[] args) {
// 创建真实对象
Subject realSubject = new RealSubject();
// 创建 InvocationHandler 实现类对象
InvocationHandler handler = new MyInvocationHandler(realSubject);
// 获取代理对象
Subject proxy = (Subject) Proxy.newProxyInstance(
realSubject.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(),
handler
);
// 调用代理对象的方法
proxy.request();
}
}
在这个示例中,我们首先定义了一个接口 Subject
和一个实现类 RealSubject
,然后编写了一个实现了 InvocationHandler
接口的 MyInvocationHandler
类,用于在方法调用前后进行拦截和增强。最后,通过 Proxy.newProxyInstance()
方法获取代理对象,并调用代理对象的方法。在方法调用前后,代理对象会调用 MyInvocationHandler
中的 invoke()
方法,执行自定义的逻辑。
总结
动态代理通常用于实现 AOP(面向切面编程)等场景,可以非常方便地实现一些横切关注点(cross-cutting concerns),例如日志记录、性能监控、事务管理等。通过动态代理,可以将这些横切关注点从业务逻辑中解耦出来,提高代码的灵活性和可维护性。