mybatis 自动化处理 mysql 的json类型字段 终极方案

news2024/11/26 19:52:31

文章目录

  • mybatis 自动化处理 mysql 的json类型字段 终极方案
    • mysql 建表 json 字段,添加1条json 数据
    • 对应的java对象 `JsonEntity`
    • mybatis,不使用 通用mapper
      • 手动自定义1个类型处理器,专门处理 JsonNode 和Json 的互相转化
        • 将 自定义的类型处理器 加入到 mybatis 核心配置,不用 xml
        • @Repository中sql查询 jdbc 的 json 字段,自动映射为 java 类型
        • 源码中的关键代码点:mybatis 如何映射json结果到 java对象
    • mybatis,使用 通用mapper
    • 最终效果展示 ,增删改查测试 代码示例 :
      • 查询并显示 json
      • 直接更新json
    • 源代码下载
    • 参考文档

mybatis 自动化处理 mysql 的json类型字段 终极方案

本文基于原生的 mybatis ,而不是 mybatis-plus ,请知悉。
目标1-查询:查询数据库的json字段,转换为java的json对象,并优雅的返回前端
目标2-更新:识别前端的请求参数,转换为 数据库的 Json 字段 ,比如新增/更新
目标3-注解:不使用 xml增加 typeHandler,而是 使用注解方式
目标4-智能:不在sql中的字段上指定 typeHandler, 不要每次都手写,要 自动化识别

  • 官网教程 ✔ 大而全,但是代码例子太少,有的地方1遍看不明白
  • 腾讯教程 ❌可 行,但是不够自动化,不优雅
  • 源码分析 ❓ 越看越懵,而且最后的而解决方案跟腾讯的一样,不优雅

mysql 建表 json 字段,添加1条json 数据

-- 建表 json 字段,添加1条json 数据
create table t_test_json(id int primary key auto_increment,json_field JSON  default null);
insert into t_test_json( json_field) values ('{"hello":"world"}');

对应的java对象 JsonEntity

@Table(name="t_test_json")
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class JsonEntity{
    @Id
    private Integer id;
    // 为何不是 ArrayNode 或者 ObjectNode ? 
    // 因为 JsonNode 是他们俩的父类,可以自动兼容2种格式的json : [{},{}] 和 {} 
    private JsonNode jsonField;
    
    @SneakyThrows
    @Override
    public String toString() {
        return JacksonUtils.writeValueAsString(this);
    }
}

mybatis,不使用 通用mapper

  • 因为 javaType JsonNode 和 mysql 的json类型字段,都不是 mybatis 默认能互相转化处理的,所以需要 手动创建 类型处理器
    在这里插入图片描述

手动自定义1个类型处理器,专门处理 JsonNode 和Json 的互相转化

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
 * includeNullJdbcType 在 Mybatis 3.4.0 开始 默认为true。
 * 想让mybatis 自动化处理映射关系,则必须保证 includeNullJdbcType =true,
 * 因为如果只是设置了  @MappedJdbcTypes(value = JdbcType.VARCHAR ) 则该处理器就无法自动处理 JdbcType是json 的情况。
 * 实际上,根据官方文档,mybatis 是把所有的返回值都当作 JdbcType = null 来自动 选择类型处理器的
 * 如果includeNullJdbcType =false,则必须在 sql中返回的字段上 明确标注 typeHandler= xxx.class 
 * 
 * @MappedJdbcTypes的value 设置为 JdbcType.LONGVARCHAR 或者 JdbcType.LONGVARCHAR 都可以。
 * 建议JdbcType.LONGVARCHAR,据测试,json 类型的返回结果的JdbcType = LONGVARCHAR 
 * 
 * @ColumnType when the reult is ResultMap
 */
// @MappedTypes(JsonNode.class) // 因为BaseTypeHandler<JsonNode> 泛型中指定了JsonNode 的话,这个注解也可以省略 
@MappedJdbcTypes(value = JdbcType.VARCHAR, includeNullJdbcType = true)
@Component
public class JsonNodeTypeHandler extends BaseTypeHandler<JsonNode> implements InitializingBean {
    static JsonNodeTypeHandler j;
    @Autowired
    ObjectMapper objectMapper;

