Java 代理模式深度解析:从静态到动态的实现与原理

发布于:2025-03-24 ⋅ 阅读:(110) ⋅ 点赞:(0)

目录

一、引言

二、静态代理:手动实现的基础形式

1. 定义业务接口与实现类

2. 创建代理类

3. 组合使用代理类

4. 优缺点分析

三、JDK 动态代理:基于接口的运行时代理

1. 核心实现

定义 InvocationHandler

客户端调用

2. 核心原理

代理类结构分析

模拟 JDK 动态代理实现

3. 优缺点

四、CGLIB 动态代理:基于继承的字节码增强

1. 核心实现

定义 MethodInterceptor

客户端调用

2. 核心原理

字节码增强流程

关键类解析

模拟 CGLIB 动态代理实现

3. 优缺点

五、代理模式对比与选择

六、Java动态代理的各种实现方式

一、引言

在 Java 开发中,代理模式(Proxy Pattern)是一种常用的设计模式。它通过引入中间层(代理类)来控制对目标对象的访问,允许在不修改原始代码的前提下添加额外功能。当我们遇到以下场景时,代理模式往往能发挥关键作用:

  • 在业务方法前后添加日志、监控等通用逻辑
  • 实现业务代码与通用功能的解耦
  • 处理仅有接口而无具体实现的场景(如 MyBatis Mapper)
  • 理解框架底层动态代理实现(如 Spring AOP)

本文将通过具体案例,深入探讨 Java 代理模式的实现方式与核心原理,并结合代码进行详细解析。

二、静态代理:手动实现的基础形式

静态代理通过手动编写代理类实现功能增强,是代理模式的基础形式。以下是其核心实现步骤:

1. 定义业务接口与实现类

// MyService接口
public interface MyService {
    void doTask();
}

// MyServiceImpl实现类
public class MyServiceImpl implements MyService {
    @Override
    public void doTask() {
        System.out.println("Task doing");
        try { Thread.sleep(10); } catch (InterruptedException e) { /* ... */ }
        System.out.println("Task done");
    }
}

2. 创建代理类

// 日志代理类
public class MyServiceLogProxy implements MyService {
    private final MyService target;
    public MyServiceLogProxy(MyService target) { this.target = target; }
    @Override public void doTask() {
        System.out.println("----do task log start----");
        target.doTask();
        System.out.println("----do task log end---");
    }
}

// 监控代理类
public class MyServiceMonitorProxy implements MyService {
    private final MyService target;
    public MyServiceMonitorProxy(MyService target) { this.target = target; }
    @Override public void doTask() {
        long start = System.currentTimeMillis();
        target.doTask();
        System.out.printf("Cost: %d ms%n", System.currentTimeMillis() - start);
    }
}

3. 组合使用代理类

// 客户端代码
public class MyServiceClient {
    public static void main(String[] args) {
        MyService service = new MyServiceImpl();
        MyService proxied = new MyServiceLogProxy(
            new MyServiceMonitorProxy(service)
        );
        proxied.doTask();
    }
}

4. 优缺点分析

  • 优点:实现简单,易于理解
  • 缺点
    • 代理类与接口强绑定,无法复用
    • 无法处理无接口的场景

三、JDK 动态代理:基于接口的运行时代理

JDK 动态代理通过反射机制在运行时生成代理类,是 Spring AOP 的默认实现方式。

1. 核心实现

定义 InvocationHandler
// 日志处理器
public class JDKDynamicLogProxy implements InvocationHandler {

    private Object target;

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

    /**
     * 获取被代理接口实例对象
     * @param 
     * @return
     */
    public <T> T getProxy() {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("----do task log start----");
        Object result = method.invoke(target, args);
        System.out.println("----do task log end---");
        return result;
    }
}
//JDK动态代理-监控
public class JDKDynamicMonitorProxy implements InvocationHandler {

    private Object target;

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

    /**
     * 获取被代理接口实例对象
     * @param 
     * @return
     */
    public <T> T getProxy() {
        /*
        Proxy.newProxyInstance()方法接受三个参数:
        ClassLoader loader:指定当前目标对象使用的类加载器,获取加载器的方法是固定的
        Class[] interfaces:指定目标对象实现的接口的类型,使用泛型方式确认类型
        InvocationHandler:指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法
         */
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long start = System.currentTimeMillis();
        System.out.println("do task start time:" + start);

        // 调用业务方法
        Object result = method.invoke(target, args);

        long end = System.currentTimeMillis();
        System.out.println("do task end time:" + end);

        System.out.println("do task cost " + (end - start) + "ms");

        return result;
    }
}
客户端调用
// JDKDynamicClient
public class JDKDynamicClient {
    public static void main(String[] args) {
        System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        MyService service = new MyServiceImpl();
        MyService proxied = new JDKDynamicLogProxy(service).getProxy();
        proxied.doTask();
    }
}

2. 核心原理

