Skip to content

beike12138/proxy_module

Repository files navigation

这个项目实现了一个基于动态生成 Java 类的代理模式,通过动态生成代理类来增强接口方法的行为。以下是该项目的核心结构和功能总结:


1. 核心目标

  • 实现一个简单的 动态代理机制,在不修改原始接口实现的前提下,通过代理对象对方法调用进行增强。

2. 关键组件说明

MyInterface

  • 定义了被代理的目标接口,包含三个方法:
    • func1()
    • func2()
    • func3()

MyInterfaceFactory

  • 动态创建代理类的核心工厂类。
  • 主要功能包括:
    • createJavaFile:根据传入的 MyHandler 动态生成 .java 文件内容(字符串拼接方式构建源码)。
    • getClassName:为每个代理类生成唯一名称,如 MyInterface$proxy1
    • newInstance:使用类加载器加载编译后的类并创建实例。
    • createProxyObject:对外暴露的方法,用于创建带有增强逻辑的代理对象。

MyHandler

  • 处理方法增强逻辑的接口。
  • 包含两个关键方法:
    • functionBody(String methodName) :返回指定方法名的增强代码体。
    • setProxy(MyInterface proxy) :设置代理对象本身,用于组合/链式调用。

Main.java

  • 示例客户端代码,演示了不同增强策略下的代理行为。
  • 使用了三种不同的 MyHandler 实现:
    • PrintFuctionName:打印方法名。
    • PrintFunctionName1 :打印数字1后再打印方法名。
    • LogHandler :前置打印 "before",调用原方法,后置打印 "after"

3. 动态代理实现流程

  1. 用户通过 MyInterfaceFactory.createProxyObject(handler) 创建代理对象。
  2. 工厂类动态生成 Java 源文件(.java),其内容根据 handler 的 functionBody() 返回值填充方法体。
  3. 源文件被编译为 .class 文件(通过 Compiler.compile(javaFile),虽然未展示具体实现)。
  4. 加载新生成的类,并通过反射创建其实例。
  5. 调用 myHandler.setProxy(proxy) 设置代理对象,支持嵌套代理或组合增强逻辑。
  6. 最终返回增强后的代理对象供调用。

4. 特点与意义

  • 灵活性高:通过不同的 MyHandler 实现可以灵活定制方法调用前后的处理逻辑。
  • 可扩展性强:可以轻松添加新的增强策略,而无需修改已有代码。
  • 模拟AOP思想:初步体现了面向切面编程(AOP)的思想,比如日志、权限控制等横切关注点的解耦。

5. 可能改进点

改进方向 建议
编译工具 Compiler.compile 方法未展示实现,可能依赖外部工具如 javacJavaCompiler API
错误处理 对生成类的错误检查不够完善,如重复类名、语法错误等
性能优化 动态生成类、写磁盘文件、编译过程可能影响性能,可考虑缓存机制

6. 适用场景

  • 需要在运行时动态增强对象行为的场景。
  • 学习 AOP、代理模式、类加载机制等原理的示例项目。
  • 自定义轻量级框架中用于拦截方法调用。

总结

这是一个通过动态生成 Java 类实现代理机制的小型实验性项目,展示了如何通过字符串拼接生成源码、编译、类加载、反射等技术实现方法增强逻辑,具有一定的教学意义和拓展潜力。

笔记

public static MyInterface newInstance(String className)throws Exception{
        // 1. 使用当前类的类加载器加载指定名称的类
        Class<?> aClass=MyInterfaceFactory.class.getClassLoader().loadClass(className);

        // 2. 获取该类的无参构造方法
        Constructor<?> constructor=aClass.getConstructor();

        // 3. 通过反射调用构造方法创建实例,并强制转换为MyInterface接口类型
        MyInterface proxy=(MyInterface)constructor.newInstance();

        return proxy;
        }