    /**
     * 魔法 注入 单例bean objectMapper; 
     * 在 @Controller 中注入ObjectMapper 不需要这么麻烦,直接 @Autowired 即可 。
     * 非Controller 注入原理:spring 启动过程中 实例化JsonNodeTypeHandler 的 bean 时,会自动把 objectMapper 携带过来;
     * spring 启动完成后的bean 又会被擦除 。所以,这个要及时赋值一下引用 objectMapper
     */
    @Override
    public void afterPropertiesSet() {
        j = this; // 初始化静态实例
        j.objectMapper = this.objectMapper; //及时拷贝引用
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, JsonNode jsonNode, JdbcType jdbcType) throws SQLException {
        ps.setString(i, jsonNode != null ? jsonNode.toString() : null);
    }

    @SneakyThrows
    @Override
    public JsonNode getNullableResult(ResultSet rs, String colName) {
        return read(rs.getString(colName));
    }

    @SneakyThrows
    @Override
    public JsonNode getNullableResult(ResultSet rs, int colIndex) {
        return read(rs.getString(colIndex));
    }

    @SneakyThrows
    @Override
    public JsonNode getNullableResult(CallableStatement cs, int i) {
        return read(cs.getString(i));
    }

    @SneakyThrows
    private JsonNode read(String json) {
        return json != null ? j.objectMapper.readTree(json) : null;
    }
}

将 自定义的类型处理器 加入到 mybatis 核心配置,不用 xml

public static SqlSessionFactory getSqlSessionFactory(DataSource dataSource, String javaEntityPath, String xmlMapperLocation) throws Exception {
    SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
    factoryBean.setDataSource(dataSource);
    factoryBean.setTypeAliasesPackage(javaEntityPath);

    //mybatis configuration
    org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
    // 下划线转驼峰
    configuration.setMapUnderscoreToCamelCase(true);
    // 返回Map类型时,数据库为空的字段也要返回  https://www.cnblogs.com/guo-xu/p/12548949.html
    configuration.setCallSettersOnNulls(true);
    // 配置 拦截器 打印 sql : TODO 补充 拦截器实现代码
    // configuration.addInterceptor(new PrintMybatisSqlInterceptor());
    factoryBean.setConfiguration(configuration);

    ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
    factoryBean.setMapperLocations(resolver.getResources(xmlMapperLocation));

    // 自定义的类型处理器: 自动双向解析 JsonNode 类型 和 mysql中的 json; 千万别写xml 了,太low 
    factoryBean.setTypeHandlers(new JsonNodeTypeHandler());
    return factoryBean.getObject();
}

/**
 * 每个类型的数据库连接,都需要单独创建1个SqlSessionFactory
 * 比如mysql DataSource 需要 配置1个 @Bean SqlSessionFactory
 * oracle 或者 hive 或者其他的数据库,都需要单独配置自己的 @Bean SqlSessionFactory 
 * 但是 ,getSqlSessionFactory() 这个静态方法是共用的,只需要修改对应entity和xml文件地址的参数 javaEntityPath 和 xmlMapperLocation 即可 
 */
@Bean
public SqlSessionFactory yourSqlSessionFactory(DataSource yourDataSource) throws Exception {
    return getSqlSessionFactory(yourDataSource,"com.server.model.entity.testpath","classpath:mapper/testpath/*.xml");
}

@Repository中sql查询 jdbc 的 json 字段,自动映射为 java 类型

@Select(" SELECT * from t_test_json where JSON_CONTAINS(json_field, #{vo.jsonField}) limit 1 ")
List<JsonEntity> testQueryJson(@Param("vo") JsonEntity vo);

