代理模式

90 阅读3分钟

静态代理

需要被代理的目标对象

//火车站买票
public class TrainStationTicket{

    public void sell() {
        System.out.println("卖票");
    }
}

代理对象

public class ProxyPoint{
    private TrainStationTicket trainStationTicket;

    public ProxyPoint(TrainStationTicket trainStationTicket) {
        this.trainStationTicket = trainStationTicket;
    }

    public void sell() {
        System.out.println("卖票前宣传");
        trainStationTicket.sell();
        System.out.println("卖票后售后");
    }
}

动态代理

1. JDK 动态代理(基于接口)

(1)实现原理

  • JDK 动态代理使用 反射 和 接口 来生成代理对象。

  • 核心类:

    • java.lang.reflect.Proxy(生成代理类)
    • java.lang.reflect.InvocationHandler(处理代理逻辑)

(2)为什么需要接口?

  1. 字节码生成限制

    • JDK 动态代理在运行时 动态生成一个类,该类继承 Proxy 并 实现目标接口
    • 由于 Java 是单继承的,代理类已经继承了 Proxy,无法再继承其他类,因此只能通过 实现接口 的方式代理。
  2. 方法调用机制

    • 代理对象的所有方法调用都会委托给 InvocationHandler.invoke()
    • 接口提供了明确的方法签名,确保代理类能正确拦截方法。 接口
public interface Service {
    boolean send(String msg);
}

需要被代理的类

public class ServiceImpl implements Service{
    @Override
    public boolean send(String msg) {
        System.out.println("发送消息:"+msg);
        return true;
    }
}

实现InvocationHandler接口,通过invoke方法自定义增强逻辑

public class ProxyHandler implements InvocationHandler {
    private final Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理前增强操作");
        Object res = method.invoke(target, args);
        System.out.println("代理后增强操作");
        return res;
    }
}

代理类工厂,用于创建代理类

public class ProxyHandlerFactory {

    public static Object getServiceProxy(Object Target){
        return Proxy.newProxyInstance(Target.getClass().getClassLoader(),Target.getClass().getInterfaces(),new ProxyHandler(Target));
    }
}
Proxy.newProxyInstance(Target.getClass().getClassLoader(),Target.getClass().getInterfaces(),new ProxyHandler(Target));的底层实现原理

Java 动态代理通过 Proxy.newProxyInstance 创建代理对象,其底层原理如下:

  • 生成代理类字节码: 根据传入的接口列表,在内存中动态生成一个实现这些接口的代理类 $Proxy0(继承 Proxy 类并实现目标接口)。
  • 加载代理类: 使用指定的类加载器(通常是目标对象的类加载器)将生成的字节码加载为 Class 对象。
  • 创建代理实例: 通过反射调用代理类的构造函数,传入 InvocationHandler 实现类(如 ProxyHandler),创建代理对象。
  • 方法调用转发: 当调用代理对象的方法时,会转交给 InvocationHandler 的 invoke 方法处理,实现如增强逻辑、转发调用等。

简而言之:动态生成实现接口的代理类 → 加载该类 → 创建其实例 → 所有方法调用被转发到 InvocationHandler。

2. CGLIB 动态代理(基于继承)

(1)实现原理

  • CGLIB(Code Generation Library)通过 字节码增强 动态生成目标类的 子类,并重写方法。

  • 核心类:

    • net.sf.cglib.proxy.Enhancer(生成代理类)
    • net.sf.cglib.proxy.MethodInterceptor(方法拦截逻辑)

(2)为什么不需要接口?

  1. 继承机制

    • CGLIB 生成的代理类 直接继承目标类,因此不依赖接口。
    • 它通过 方法拦截(MethodInterceptor)  实现增强。
  2. 覆盖非 final 方法

    • CGLIB 会重写目标类的非 final 方法(final 方法无法代理)。

注意: JDK 9+ 的模块系统(JPMS)限制了对某些类的反射访问,特别是 java.lang.ClassLoader.defineClass 方法。CGLIB 在生成代理类时尝试通过反射调用该方法,但被 JVM 拒绝,导致 InaccessibleObjectException。所以需要添加动态参数--add-opens java.base/java.lang=ALL-UNNAMED

public class ClibProxy {
    static class Target {
        public void foo() {
            System.out.println("target foo");
        }

        @Override
        public String toString() {
            return super.toString();
        }
    }

    public static void main(String[] param) {
        // 目标对象
        Target target = new Target();
        // 代理对象
        Target proxy = (Target) Enhancer.create(Target.class,
                (MethodInterceptor) (p, method, args, methodProxy) -> {
                    System.out.println("proxy before...");
//                    Object result = method.invoke(target, args);
                    // 另一种调用方法,不需要目标对象实例
            Object result = methodProxy.invokeSuper(p, args);
                    System.out.println("proxy after...");
                    return result;
                });
        // 调用代理
        proxy.foo();
    }

    @Override
    public String toString() {
        return super.toString();
    }
}

总结

代理方式实现机制是否需要接口性能适用场景
JDK 动态代理接口 + 反射✅ 必须较低目标类已实现接口
CGLIB继承 + 字节码增强❌ 不需要较高目标类无接口或需更高性能
  • JDK 代理 是 Java 标准库的一部分,但受限于接口。
  • CGLIB 更灵活,但需注意 final 方法的限制。
  • Spring AOP 根据目标类自动选择,也可手动配置。