手写动态代理

动态代理 JavaJava

Posted by Lvyonghao on April 17, 2025

简介

Java 中的动态代理是为了解决“如何在不修改原始类的前提下,统一添加功能逻辑”,它通过运行时生成代理类,在方法调用时拦截请求,广泛应用于 AOP、RPC、ORM 等框架中。

场景

假设我现在有一个接口有三个方法:

package main.java.tech.instight.proxy;

public interface MyInterface {
    void func1();
    void func2();
    void func3();
}

他的一个实现类是:

package main.java.tech.instight.proxy;

public class NameImpl implements MyInterface{
    @Override
    public void func1() {
        System.out.println("func1");
    }

    @Override
    public void func2() {
        System.out.println("func2");
    }

    @Override
    public void func3() {
        System.out.println("func3");
    }
}

但如果我还想要在func1/2/3中添加新的功能,打印func1/2/3方法名的长度(方法的逻辑是根据方法的名称实现的)该如何做呢?普通的写代码已经不能实现了,理论上一个类就是一个文件内部是一堆字符串,类被编译成字节码,再加载到虚拟机jvm中就可以动态的创建一个类了。 OK,我们先来准备一个编译器,编译指定的java文件成一个字节码:

package tech.instight.proxy;

import javax.tools.JavaCompiler;
import javax.tools.*;
import java.io.File;
import java.util.Arrays;
import java.util.List;

public class Compiler {