源码中的关键代码点:mybatis 如何映射json结果到 java对象

  • 查询到结果ResultSet ,去交给DefaultResultSetHandler类的createAutomaticMappings方法去处理映射关系

    /**
     * 查询到结果ResultSet ,去交给DefaultResultSetHandler类的createAutomaticMappings方法去处理映射关系 
     * @Param metaObject MetaObject#findProperty() 能把 rs 结果中columnName 的下划线去除,对应到java对象 JsonEntity 的属性名 
     * @Param rsw final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
     */
    private List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix)
    

    在这里插入图片描述

    • 这个 createAutomaticMappings 方法,内部主要干了几件事

      • 找出resultset结果集中那些无法用 mybatis内置的类型处理器映射的字段名,比如json类型的 json_field

      • json_field改为驼峰格式jsonField,反射查找到Java对象中该属性为 private JsonNode jsonField;

        public String findProperty(String name, boolean useCamelCaseMapping) {
            if (useCamelCaseMapping) {
              name = name.replace("_", "");
            }
            return findProperty(name);
        }
        
      • 再根据 JsonNode 去查找已注册的类型处理器,就定位到 我们手动 自定义的类型处理器 JsonNodeTypeHandler
        在这里插入图片描述

        • 从源码能看出,json 字段对应的jdbcType 其实是 jdbcType.LONGVARCHAR ,
          假如我们设置 @MappedJdbcTypes(value = JdbcType.VARCHAR, includeNullJdbcType = true),
          则经过一些简单的 null 判断,最后依然可以定位到手写的这个 JsonNodeTypehandler 。
          最关键的就是:一定要保证 includeNullJdbcType = true,防止 JdbcType 手动设置错误导致 定位失败
    • 接下来,就是 执行 自定义类型处理器的 方法 typeHandler.getResult(ResultSet rs, String colName) 获取到值了

mybatis,使用 通用mapper

使用 通用mapper,可以少写很多单表操作的sql ,增删改查,单表操作非常方便

  • pom
    <dependency>
        <groupId>tk.mybatis</groupId>
        <artifactId>mapper</artifactId>
        <version>${mapper.version}</version><!-- 4.0.4  -->
    </dependency>
    
  • 与手写sql 处理 json 字段的最大的不同点 :需要注解@ColumnType 明确标注出来 json 字段
  • 并且 JsonNodeTypeHandler 中不能有未知类型的泛型 (比如T),必须是 确定的已知的java类型 (比如 JsonNode)
import tk.mybatis.mapper.annotation.ColumnType;

@Table(name="t_test_json")
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class JsonEntity{
    @Id
    private Integer id;
    // 多了这个ColumnType,通用mapper生成sql必须的;如果没有该注解,则最终生成sql时 该JsonNode类型的字段将会被忽略
    // 如果你没有使用 通用mapper,而是完全手写sql,那么完全没必要加该注解,mybatis的自动发现足咦!!
    @ColumnType(typeHandler = JsonNodeTypeHandler.class) 
    private JsonNode jsonField;
    
    @SneakyThrows
    @Override
    public String toString() {
        return JacksonUtils.writeValueAsString(this);
    }
}

最终效果展示 ,增删改查测试 代码示例 :

