代理模式是一种设计模式它可以在运行时动态地创建对象,通过代理对象控制对目标对象(实际业务对象)的访问。代理对象通过控制对目标对象的访问来增加额外的功能,如权限验证、延迟加载、日志记录等。代理模式可以实现当一个客户不想或者不能直接引用另一个对象时,在客户端和目标对象直接起到中介的作用。
代理的角色分为3种,分别是抽像角色,代理角色和真实角色:
抽象角色是用于声明真实对象和代理对象的共同接口;
而代理对象角色内部含有对真实对象的引用,从而可以操纵真实的对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象,同时代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装;
真实角色即代理所代理的真实角色,是我们要引用的对象。
java中有2种代理模式,分别是静态代理和动态代理:
静态代理是指在编译时就创建代理类,并且代理类和目标类实现相同的接口。静态代理的核心思想是代理类和目标类具有相同的接口,代理类通过调用目标类的方法来实现具体的功能。其易于理解和实现。代理类和目标类具有明确的关系,使用时直接调用。但是如果有多个目标对象,就需要为每个目标对象创建一个代理类,代码量大,维护困难。且代理类和目标类耦合紧密,灵活性差。
//静态代理案例
// 定义一个接口
interface SS {
void DoSomeThing();
}
// 目标类,具体实现
class Sum implements SS {
public void DoSomeThing() {
System.out.println("运行中...");
}
}
// 代理类
class Pro implements SS {
private Sum sum;
public Pro(Sum s) {
this.sum = s;
}
public void DoSomeThing() {
System.out.println("运行前功能...");//在这里实现运行前的功能部分
sum.DoSomeThing();
System.out.println("运行后功能...");//实现运行后的功能部分
}
}
public class Test5 {
public static void main(String[] args) {
Sum r = new Sum();
Pro p = new Pro(r);
// 通过代理类调用目标类的方法
p.DoSomeThing();
}
}
而动态代理是指在运行时,根据目标对象的接口动态地创建代理对象,代理类不需要提前实现接口,而是在运行时生成。Java 提供了 java.lang.reflect.Proxy
类来实现动态代理。动态代理主要有两种方式:基于接口的JDK动态代理和基于继承的CGLIB动态代理。 其中,JDK 动态代理只能为实现了接口的类创建代理对象。通过 java.lang.reflect.Proxy
和 InvocationHandler
接口来实现,其代理对象会调用 InvocationHandler
接口的 invoke()
方法,并通过反射机制调用目标对象的具体方法。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 定义一个接口
interface SS {
void DoSomeThing();
}
// 目标类,具体实现
class Thing implements SS {
public void DoSomeThing() {
System.out.println("运行中...");
}
}
// 动态代理处理类
class ProxyInvocationHandler implements InvocationHandler {
private Object target; // 目标对象
public ProxyInvocationHandler(Object target) {
this.target = target;
}
// 代理对象会调用这个方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("运行前...");
// 调用目标对象的方法
Object result = method.invoke(target, args);
System.out.println("运行后...");
return result;
}
}
public class Test5 {
public static void main(String[] args) {
Thing t = new Thing();
// 创建代理类
SS proxySubject = (SS) Proxy.newProxyInstance(
t.getClass().getClassLoader(),
t.getClass().getInterfaces(),
new ProxyInvocationHandler(t)
);
// 通过代理对象调用方法
proxySubject.DoSomeThing();
}
}
而CGLIB 代理用于在运行时动态生成目标类的子类,由于其是基于继承的,而非接口实现,因此可以代理没有实现接口的类。CGLIB 使用字节码技术动态生成代理类。CGLIB 动态代理通过继承目标类来实现代理,因此目标类不需要实现任何接口。
静态代理适用于代理类和目标类之间关系固定、不需要改变的场景。通常用于特定的功能扩展,如日志记录、性能统计等。而动态代理则适用于目标类未知或数量庞大的情况,能在运行时灵活生成代理类,常见于大型框架(如 Spring)中。