当前位置: 首页 > news >正文

动态类生成 / 加载机制(更新)

前言

一般的编译是编译器编译.java文件  生成.class文件  然后JVM加载并运行.class字节码文件

反射的前提是内存中已经有了Class<T>对象  或者 通过Class.forName等方法去加载静态的.class文件  最终还是需要Class对象

那么动态生成Class对象的几种技术  JDK代理  CGLIB代理  ASM  ByteBuddy Javassist 这几种技术都是内存中生成Class对象 默认都不会有.class静态文件(待确认)  其中JDK代理和CBLIB代理是基于目标接口/父类需要以某种形式被JVM加载(例如已有的.class文件进行代理 或者 目标接口/父类 已经在内存中生成)  然后通过反射调用

反射的前提是需要内存中有Class对象  不管这个Class对象是通过静态加载 或动态加载

JDK、CGLIB、ASM、ByteBuddy、Javassist 最终都可以动态在内存中生成Class对象 利用反射完成功能 

反射是一种操作类,对象,方法的方式   但是内存中已经有了Class对象  不一定非要用反射   也可以直接new 


CGLIB  是基于ASM


ByteBuddy 也是基于ASM   因为JDK代理和CGLIB代理基于目标接口/父类 已经在内存中生成     那么就是ByteBuddy和Javassist  是正在完全动态

JDK 动态代理

目标对象必须实现接口

1.定义接口

public interface UserService {
    void doSomething();
}

目标类

public class UserServiceImpl implements UserService {
    @Override
    public void doSomething() {
        System.out.println("执行核心业务逻辑...");
    }
}
 

以匿名内部类的写法

package com.hrui.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

/**
 * @author hrui
 * @date 2025/4/10 11:03
 */
public class JdkProxyDemo {
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();
        UserService proxyInstance = (UserService) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),       // 类加载器
                new Class[]{UserService.class},           // 接口列表
                (proxy, method, args1) -> {
                    System.out.println("before");
                    Object invoke = method.invoke(target, args1);
                    System.out.println("after");
                    return invoke;
                }//调用处理器 可以以匿名内部类的写法   也可以以单独写一个类实现InvocationHandler接口
        );
        proxyInstance.doSomething();
    }
}

创建代理处理器的写法(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 JdkProxyDemo {
    public static void main(String[] args) {
        // 创建目标对象
        UserService target = new UserServiceImpl();

        // 创建代理对象
        UserService proxyInstance = (UserService) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),       // 类加载器
                new Class[]{UserService.class},           // 接口列表
                new LogInvocationHandler(target)          // 调用处理器
        );

        // 调用方法(会被代理拦截)
        proxyInstance.doSomething();
    }
}
 

可以看到并没有生成静态的.class文件

CGLIB 动态代理

代理对象会继承目标类,不需要目标类实现某接口,目标类不能是final修饰的

引入依赖

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
 

被代理的目标类

public class UserService {
    public void doSomething() {
        System.out.println("执行业务逻辑");
    }
}
 

创建方法拦截器

package com.hrui.cglib;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * CGLIB 动态代理处理器(基于继承方式实现)
 * 用于在调用目标类方法前后插入自定义逻辑(如日志记录)
 *
 * 原理:CGLIB 通过生成目标类的子类,重写方法插入拦截逻辑(类似 AOP)
 *
 * 注意事项:
 * - 被代理的类不能是 final 类
 * - 被代理的方法不能是 final 或 static
 * - JDK 16+ 需要 JVM 参数开放模块:--add-opens java.base/java.lang=ALL-UNNAMED
 *
 * 示例用法:
 *   UserService proxy = (UserService) new LogHandler().getInstance(UserService.class);
 *   proxy.doSomething();
 *
 * 输出结果:
 *   之前
 *   执行业务
 *   之后
 * 
 * @author hrui
 * @date 2025/4/10 16:03
 */
public class LogHandler implements MethodInterceptor {

    /**
     * 创建代理对象
     *
     * @param clazz 目标类的 Class 对象
     * @return 被代理后的实例对象
     */
    public Object getInstance(Class<?> clazz) {
        Enhancer enhancer = new Enhancer();             // 创建增强器
        enhancer.setSuperclass(clazz);                  // 设置父类(被代理的目标类)
        enhancer.setCallback(this);                     // 设置方法拦截器回调
        return enhancer.create();                       // 创建代理对象
    }