查询并显示 json

  • controller 层
    @AutoWired
    IDao dao;
    @ApiOperation(value = "查询 自动转换 JsonNode 和 json 类型,自动发现 typeHandler ")
    @PostMapping("test/json")
    public ResultBean testJson(@RequestBody(required = false) JsonEntity vo) {
        return ResultUtils.oK(dao.select(vo));
    }
    
  • swagger 或者 postman 发起请求 json 格式
    • 请求 1: 查询 全量 json 结果
      curl -X POST "http://localhost:8080/api/test/json" -H "accept: */*" -H "Content-Type: application/json" -d "{}"

    • 请求 2: 根据 id 查询 json 结果,-d “{}” 里加参数即可
      curl -X POST "http://localhost:8080/api/test/json" -H "accept: */*" -H "Content-Type: application/json" -d "{ id:1}"

      {id:1}
      

      以上 2种情况,dao 层都使用 通用mapper 生成sql即可。
      都能正常被 @RequestBody(required = false) JsonEntity vo 识别 并且 生成JsonEntity 对象,return 正常的JsonEntity 结果
      在这里插入图片描述

    • 请求 3: 根据 JsonNode 字段 查询 JsonEntity 结果
      curl -X POST "http://localhost:8034/api/test/json" -H "accept: */*" -H "Content-Type: application/json" -d "{ jsonField:{\"hello\":\"world\"}}"

      • 通用mapper 自动生成sql 的where 条件为 where json_field = {"hello":"world"} ,查询结果为空 ❌。
      • 放弃通用mapper,手动 自定义sql ,使用 函数 JSON_CONTAINS 去匹配json ,ok ✔
        /**
       * 直接 查询 json 字段
       * 无法使用 通用mapper 生成的sql, 必须 手写 sql判断 json 是否存在的 JSON_CONTAINS 语法:  
       * where JSON_CONTAINS(json, #{vo.jsonField})
       * 最终生成的sql 是 
       * select * from  t_test_json where JSON_CONTAINS(json_field,'{"hello": "world"}')
       * 如此,才能让 mysql 正常查询json 字段和返回结果 
       * 参考 mysql 正确比较 2个json 字段 的写法 https://blog.csdn.net/weixin_39926042/article/details/118812599
       */
      @Select(" select * from  t_test_json where JSON_CONTAINS(json_field, #{vo.jsonField}) ")
      List<JsonEntity> selectByJson(@Param("vo") JsonEntity vo);
      

      -

  • 直接查询json 字段,没问题,接下来,看下 如何直接 更新 json 字段

直接更新json

  • swagger 或者 postaman 请求 json 格式参数
    curl -X POST "http://localhost:8034/api/test/json/update" -H "accept: */*" -H "Content-Type: application/json" -d "{\"id\": 1, \"jsonField\": {\"hello\":\"world again!\"}}"

    {
      "id": 1,
      "jsonField": {"hello":"world again!"}
    }
    
  • controlller 层

    @ApiOperation(value = "更新 json 字段,自动生成sql或者手写sql 均可,都无需指定 typehandler 属性 ")
    @PostMapping("/json/update")
    public ResultBean testUpdateJson(@RequestBody JsonEntity vo) {
        final IJsonDao dao = SpringContextUtils.getBean(IJsonDao.class);
        // 通用mapper生成 sql UPDATE T_TEST_JSON SET id = id,json_field = {"hello":"world again!"} WHERE id = 1 ;
        final int i = dao.updateByPrimaryKeySelective(vo);
        // 手动写 sql UPDATE t_test_json SET json_field = {"hello":"world again!"} where id = 1 ;
        return DFResultUtils.oK(dao.testUpdateJson((vo)));
    }
    

    -

  • 通用mapper Dao 的写法

    @Repository("jsonDao")
    public interface IJsonDao extends MyMapper<JsonEntity> {
        @Transactional(value = JK.transactionManager, rollbackFor = Exception.class)
        @Select(" UPDATE t_test_json SET json_field = #{vo.jsonField} where id  = #{vo.id}")
        List<Integer> testUpdateJson(@Param("vo") JsonEntity vo);
    }
    /** MyMapper 通用mapper类 写法 
    public interface MyMapper<T>
        extends
        BaseMapper<T>,
        ExampleMapper<T>,
        ConditionMapper<T>,
        MySqlMapper<T> {
    }*/
    
  • 更新后结果
    -

源代码下载

  • 下载地址 https://download.csdn.net/download/w1047667241/86927373在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