    public static void compile(File javaFile) {
        // 获取系统 Java 编译器
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

        if (compiler == null) {
            System.out.println("未找到 Java 编译器。请确保使用的是 JDK 而不是 JRE!");
            return;
        }

        // 获取文件管理器
        try (StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null)) {

            // 获取要编译的 Java 文件对象
            Iterable<? extends JavaFileObject> compilationUnits =
                    fileManager.getJavaFileObjectsFromFiles(List.of(javaFile));

            // 设置编译选项(输出到指定 classes 文件夹)
            List<String> options = Arrays.asList("-d", "./out/production/NoUse");

            // 创建编译任务
            JavaCompiler.CompilationTask task = compiler.getTask(
                    null,              // 输出 writer
                    fileManager,       // 文件管理器
                    null,              // DiagnosticListener
                    options,           // 编译参数
                    null,              // 要编译的类名,null 表示自动获取
                    compilationUnits   // 源代码
            );

            // 执行编译任务
            boolean success = task.call();
            if (success) {
                System.out.println("✅ 编译成功!");
            } else {
                System.out.println("❌ 编译失败!");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

然后编写一个MyInterfaceFactory作为一个生成动态类的工厂,主要功能就是通过creatJavaFile方法把字符串生成一个NameImpl.java的文件,再交给Compiler编译成字节码:

package tech.instight.proxy;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;

public class MyInterfaceFactory {
    public static File creatJavaFile() throws IOException {
        String context = "package tech.instight.proxy;\n" +
                "\n" +
                "public class NameImpl implements MyInterface{\n" +
                "    @Override\n" +
                "    public void func1() {\n" +
                "        System.out.println(\"func1\");\n" +
                "    }\n" +
                "\n" +
                "    @Override\n" +
                "    public void func2() {\n" +
                "        System.out.println(\"func2\");\n" +
                "    }\n" +
                "\n" +
                "    @Override\n" +
                "    public void func3() {\n" +
                "        System.out.println(\"func3\");\n" +
                "    }\n" +
                "}\n";
        File javaFile = new File("NameImpl.java");
        Files.writeString(javaFile.toPath(),context);
        return javaFile;
    }

    public static void main(String[] args) throws IOException {
        File javaFile = creatJavaFile();
        Compiler.compile(javaFile);
    }
}

进一步的,我们想动态的生成自定义的类名的类的实例,通过creatProxyObject生成字节码并通过newInstance方法拿到MyInterface的代理类的实例:

package tech.instight.proxy;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.util.concurrent.atomic.AtomicInteger;

public class MyInterfaceFactory {
    private static final AtomicInteger count = new AtomicInteger();
    private static File creatJavaFile(String className) throws IOException {
        String func1Body = functionBody("fun1");
        String func2Body = functionBody("fun2");
        String func3Body = functionBody("fun3");
        String context = "package tech.instight.proxy;\n" +
                "\n" +
                "public class "+ className + "  implements MyInterface{\n" +
                "    @Override\n" +
                "    public void func1() {\n" +
                "        " + func1Body + "\n" +
                "    }\n" +
                "\n" +
                "    @Override\n" +
                "    public void func2() {\n" +
                "        " + func2Body + "\n" +
                "    }\n" +
                "\n" +
                "    @Override\n" +
                "    public void func3() {\n" +
                "        " + func3Body + "\n" +
                "    }\n" +
                "}\n";
        File javaFile = new File(className + ".java");
        Files.writeString(javaFile.toPath(),context);
        return javaFile;
    }

    private static String getClassName(){
        return "MyInterface$proxy" + count.incrementAndGet();
    }

    private static String functionBody(String methodName){
        return "System.out.println(\"" + methodName + "\");";
    }

    //通过类名加载类到jvm中 创建实例并返回
    private static MyInterface newInstance(String className) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Class<?> aClass = MyInterface.class.getClassLoader().loadClass(className);
        Constructor<?> constructor = aClass.getConstructor();
        MyInterface proxy = (MyInterface) constructor.newInstance();
        return proxy;
    }

    public static MyInterface creatProxyObject() throws Exception {
        String className = getClassName();
        File javaFile = creatJavaFile(className);
        Compiler.compile(javaFile);
        return newInstance("tech.instight.proxy." + className);
    }
}

需要一个新的接口MyHandler用来传入的方法名。

package tech.instight.proxy;

public interface MyHandler {

    //传入方法名,返回方法体,实现动态
    String functionBody(String methodName);
}

实际使用时是:

package tech.instight.proxy;

import java.lang.reflect.Field;

public class Main {
    public static void main(String[] args) throws Exception {
        MyInterface proxyObject = MyInterfaceFactory.creatProxyObject(new PrintFunctionName());
        proxyObject.func1();
        proxyObject.func2();
        proxyObject.func3();
        System.out.println("-------------");
    }

    //打印当前函数名
    static class PrintFunctionName implements MyHandler{
        @Override
        public String functionBody(String methodName) {
            return "System.out.println(\"" + methodName + "\");";
        }
    }
}

但是我们的方法还是写死的,怎么让他能够动态的生成: 先修改下MyInterfaceFactory,通过MyHandler的新方法setProxy传入MyInterface,利用反射机制 向某个动态生成的类实例中注入一个字段值。就是把生成的代理对象自己“塞”进它的 myInterface 字段中,这样就能实现链式的动态代理了。

package tech.instight.proxy;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.util.concurrent.atomic.AtomicInteger;

public class MyInterfaceFactory {
    private static final AtomicInteger count = new AtomicInteger();
    private static File creatJavaFile(String className,MyHandler handler) throws IOException {
        String func1Body = handler.functionBody("func1");
        String func2Body = handler.functionBody("func2");
        String func3Body = handler.functionBody("func3");
        String context = "package tech.instight.proxy;\n" +
                "\n" +
                "public class "+ className + "  implements MyInterface{\n" +
                "    MyInterface myInterface;\n" +
                "    @Override\n" +
                "    public void func1() {\n" +
                "        " + func1Body + "\n" +
                "    }\n" +
                "\n" +
                "    @Override\n" +
                "    public void func2() {\n" +
                "        " + func2Body + "\n" +
                "    }\n" +
                "\n" +
                "    @Override\n" +
                "    public void func3() {\n" +
                "        " + func3Body + "\n" +
                "    }\n" +
                "}\n";

        File javaFile = new File(className + ".java");
        Files.writeString(javaFile.toPath(), context);
        return javaFile;
    }

    private static String getClassName(){
        return "MyInterface$proxy" + count.incrementAndGet();
    }

    //通过类名加载类到jvm中 创建实例并返回
    private static MyInterface newInstance(String className,MyHandler myHandler) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Class<?> aClass = MyInterface.class.getClassLoader().loadClass(className);
        Constructor<?> constructor = aClass.getConstructor();
        MyInterface proxy = (MyInterface) constructor.newInstance();
        //加入代理对象实例
        myHandler.setProxy(proxy);
        return proxy;
    }

    public static MyInterface creatProxyObject(MyHandler myHandler) throws Exception {
        String className = getClassName();
        File javaFile  = creatJavaFile(className,myHandler);
        Compiler.compile(javaFile);
        return newInstance("tech.instight.proxy." + className, myHandler);
    }
}

修改的MyHandler:

package tech.instight.proxy;

public interface MyHandler {

    //传入方法名,返回方法体,实现动态
    String functionBody(String methodName);

    default void setProxy(MyInterface proxy){};
}

实际使用:

package tech.instight.proxy;

import java.lang.reflect.Field;

public class Main {
    public static void main(String[] args) throws Exception {
        MyInterface proxyObject = MyInterfaceFactory.creatProxyObject(new PrintFunctionName());
        proxyObject.func1();
        proxyObject.func2();
        proxyObject.func3();
        System.out.println("-------------");
        //使用LogHandler包裹PrintFunctionName
        proxyObject = MyInterfaceFactory.creatProxyObject(new LogHandler(proxyObject));
        proxyObject.func1();
        proxyObject.func2();
        proxyObject.func3();
    }

    //打印当前函数名
    static class PrintFunctionName implements MyHandler{
        @Override
        public String functionBody(String methodName) {
            return "System.out.println(\"" + methodName + "\");";
        }
    }

    //动态代理类
    static class LogHandler implements MyHandler{
        //想要被代理的类
        MyInterface myInterface;

        public LogHandler(MyInterface myInterface) {
            this.myInterface = myInterface;
        }

        //使用反射赋值,把要被代理的对象加入其中
        @Override
        public void setProxy(MyInterface proxy) {
            Field filed = null;
            try {
                //获取代理类中名为 "myInterface" 的字段
                filed = proxy.getClass().getDeclaredField("myInterface");
                //允许访问私有字段:
                filed.setAccessible(true);
                //把 handler 中的 myInterface 值注入进代理对象里:
                filed.set(proxy,myInterface);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        @Override
        public String functionBody(String methodName) {
            return "    System.out.println(\"before\");\n" +
                    "            myInterface."+ methodName + "();\n" +
                    "            System.out.println(\"after\");";
        }
    }
}

动态代理demo

我们要实现有一个UserService接口,一个 UserServiceImpl 真实实现类,通过动态代理创建一个带日志功能的代理对象所有方法调用都会自动加上日志打印的例子: 定义接口

public interface UserService {
    void register(String username);
    void login(String username);
}

真是实现类(目标对象)

public class UserServiceImpl implements UserService {
    @Override
    public void register(String username) {
        System.out.println(username + " 注册成功!");
    }

    @Override
    public void login(String username) {
        System.out.println(username + " 登录成功!");
    }
}

代理处理器(InvocationHandler)

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class LogInvocationHandler implements InvocationHandler {

    private final Object target; // 被代理对象

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

    // 每次调用代理对象的方法,都会执行这个方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("[日志] 调用方法:" + method.getName());
        Object result = method.invoke(target, args); // 调用目标对象的方法
        System.out.println("[日志] 方法执行完毕:" + method.getName());
        return result;
    }
}

创建代理并测试:

import java.lang.reflect.Proxy;

public class Main {
    public static void main(String[] args) {
        UserService realService = new UserServiceImpl(); // 原始对象

        // 创建代理对象
        UserService proxy = (UserService) Proxy.newProxyInstance(
            UserService.class.getClassLoader(),           // 类加载器
            new Class[]{UserService.class},               // 实现的接口
            new LogInvocationHandler(realService)         // 调用处理器
        );

        // 调用代理方法
        proxy.register("Alice");
        proxy.login("Bob");
    }
}

输出:

[日志] 调用方法:register
Alice 注册成功!
[日志] 方法执行完毕:register

[日志] 调用方法:login
Bob 登录成功!
[日志] 方法执行完毕:login