JDK 动态代理的核心在于java.lang.reflect.Proxyjava.lang.reflect.InvocationHandler的配合。当调用代理对象的方法时,实际执行的是InvocationHandlerinvoke方法。其核心流程如下:

  1. 字节码生成:通过Proxy.newProxyInstance生成动态代理类字节码,该类会实现目标接口。
  2. 方法拦截:代理类的所有方法调用都会委派给InvocationHandlerinvoke方法。
  3. 反射调用:在invoke方法中,通过反射调用目标对象的真实方法。
代理类结构分析

生成的代理类(如$Proxy0)会实现目标接口,并持有InvocationHandler实例:

public final class $Proxy0 implements MyService {
    private final InvocationHandler h;
    
    public $Proxy0(InvocationHandler h) { this.h = h; }
    
    public void doTask() {
        h.invoke(this, MyService.class.getMethod("doTask"), null);
    }
}
模拟 JDK 动态代理实现
代理逻辑的入口
public interface InvocationHandler {

    void invoke(Object o, Method m);

}
public class MonitorHandler implements InvocationHandler {

    private Object target;

    public MonitorHandler(Object target) {
        super();
        this.target = target;
    }

    @Override
    public void invoke(Object o, Method m) {
        long start = System.currentTimeMillis();

        System.out.println("do task start time:" + start);

        try {
            m.invoke(target);
        } catch (Exception e) {
            e.printStackTrace();
        }

        long end = System.currentTimeMillis();
        System.out.println("do task end time:" + end);
        System.out.println("do task cost " + (end - start) + "ms");
    }
}
模拟JDK动态代理类
public class Proxy {

    public static Object newProxyInstance(Class infce, InvocationHandler h) throws Exception {
        String methodStr = "";

        Method[] methods = infce.getMethods();

        for (Method m : methods) {
            methodStr += "    @Override\n" +
                    "    public void " + m.getName() + "() {\n" +
                    "        try {\n" +
                    "            Method md = " + infce.getName() + ".class.getMethod(\"" + m.getName() + "\");\n" +
                    "            h.invoke(this, md);\n" +
                    "        } catch(Exception e) {\n" +
                    "            e.printStackTrace();\n" +
                    "        }\n" +
                    "    }\n";
        }

        String sourceCode = "package cn.dmlove.proxy;\n" +
                "\n" +
                "import java.lang.reflect.Method;\n" +
                "import cn.dmlove.demo3.InvocationHandler;\n" +
                "\n" +
                "public class $JDKDynamicProxy1 implements " + infce.getName() + "{\n" +
                "\n" +
                "    private InvocationHandler h;\n" +
                "\n" +
                "    public $JDKDynamicProxy1(InvocationHandler h) {\n" +
                "        this.h = h;\n" +
                "    }\n" +
                "\n" +
                methodStr +
                "}";

        // 生成java源码文件
        String javaFileName = System.getProperty("user.dir") + "/src/main/java/cn/dmlove/proxy/$JDKDynamicProxy1.java";
        File javaFile = new File(javaFileName);
        FileWriter fileWriter = new FileWriter(javaFile);
        fileWriter.write(sourceCode);
        fileWriter.flush();
        fileWriter.close();

        // 获取编译器
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

        // 获取java文件管理器
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
        // 要编译的java文件
        Iterable javaFileObjects = fileManager.getJavaFileObjects(javaFile);

        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        // 指定class文件目录
        String flag = "-d";
        String outDir = "";
        try {
            File classPath = new File(classLoader.getResource("").toURI());
            outDir = classPath.getAbsolutePath() + File.separator;
        } catch (URISyntaxException e1) {
            e1.printStackTrace();
        }
        Iterable options = Arrays.asList(flag, outDir);
        // 获取编译任务
        JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, options, null, javaFileObjects);
        // 执行编译任务
        task.call();

        // 关闭java文件管理器
        fileManager.close();

        // class文件load到内存
        Class clazz = classLoader.loadClass("cn.dmlove.proxy.$JDKDynamicProxy1");

        Constructor cons = clazz.getConstructor(InvocationHandler.class);

        return cons.newInstance(h);
    }

}
public class $JDKDynamicProxy1 implements cn.dmlove.MyService {

    private InvocationHandler h;

    public $JDKDynamicProxy1(InvocationHandler h) {
        this.h = h;
    }

    @Override
    public void doTask() {
        try {
            Method md = cn.dmlove.MyService.class.getMethod("doTask");
            h.invoke(this, md);
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
}
模拟JDK动态代理调用
public class Client {

    public static void main(String[] args) throws Exception {
        MyService myService = new MyServiceImpl();
        InvocationHandler h = new MonitorHandler(myService);

        MyService m = (MyService)Proxy.newProxyInstance(MyService.class, h);

        m.doTask();
    }
}

运行结果:

生成代理类

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.sun.proxy;

import cn.dmlove.MyService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements MyService {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void doTask() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("cn.dmlove.MyService").getMethod("doTask");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

3. 优缺点

  • 优点
    • 完全运行时生成,无需手动编码
    • 支持接口方法拦截
  • 缺点
    • 仅支持接口代理
    • 反射调用存在性能损耗

四、CGLIB 动态代理:基于继承的字节码增强

CGLIB 通过字节码生成技术为类创建子类,实现对非接口类的代理。

1. 核心实现

定义 MethodInterceptor
/**
 * cglib动态监控代理
 */
public class CglibMonitorProxy implements MethodInterceptor {

    private Object target;

    public final Object getInstance(final Object target) {
        this.target = target;
        //cglib 中字节码加强器,用来创建动态代理
        Enhancer enhancer = new Enhancer();
        // 设置要创建动态代理的类
        enhancer.setSuperclass(this.target.getClass());
        // 设置回调,这里相当于是对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦截
        enhancer.setCallback(this);
        // 返回实例
        return enhancer.create();
    }

    @Override
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        long start = System.currentTimeMillis();
        System.out.println("do task start time:" + start);

        Object result = methodProxy.invokeSuper(object, args);

        long end = System.currentTimeMillis();
        System.out.println("do task end time:" + end);
        System.out.println("do task cost " + (end - start) + "ms");

        return result;
    }
客户端调用
public class CglibProxyClient {

    public static void main(String[] args) {
        //获取代理
        MyServiceImpl myServiceImplProxy = (MyServiceImpl) new CglibMonitorProxy().getInstance(new MyServiceImpl());

        // 执行
        myServiceImplProxy.doTask();
    }
}

2. 核心原理

字节码增强流程
  1. 子类生成:CGLIB 通过Enhancer生成目标类的子类
  2. 方法拦截:重写父类方法,通过MethodInterceptor实现逻辑增强
  3. 快速调用:使用MethodProxy优化方法调用,避免反射开销(文档 3-59)
关键类解析
  • Enhancer:字节码增强器,用于设置父类和回调
  • MethodInterceptor:定义拦截逻辑,类似 JDK 的InvocationHandler
  • MethodProxy:缓存方法调用,提升性能
模拟 CGLIB 动态代理实现
import java.lang.reflect.Method;

// 目标类
class TargetClass {
    public void doSomething() {
        System.out.println("Target is doing something.");
    }
}

// 方法拦截器接口
interface MethodInterceptor {
    Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable;
}

// 方法代理类
class MethodProxy {
    private Method method;

    public MethodProxy(Method method) {
        this.method = method;
    }

    public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        return method.invoke(obj, args);
    }
}

// 模拟 Enhancer 类,用于生成代理类
class Enhancer {
    private Class<?> superclass;
    private MethodInterceptor callback;

    public void setSuperclass(Class<?> superclass) {
        this.superclass = superclass;
    }

    public void setCallback(MethodInterceptor callback) {
        this.callback = callback;
    }

    public Object create() {
        try {
            // 这里模拟生成代理类,实际上 CGLIB 会动态生成字节码
            // 这里简单使用反射创建目标类的实例
            TargetClass target = (TargetClass) superclass.getDeclaredConstructor().newInstance();
            return new ProxyClass(target, callback);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    // 模拟生成的代理类
    private static class ProxyClass extends TargetClass {
        private TargetClass target;
        private MethodInterceptor callback;

        public ProxyClass(TargetClass target, MethodInterceptor callback) {
            this.target = target;
            this.callback = callback;
        }

        @Override
        public void doSomething() {
            try {
                Method method = TargetClass.class.getMethod("doSomething");
                MethodProxy proxy = new MethodProxy(method);
                callback.intercept(this, method, new Object[0], proxy);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

// 测试代码
public class SimulateCglibProxy {
    public static void main(String[] args) {
        // 创建 Enhancer 实例
        Enhancer enhancer = new Enhancer();
        // 设置目标类
        enhancer.setSuperclass(TargetClass.class);
        // 设置方法拦截器
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                System.out.println("Before method execution.");
                Object result = proxy.invokeSuper(obj, args);
                System.out.println("After method execution.");
                return result;
            }
        });
        // 创建代理对象
        TargetClass proxy = (TargetClass) enhancer.create();
        // 调用代理对象的方法
        proxy.doSomething();
    }
}    

3. 优缺点

  • 优点
    • 支持非接口类代理
    • 性能优于 JDK 代理
  • 缺点
    • 无法代理final类 / 方法
    • 大量使用可能导致 PermGen 内存溢出

五、代理模式对比与选择

特性 静态代理 JDK 动态代理 CGLIB 动态代理
代理机制 手动实现 接口实现 子类继承
生成时机 编译期 运行期 运行期
性能 中(反射调用) 高(字节码优化)
灵活性
适用场景 简单功能扩展 接口类代理 非接口类代理

六、Java动态代理的各种实现方式


网站公告

今日签到

点亮在社区的每一天
去签到