参考文档

  1. mysql 正确比较 2个json 字段 的写法: JSON_CONTAINS(json_field, #{vo.jsonField})

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/4187.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java笔记(十二)

文献种类&#xff1a;专题技术总结文献 开发工具与关键技术&#xff1a; IntelliJ IDEA、Java 语言 作者&#xff1a; 方建恒 年级&#xff1a; 2020 撰写时间&#xff1a; 2022 年 11 月 8 日 Java笔记(十二) 今天我给大家继续分享一下我的Java笔记&#xff0c; 我们继续来了…

使用前缀和数组解决“区间和查询“问题

本文已收录到 GitHub AndroidFamily&#xff0c;有 Android 进阶知识体系&#xff0c;欢迎 Star。技术和职场问题&#xff0c;请关注公众号 [彭旭锐] 进 Android 面试交流群。 前言 大家好&#xff0c;我是小彭。 今天分享到一种非常有趣的数据结构 —— 前缀和数组。前缀和…

每日一题|2022-11-8|1684. 统计一致字符串的数目|哈希表|Golang

1684. 统计一致字符串的数目 思路1:丢人做法 哈希记录allowed&#xff0c;暴力遍历words所有字母&#xff0c;如果有不在哈希表里的&#xff0c;计数。最后用words的长度减去 计数 就行。 func countConsistentStrings(allowed string, words []string) int {has1 : make(map[…

如何判断一段程序是否是裸机程序?

在嵌入式MCU领域&#xff0c;一般将不移植操作系统直接烧录运行的程序称为裸机程序。 一般来说&#xff0c;非易失性存储&#xff0c;时钟&#xff0c;图形显示&#xff0c;网络通讯&#xff0c;用户I/O设备…都需要硬件依赖。 基于硬件基础&#xff0c;内存管理、文件系统、…

【API部署】fastapi与nuitka打包py项目

提示&#xff1a;分两部分&#xff1a;fastapi接口调用&#xff0c;与nuitka快速打包 功能&#xff1a;作为一名算法工程师&#xff0c;训练机器学习模型只是为客户提供解决方案的一部分。 除了生成和清理数据、选择和调整算法之外&#xff0c;还需交付和部署结果&#xff0c;…

130道基础OJ编程题之: 29 ~ 38 道

130道基础OJ编程题之: 29 ~ 38 道 文章目录130道基础OJ编程题之: 29 ~ 38 道0. 昔日OJ编程题:29. BC23 时间转换30. BC24 总成绩和平均分计算31. BC30 KiKi和酸奶32. BC31 发布信息33. BC3 输出学生信息34. BC33 计算平均成绩35. BC34 进制AB36. BC37 网购37.BC39 争夺前五名38…

【谷粒商城】

一、项目介绍 1.微服务架构图 2.微服务划分图 二、环境搭建 1.虚拟机搭建环境 这里我买了华为云&#xff0c;没用虚拟机 华为云配置 2.Linux 安装docker docker文档&#xff1a;https://docs.docker.com/engine/install/centos/ # 1. 卸载之前的dockersudo yum remove d…

[MySql]初识数据库与常见基本操作

专栏简介 :MySql数据库从入门到进阶. 题目来源:leetcode,牛客,剑指offer. 创作目标:记录学习MySql学习历程 希望在提升自己的同时,帮助他人,,与大家一起共同进步,互相成长. 学历代表过去,能力代表现在,学习能力代表未来! 文章目录 前言 1.初识数据库 1.1 数据库概述 1.2 数据库…

mysql隔离级别RR下的行锁、临键锁、间隙锁详解及运用

一&#xff1a;mysql 锁的基本概念 锁&#xff1a;悲观锁、乐观锁 悲观锁&#xff1a;写锁 for update、读锁for share 写锁&#xff1a;只允许当前事务读写&#xff0c;其它事务全部等待&#xff0c;包括读取数据&#xff0c;锁的数据范围需要具体分析 读锁&#xff1a;允…

【前端】Vue+Element UI案例:通用后台管理系统-Echarts图表:折线图、柱状图、饼状图

文章目录目标代码数据改写为动态Echarts引入与html结构折线图&#xff1a;orderData柱状图&#xff1a;userData饼状图&#xff1a;videoData总效果总代码:Home.vue上一篇&#xff1a;【前端】VueElement UI案例&#xff1a;通用后台管理系统-Echarts图表准备&#xff1a;axios…

公司缺人自己搞了vue又搞koa,熬夜把架子搭起来

如果有一天&#xff0c;人手紧缺&#xff0c;自己搞了前端还要搞服务端&#xff0c;今天我们把这个项目架子搭起来&#xff0c;让前端同学也可以轻松全栈开火。 技多不压身&#xff0c;活儿多了可压身啊 目录 一、上午写VUE 1、 新建一个我们的伟大项目文件夹 2、用vscode打…

程序中断方式

中断的基本概念 程序中断是指在计算机执行现行程序的过程中&#xff0c;出现某些急需处理的异常情况或特殊请求&#xff0c;CPU暂时中止现行程序&#xff0c;而转去对这些异常情况或特殊请求进行处理&#xff0c;在处理完毕后CPU又自动返回到现行程序的断点处&#xff0c;继续…

c语言之“数组”初级篇

前言 牛牛又和大家见面了&#xff0c;本篇牛牛要讲的内容是c语言中有关数组的内容。 欢迎大家一起学习&#xff0c;共同进步。 目录前言数组一、一维数组1.1 一维数组的创建1.2 一维数组的初始化1.3 一维数组的应用1.4 一维数组的存储二、二维数组2.1 二维数组创建2.2 二维数…

MySQL的select语句

SQL概述 SQL背景知识 1946 年&#xff0c;世界上第一台电脑诞生&#xff0c;如今&#xff0c;借由这台电脑发展起来的互联网已经自成江湖。在这几十年里&#xff0c;无数的技术、产业在这片江湖里沉浮&#xff0c;有的方兴未艾&#xff0c;有的已经几幕兴衰。但在这片浩荡的波…

基于android的车辆违章停放执法移动APP(ssm+uinapp+Mysql)-计算机毕业设计

车辆违章停放执法移动APP的功能已基本实现&#xff0c;主要实现首页&#xff0c;个人中心&#xff0c;市民管理&#xff0c;警察管理&#xff0c;罚单信息管理&#xff0c;缴费通知管理&#xff0c;系统管理等功能的操作系统。 论文主要从系统的分析与设计、数据库设计和系统的…

【机器学习】回归的原理学习与葡萄酒数据集的最小二乘法线性回归实例

文章目录一&#xff0c;回归1.1回归分析的基本概念1.2线性回归1.3最小二乘法1.4一元(简单)线性回归模型1.4.1随机误差项(线性回归模型)的假定条件1.4.2参数的普通最小二乘估计(0LS)1.5葡萄酒数据集的最小二乘法线性回归实例一&#xff0c;回归 1.1回归分析的基本概念 回归分析…

前端一面经典vue面试题总结

一般在哪个生命周期请求异步数据 我们可以在钩子函数 created、beforeMount、mounted 中进行调用&#xff0c;因为在这三个钩子函数中&#xff0c;data 已经创建&#xff0c;可以将服务端端返回的数据进行赋值。 ​ 推荐在 created 钩子函数中调用异步请求&#xff0c;因为在…

受激拉曼散射计量【Stimulated-Raman-Scattering Metrology】(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️❤️&#x1f4a5;&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑…

单元测试的时候读不到resources.test中配置

背景 接手了几个老工程&#xff0c;跑单元测试的时候&#xff0c;发现数据库的配置总是走了dev环境&#xff0c; 原因是工程中分环境进行了db的配置 历史经验 指定本地环境 ActiveProfiles(“test”) 没有生效 解决 在pom文件中 新加如下配置 <build><!--单元测…

Java—类加载机制

类加载机制 我们多次提到了类加载器ClassLoader&#xff0c;本章就来详细讨论Java中的类加载机制与ClassLoader。 类加载器ClassLoader就是加载其他类的类&#xff0c;它负责将字节码文件加载到内存&#xff0c;创建Class对象。与之前介绍的反射、注解和动态代理一样&#xf…