    /**
     * 拦截方法逻辑(类似 AOP)
     *
     * @param obj           代理对象
     * @param method        被调用的方法
     * @param args          方法参数
     * @param proxy         方法代理对象(用于调用原始方法)
     * @return 方法返回值(必须返回原值,否则会丢失结果)
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        before();                                        // 调用前增强
        Object result = proxy.invokeSuper(obj, args);   // 调用原始方法(非反射!)
        after();                                         // 调用后增强
        return result;
    }

    private void before() {
        System.out.println("之前");
    }

    private void after() {
        System.out.println("之后");
    }
}

调用

package com.hrui.cglib;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
 * @author hrui
 * @date 2025/4/10 15:59
 */
public class CglibDemo {
    public static void main(String[] args) {
        UserService proxy = (UserService) new LogHandler().getInstance(UserService.class);
        proxy.doSomething();
    }
}

注意 JDK17中

因为 CGLIB 使用反射访问了 java.lang.ClassLoader.defineClass(...) 方法,而这个方法从 JDK 16+ 开始默认被封闭需要手动开放访问权限。 

需要添加JVM参数    

--add-opens java.base/java.lang=ALL-UNNAMED

匿名内部类方式

package com.hrui.cglib;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
 * @author hrui
 * @date 2025/4/10 15:59
 */
public class CglibDemo {
//    public static void main(String[] args) {
//        UserService proxy = (UserService) new LogHandler().getInstance(UserService.class);
//        proxy.doSomething();
//    }


    //用匿名内部类方式
    public static void main(String[] args) {
        // 目标类
        class UserService {
            public void sayHello() {
                System.out.println("执行业务逻辑");
            }
        }

        // 创建代理对象(使用匿名内部类作为拦截器)
        UserService proxy = (UserService) Enhancer.create(
                UserService.class, // 设置父类
                new MethodInterceptor() {
                    @Override
                    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxyMethod) throws Throwable {
                        System.out.println("之前");
                        Object result = proxyMethod.invokeSuper(obj, args); // 调用原始方法
                        System.out.println("之后");
                        return result;
                    }
                });

        proxy.sayHello(); // 调用代理方法
    }
}

ByteBuddy

ByteBuddy 不仅仅能用来创建代理,它的能力远远超过“代理”——它是一个通用的 Java 字节码操作和生成框架。

  • 基于 ASM 构建的字节码生成工具

  • 支持动态创建类、增强已有类、方法拦截、注解处理等

  • API 语义化、链式调用、可读性强

  • 可用于运行时 / 构建期(AOT)

添加依赖

<dependency>
    <groupId>net.bytebuddy</groupId>
    <artifactId>byte-buddy</artifactId>
    <version>1.14.11</version>
</dependency>

Javassist

ASM

http://www.dtcms.com/a/123053.html

相关文章:

  • Java集合框架:核心接口与关系全解析
  • springboot中测试python脚本:ProcessBuilder
  • 如何使用 PyTorch 和 EfficientNet 创建一个 AI 游戏外挂[特殊字符]
  • 基于MCP协议调用的大模型agent开发03
  • InnoDB 事务更新过程深度剖析:原理、流程与优化
  • 前端-vue2核心
  • 如何用postman做接口自动化测试?
  • Seata框架,如何保证事务的隔离性?
  • 【时时三省】(C语言基础)用switch语句实现多分支选择结构
  • 【运维 | 硬件】服务器中常见的存储插槽类型、对应的传输协议及其特性总结
  • 环信鸿蒙版 UIKit 快速上手指南
  • 如何将多个Word文档合并
  • 【HTML】动态背景效果前端页面
  • 基于 Qt4 的图片处理工具开发(二):增加对比度调节、界面布局优化、多线程操作
  • 初识Redis · 简单理解Redis
  • 使用注解@RequestBody变红的解决问题
  • 【spark认任务提交】配置优先级顺序
  • 银河麒麟v10(arm架构)部署Embedding模型bge-m3【简单版本】
  • 【C++进阶】关联容器:multimap类型
  • 学习海康VisionMaster之四边形查找
  • 【达梦数据库】bash: /dev/null: Permission denied
  • 使用stm32cubeide stm32f407 lan8720a freertos lwip 实现tcp客户端、服务端及网络数据转串口数据过程详解
  • SQL 关键字
  • 低硬件资源微调预训练Mamba模型的方法
  • 在 transformers 中,return_tensors=‘pt‘ 里的 pt 是什么 tf,np
  • gdal_shp文件的组成
  • 【设备连接涂鸦阿里云】
  • [数据结构]Trie字典树
  • RocketMQ和kafka 的区别
  • 算法导论(递归回溯)——⼆叉树中的深搜