代码解析:

  1. 类加载阶段
    loadClass(className) 通过类加载器加载由 createJavaFile() 动态生成的类(如 MyInterface$proxy1 )。若类未被编译或类路径错误,会抛出 ClassNotFoundException

  2. 构造方法获取
    getConstructor() 获取目标类的无参公共构造方法。若生成的类没有显式定义无参构造器,会抛出 NoSuchMethodException

  3. 实例创建与类型转换
    newInstance() 通过反射调用构造方法生成实例,并强制转换为 MyInterface 接口类型。若目标类未正确实现该接口,会抛出 ClassCastException

  4. 异常处理
    方法声明抛出 Exception,需在调用时捕获处理可能的异常(如类加载失败、构造器不可用、实例化异常等)。

关键点:

  • 动态代理核心:此方法用于加载并实例化动态生成的代理类(如 MyInterface$proxy1),是实现动态代理的关键步骤。
  • 依赖编译结果:需确保 createJavaFile() 生成的 .java 文件已通过 Compiler.compile() 成功编译为 .class 文件,否则 loadClass 会失败。
  • 线程安全:使用 AtomicInteger 保证生成的类名唯一,避免多线程下冲突。

典型调用流程:

// 1. 生成Java源文件
File
javaFile = MyInterfaceFactory.createJavaFile();
// 2. 编译源文件为字节码
Compiler.compile(javaFile);
// 3. 加载并实例化生成的类
MyInterface
proxy = MyInterfaceFactory.newInstance("MyInterface$proxy1");

为什么MyInterfaceFactory的类加载器可以加载加载传入的类名对应的Class对象?

原因分析:

  1. 类加载器的继承关系
    MyInterfaceFactory.class.getClassLoader() 返回的是 应用类加载器(AppClassLoader),负责加载以下位置的类:

    • 项目编译后的 target/classes 目录
    • 依赖库(如 jar 文件)

    当动态生成的 MyInterface$proxy1.class 被编译到 target/classes/com/han/ 目录时,应用类加载器会扫描到该类。

  2. 类路径(Classpath)的匹配

    • 生成的类 MyInterface$proxy1 的完整类名为 com.han.MyInterface$proxy1
    • .class 文件路径需符合 Java 规范:target/classes/com/han/MyInterface$proxy1.class
    • 应用类加载器默认扫描 target/classes 目录,因此能定位该类。
  3. 类加载的双亲委派模型
    类加载过程遵循 双亲委派机制

    AppClassLoader(应用类加载器)
        ↓ 委托加载
    ExtClassLoader(扩展类加载器)
        ↓ 委托加载
    BootstrapClassLoader(启动类加载器)
    
    • 若类属于 Java 核心库(如 java.lang.Object),由 BootstrapClassLoader 加载
    • 若类属于扩展库(如 javax.*),由 ExtClassLoader 加载
    • 否则由 AppClassLoader 自己加载

    动态生成的 com.han.MyInterface$proxy1 不属于核心/扩展类库,最终由 AppClassLoader 加载。

  4. 编译与类路径的绑定
    main 方法中调用了 Compiler.compile(javaFile),假设该方法实现了:

    // 伪代码示例
    public static void compile(File javaFile) {
        // 调用 javac 编译 .java 文件到 target/classes 目录
        runCommand("javac -d target/classes " + javaFile.getAbsolutePath());
    }
    

    这确保了编译后的 .class 文件位于类路径下,类加载器可直接访问。


关键结论:

  • 无需自定义类加载器:因为动态生成的类被编译到默认类路径(target/classes),应用类加载器天然支持加载。
  • 依赖正确的编译输出:若 Compiler.compile() 未正确编译或输出路径错误,loadClass() 会抛出 ClassNotFoundException
  • 包名一致性:生成的类必须与文件路径匹配(如 com.han.MyInterface$proxy1 对应 com/han/MyInterface$proxy1.class)。

验证方式:

可在 newInstance 方法中添加调试日志:

System.out.println("类路径: "+System.getProperty("java.class.path"));
        System.out.println("加载器类型: "+MyInterfaceFactory.class.getClassLoader().getClass().getName());

输出示例:

类路径: /path/to/project/target/classes:/path/to/dependency.jar
加载器类型: jdk.internal.loader.ClassLoaders$AppClassLoader

确认类路径和加载器类型符合预期。

About

手写动态代理

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages