Mybatis操作数据库的两种方式:Mapper代理模式

1.Mapper代理模式的特点

程序员没有写接口的子实现——直接获取数据库的数据

因为Mybatis定义了一套规则,对方法进行了实现,程序员只要遵循这套方法就可以直接使用

2.如何实现Mapper代理模式

步骤:

1.创建一个dao接口,在接口中写增删改查的方法

2.创建一个子清单文件,且子清单文件中的命名空间必须namespace="包名.接口名"

3.在子清单文件中:select\insert\update\delete 节点id为接口中的方法名称

        方法的参数对应parameterType,返回值对应resultType

4.在总清单文件引入子清单文件

5.在需要的地方:接口类型 jdk动态代理对象=sqlSqssion.getMapper(接口类型.class)

       返回类型 返回类型对象 jdk动态代理对象.调用目标方法();

程序员没有写接口子实现,就能获得数据库数据

configruation.xml:

 设置类别名,方便书写:

    <typeAliases>
        <!--设置别名-->
        <typeAlias type="org.example.entity.User" alias="User"/>
    </typeAliases>

注意添加子清单文件 

 <mapper resource="mapper/userMapper.xml"/>

增删改实现 

xxMapper.java

    public int addUser(User user);
    public int deleteUser(Integer id);
    public int updateUser(User user);

userMapper.xml

<mapper namespace="org.example.dao.UserMapper">
    <!--插入用户-->
    <insert id="addUser"
            parameterType="User">
        insert into t_user(
        user_name,
        user_password,
        address)
        values(
        #{name},
        #{password},
        #{address}
        );
    </insert>
    <!--删除用户-->
    <delete id="deleteUser"
            parameterType="java.lang.Integer">
        delete from t_user
        where id=#{id};
    </delete>
    <!--根据id更新用户-->
    <update id="updateUser"
            parameterType="org.example.entity.User">
        update t_user set
        user_name=#{name},
        user_password=#{password},
        address=#{address}
        where
        id=#{id}
    </update>
</mapper>

单元测试:


    @Test
    public void addUser() {
        //假数据
        User user=new User();
        user.setName("add");
        user.setPassword("654321");
        user.setAddress("测试用例|mapper.addUser()");
        Integer rowAffect=0;
        SqlSession sqlSession=null;
        try{
            sqlSession= MybatisUtil.getSession();
            //接口类型, jdk动态代理对象=sqlSession.getMapper();
            UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
            rowAffect= userMapper.addUser(user);
            sqlSession.commit();
        }catch (Exception e){
            e.printStackTrace();
            sqlSession.rollback();
        }finally {
            if(sqlSession!=null){
                sqlSession.close();
            }
        }
        System.out.println(rowAffect);
    }

    @Test
    public void deleteUser() {
        Integer rowAffect=0;
        SqlSession sqlSession=null;
        try{
            sqlSession= MybatisUtil.getSession();
            //接口类型, jdk动态代理对象=sqlSession.getMapper();
            UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
            rowAffect= userMapper.deleteUser(9);
            sqlSession.commit();
        }catch (Exception e){
            e.printStackTrace();
            sqlSession.rollback();
        }finally {
            if(sqlSession!=null){
                sqlSession.close();
            }
        }
        System.out.println(rowAffect);
    }

    @Test
    public void updateUser() {
        //假数据
        User user=new User();
        user.setId(9);
        user.setName("update");
        user.setPassword("654321");
        user.setAddress("测试用例|mapper.updateUser()");
        Integer rowAffect=0;
        SqlSession sqlSession=null;
        try{
            sqlSession= MybatisUtil.getSession();
            //接口类型, jdk动态代理对象=sqlSession.getMapper();
            UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
            rowAffect= userMapper.updateUser(user);
            sqlSession.commit();
        }catch (Exception e){
            e.printStackTrace();
            sqlSession.rollback();
        }finally {
            if(sqlSession!=null){
                sqlSession.close();
            }
        }
        System.out.println(rowAffect);
    }

 效果展示:

addUser

updateUser 

 deleteUser

总结:

mybatis 的 mapper代理模式和原生api相比,可以不写接口的具体实现类。

mapper模式是纯接口调用,因为有接口的存在,可以使用jdk动态代理为其动态生成实现类。

jdk生成的实现类和接口之间是实现和被实现的关系,

jdk生成的实现类是实现类,也是代理类,其创建动态代理对象,通过代理对象.目标方法的方式,即invacationHandler调用invoke(),来进行实现。

查询:根据id获取用户信息

xxMapper.java

  public User findUserById(Integer id);

userMapper.xml

    <!--根据id获得用户信息-->
    <select id="findUserById"
            resultType="org.example.entity.User"
            parameterType="java.lang.Integer">
        select
        id,
        user_name as name,
        user_password as password,
        address
        from t_user
        where id = #{id}
    </select>

单元测试:

    @Test
    public void findUserById() {
        User user=null;
        SqlSession sqlSession=null;
        try{
            sqlSession= MybatisUtil.getSession();
            //接口类型, jdk动态代理对象=sqlSession.getMapper();
            UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
            user= userMapper.findUserById(1);
            sqlSession.commit();
        }catch (Exception e){
            e.printStackTrace();
            sqlSession.rollback();
        }finally {
            if(sqlSession!=null){
                sqlSession.close();
            }
        }
        System.out.println(user);
    }

结果展示:

查询:根查所有用户List

xxMapper.java

    //返回list结构
    public List<User> findAllUser1();
    public List<Map<String,Object>> findAllUser2();

userMapper.xml

<!--查所有返回list<User>-->
    <select id="findAllUser1"
            resultType="User"
    >
        select
        id,
        user_name as name,
        user_password as password,
        address
        from t_user
    </select>

    <!--查所有返回list<Map>-->
    <select id="findAllUser2"
            resultType="java.util.Map"
    >
        select
        id,
        user_name,
        user_password,
        address
        from t_user
    </select>

单元测试:


    @Test
    public void findAllUser1() {
        List<User> userList=new ArrayList<User>();
        SqlSession sqlSession=null;
        try{
            sqlSession= MybatisUtil.getSession();
            //接口类型, jdk动态代理对象=sqlSession.getMapper();
            UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
            userList= userMapper.findAllUser1();
            sqlSession.commit();

        }catch (Exception e){
            e.printStackTrace();
            sqlSession.rollback();
        }finally {
            if(sqlSession!=null){
                sqlSession.close();
            }
        }
        for(User user:userList){
            System.out.println(user);
        }
    }

    @Test
    public void findAllUser2() {
        List<Map<String,Object>> userList=new ArrayList<Map<String, Object>>();
        SqlSession sqlSession=null;
        try{
            sqlSession= MybatisUtil.getSession();
            //接口类型, jdk动态代理对象=sqlSession.getMapper();
            UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
            userList= userMapper.findAllUser2();
            sqlSession.commit();
        }catch (Exception e){
            e.printStackTrace();
            sqlSession.rollback();
        }finally {
            if(sqlSession!=null){
                sqlSession.close();
            }
        }
        for(Map<String,Object> user:userList){
            System.out.println(user);
        }
    }

结果展示:

 

查询:根查所有用户Map

xxMapper.java

    //返回map结构
    @MapKey("id")
    public Map<Integer,User> findAllUser3();
    @MapKey("id")
    public Map<Integer,Map<String,Object>> findAllUser4();

userMapper.xml

    <!--查所有返回Map<User>-->
    <select id="findAllUser3"
            resultType="User"
    >
        select
        id,
        user_name as name,
        user_password as password,
        address
        from t_user
    </select>

    <!--查所有返回Map<Map>-->
    <select id="findAllUser4"
            resultType="java.util.Map"
    >
        select
        id,
        user_name,
        user_password,
        address
        from t_user
    </select>

单元测试:

    @Test
    public void findAllUser3() {
        Map<Integer,User> userMap= new HashMap<Integer, User>();
        SqlSession sqlSession=null;
        try{
            sqlSession= MybatisUtil.getSession();
            //接口类型, jdk动态代理对象=sqlSession.getMapper();
            UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
            userMap= userMapper.findAllUser3();
            sqlSession.commit();
        }catch (Exception e){
            e.printStackTrace();
            sqlSession.rollback();
        }finally {
            if(sqlSession!=null){
                sqlSession.close();
            }
        }
        for(Integer key:userMap.keySet()){
            System.out.println(key+" "+userMap.get(key));
        }
    }

    @Test
    public void findAllUser4() {
        Map<Integer,Map<String,Object>> userMap= new HashMap<Integer, Map<String,Object>>();
        SqlSession sqlSession=null;
        try{
            sqlSession= MybatisUtil.getSession();
            //接口类型, jdk动态代理对象=sqlSession.getMapper();
            UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
            userMap= userMapper.findAllUser4();
            sqlSession.commit();
        }catch (Exception e){
            e.printStackTrace();
            sqlSession.rollback();
        }finally {
            if(sqlSession!=null){
                sqlSession.close();
            }
        }
        for(Integer key:userMap.keySet()){
            System.out.println(key+" "+userMap.get(key));
        }
    }

结果展示:

3.JDKProxy模拟Mapper代理实现

Mybatis的Mapper代理模式,可以通过接口使用jdk的代理机制实现自动代理。(jdk的动态代理是基于接口的,而此处的xxMapper接口即为被代理的对象接口)

        本质上xxMapper并没有实现类,jdk的动态代理也没有对所谓的老方法进行代理。

程序员不需要写接口的具体实现,只要根据规则定义好接口,即可使用相关功能。

我们使用JDKProxy类模拟Mybatis实现getMapper()的方法,理解其中的内部实现。

package org.example.proxy;

import org.example.proxy.handler.MapperHandler;

import java.lang.reflect.Proxy;

public class JDKProxy {

    /**
     * 根据接口获取代理对象
     * @param clazz
     * @return
     */
    public static Object getMapper(Class clazz){
        Object proxyObject=null;
        /**
         * 参数一: 类加载器
         * 参数二: 接口数组
         * 参数三: InvocationHandler 接口的回调
         */
        proxyObject= Proxy.newProxyInstance(
             clazz.getClassLoader(),
             new Class[]{clazz},
             new MapperHandler()
        );
        return proxyObject;
    }

}

这里虽然使用了动态代理,但是并不是动态代理的常规用法,这里只有代理对象的接口,且该接口没有任何实现。动态代理在这里的目的并不是调用老方法做切面设计,而是获取方法信息:参数列表、返回值等,进行判断,根据方法选择调用mybatis的原生api。所以Mapper代理模式底层调用的还是Mybatis的原生api,  其只是通过动态代理实现了一个默认实现版本,来较少程序员的代码量。

关于JDK的第三个参数 InvocationHandler 接口的回调,我们构建实现类MapperHandler,其实现InvocationHandler接口。具体来说就是:

package org.example.proxy.handler;

import org.example.entity.User;

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

public class MapperHandler implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object returnValue=null;
        System.out.println("由Mybatis根据若干信息选择执行对应的api");
        /**
         * mybatis 根据method能得到目标方法的返回值,方法名,参数类型
         * if判断返回的数据为一个值,则调用mybatis的原生api  selectOne方法
         * if判断返回的数据为多个值时,看返回类型是否为List,则调用mybatis的原生 selectList()方法
         * if判断返回的数据为多个值时,看返回类型是否为Map,则调用mybatis的原生 selectMap()方法
         * if判断为insert/delete/update时,调用原生api  insert()  delete()  update()
         */
        //eg:执行selectOne()
        User user=new User();
        user.setId(1);
        user.setName("aaa");
        user.setPassword("123456");
        user.setAddress("诺亚方舟");
        returnValue=user;
        return returnValue;
    }
}

 使用单元测试来对方法进行测试:

package org.example.proxy;

import org.example.dao.UserMapper;
import org.example.entity.User;
import org.junit.Test;

import static org.junit.Assert.*;

public class JDKProxyTest {

    @Test
    public void getMapper() {
        //接口类型 jdk动态代理对象=sqlSession.getMapper(接口类型.class);
        UserMapper userMapper=(UserMapper) JDKProxy.getMapper(UserMapper.class);
        User user=userMapper.findUserById(1);
        System.out.println(user);
    }
}

值得注意的是,getMapper中我们并没有写具体的实现逻辑,因为测试的是findUserById()方法,我们在getMapper()中返回了一个假数据,用来做测试。而Mybatis则根据逻辑写了具体的实现。 

4.Mybatis 中getMapper的实现

重点:模拟getMapper的实现


    public Object execute(SqlSession sqlSession, Object[] args) {
        Object param;
        Object result;
        switch (this.command.getType()) {
            case INSERT:
                param = this.method.convertArgsToSqlCommandParam(args);
                result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
                break;
            case UPDATE:
                param = this.method.convertArgsToSqlCommandParam(args);
                result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
                break;
            case DELETE:
                param = this.method.convertArgsToSqlCommandParam(args);
                result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
                break;
            case SELECT:
                if (this.method.returnsVoid() && this.method.hasResultHandler()) {
                    this.executeWithResultHandler(sqlSession, args);
                    result = null;
                } else if (this.method.returnsMany()) {
                    result = this.executeForMany(sqlSession, args);
                } else if (this.method.returnsMap()) {
                    result = this.executeForMap(sqlSession, args);
                } else if (this.method.returnsCursor()) {
                    result = this.executeForCursor(sqlSession, args);
                } else {
                    param = this.method.convertArgsToSqlCommandParam(args);
                    result = sqlSession.selectOne(this.command.getName(), param);
                }
                break;
            case FLUSH:
                result = sqlSession.flushStatements();
                break;
            default:
                throw new BindingException("Unknown execution method for: " + this.command.getName());
        }

        if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
            throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
        } else {
            return result;
        }
    }

基本流程:getMapper()接口——>创建Proxy对象——>execute()实现具体实现   

特别的关于select()方法

其中method.hasResultHandler()来实现自定义的方法处理

还可以匹配不同的返回类型,其中游标模式Cursor不怎么用了

5.总结

接口定义:userDao 和UserMapper一样的写法不同,用来定义方法接口

        其可以自己写实现类也可以通过mapper() 方式来操作数据

总清单文件:

        数据库配置

        子清单文件

                模板统一

                原生写法的子清单文件 namespace和增删改查节点信息任意配置

                mapper写法的子清单 namespace="包名.接口"

                        接口方法名和节点id一致

                        接口方法参数和节点parameterType一样

                        接口的返回值跟节点resultType一样

mybatis自己的api解析xml来构建sqlSessionFactory,用于生产SqlSession

使用sqlSession来做增删改查

mapper接口方法底层还是原生api  api结合jdk动态代理

原生api

  • insert() delete() update()
  • selectOne()  selectList() selectMap()
  • select() 自定义返回的数据结构(策略+回调)
  • 返回结果类型丰富

6.设计模式:

代理模式:MapperProxy实现jdk动态代理

7.补充

什么是面向接口编程?

如select面向接口、jdbc面向接口和spring面向接口都有相关的面向对象接口编程,其目的是实现解耦操作

  • 面向接口编程是一种编程范式,它强调的是在设计软件应用时,应该先定义接口,然后再实现接口。这种方式有很多优点,包括提高代码的可读性、可维护性和可扩展性,以及降低代码之间的耦合度。
  • 接口是一种契约,它定义了一组方法,这些方法应该在实现接口的类中实现。接口本身并不包含任何实现细节,它只是定义了一种规范,规定了实现接口的类应该做什么,而不是怎么做。

摘自:《17.Spring 面向接口编程 - 知乎》

select()也是面向接口编程提供select接口但没有实现,其具体的实现在handler中去处理。实现了接口和实现的分离。


 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

KAN神经网络简短介绍

KANs简介 Kolmogorov-Arnold Networks (KANs) 是一种创新的神经网络模型&#xff0c;它挑战了传统多层感知器(MLPs)的设计&#xff0c;通过将激活函数从节点转移到边上来提升模型的性能和可解释性。KAN的核心在于&#xff0c;其所有权重参数均被单变量的样条函数代替&#xff…

设计模式 六大原则之里氏替换原则

文章目录 概念替换逻辑行为不变 拆解小结 概念 子类对象能够替换程序中父类对象出现的任何地方&#xff0c;并且保证原来程序的逻辑行为不变及正确性不被破坏。 替换 替换的前提是面向对象语言所支持的多态特性&#xff0c;同一个行为具有多个不同表现形式或形态的能力。 逻…

Web3加密空投入门:空投类型有哪些?如何避免限制?

今天分享空投如何避免限制以提高效率&#xff0c;增加成功几率&#xff0c;首先我们来了解什么是空投加密&#xff0c;有哪些空投类型。 一、什么是空投加密&#xff1f; 加密货币空投是一种营销策略&#xff0c;包括向用户的钱包地址发送免费的硬币或代币。 加密货币项目使用…

哈希表Hash table

哈希表是根据关键码的值而直接进行访问的数据结构。 数组就是⼀张哈希表。 哈希表中关键码就是数组的索引下标&#xff0c;然后通过下标直接访问数组中的元素&#xff0c;如下图所示&#xff1a; 那么哈希表能解决什么问题呢&#xff0c;一般哈希表都是用来快速判断⼀个元素是…

人脸消费给传统食堂带来的变化

消费的技术基础是脸部识别&#xff0c;脸部识别是基于人的容貌特征信息进行认证的生物特征识别技术&#xff0c;其突出的特征是以非接触方式进行识别&#xff0c;避免个人信息的泄露。 面部识别和指纹识别、掌纹识别、视网膜识别、骨骼识别、心率识别等都是人体生物特征识别技术…

STC8增强型单片机开发——串口调试UART

一、什么是串口 串口是一种在数据通讯中广泛使用的通讯接口&#xff0c;通常我们叫做UART (通用异步收发传输器Universal Asynchronous Receiver/Transmitter)&#xff0c;其具有数据传输速度稳定、可靠性高、适用范围广等优点。在嵌入式系统中&#xff0c;串口常用于与外部设备…

IDEA安装使用Git

IDEA安装使用Git 1 Git下载与安装 2 在IDEA中使用Git 2.1 IDEA中配置Git 在IDEA中使用Git&#xff0c;本质上还是使用本地安装的Git软件&#xff0c;所以需要在IDEA中配置Git。 2.2 在IDEA中使用Git 2.2.1 获取Git仓库 在IDEA中使用Git获取仓库有两种方式: 本地初始化仓库从…

Java | Leetcode Java题解之第85题最大矩形

题目&#xff1a; 题解&#xff1a; class Solution {public int maximalRectangle(char[][] matrix) {int m matrix.length;if (m 0) {return 0;}int n matrix[0].length;int[][] left new int[m][n];for (int i 0; i < m; i) {for (int j 0; j < n; j) {if (mat…

Unity Material(材质)、Texture(纹理)、Shader(着色器)简介

文章目录 一、概念二、Rendering Mode三、Main Maps三、参考文章 一、概念 Material(材质)&#xff1a;物体的“色彩”、“纹理”、“光滑度”、“透明度”、“反射率”、“折射率”、“发光度”等&#xff0c;材质的本质是shader的实例(载体)Texture(贴图)&#xff1a;附件到…

【kali工具使用】Tcpdump 抓包查看三次握手过程

Tcpdump 抓包查看三次握手过程 tcpdump 常用参数&#xff1a; -c 指定要抓取的数据包数量 -n 对 IP 地址以数字方式显式&#xff0c;否则显式为主机名 port 指定端口 -I 指定 tcpdump 需要监听的接口。默认会抓取第一个网络接口 tcp 1ClientSYN1seqx 2Server SYN1 seq…

从 Oracle 到 TiDB,国有大行打造本地生活 APP 新体验

导读 本文介绍了某国有大行推出的本地生活服务类 APP 在数字时代的创新应用实践。该 APP 利用金融科技和互联网平台模式&#xff0c;打造“金融非金融”的线上生态服务平台&#xff0c;满足了用户多样化的生活需求。为应对用户增长和数据量增加带来的挑战&#xff0c;该 APP 决…

地表净辐射通量数据、太阳辐射量数据、降雨量数据、气温数据、日照时长、水汽压分布、风速风向数据、地表温度

引言 地表净辐射作为驱动大气运动的主要能量&#xff0c;它是气候变化乃至全球变化的重要驱动力。由地表净辐射可反演比辐射率、地表温度、地表反照率等地表特征参数&#xff0c;是提高天气预报质量和大气环流模式研究的一个重要参数。多种卫星遥感数据反演地表净辐射通量信息产…

发表在期刊PRB和JAP上文章的说明及引用

文章目录 前言一、磁畴壁波导的能带调控研究&#xff08;in PRB&#xff09;文章简介&#xff1a;关键词&#xff1a;文章引用&#xff1a; 二、具有固定旋转轨道的自旋转矩纳米振荡器&#xff08;in JAP&#xff09;文章简介&#xff1a;关键词&#xff1a;文章引用&#xff1…

LeetCode-460. LFU 缓存【设计 哈希表 链表 双向链表】

LeetCode-460. LFU 缓存【设计 哈希表 链表 双向链表】 题目描述&#xff1a;解题思路一&#xff1a;一张图秒懂 LFU&#xff01;解题思路二&#xff1a;精简版&#xff01;两个哈希表&#xff0c;一个记录所有节点&#xff0c;一个记录次数链表【defaultdict(new_list)&#x…

kafka安装配置及集成springboot

1. 安装 单机安装kafka Kafka对于zookeeper是强依赖&#xff0c;保存kafka相关的节点数据&#xff0c;所以安装Kafka之前必须先安装zookeeper dockerhub网址: https://hub.docker.com Docker安装zookeeper 下载镜像&#xff1a; docker pull zookeeper:3.4.14创建容器 doc…

SeetaFace6人脸活体检测C++代码实现Demo

SeetaFace6包含人脸识别的基本能力&#xff1a;人脸检测、关键点定位、人脸识别&#xff0c;同时增加了活体检测、质量评估、年龄性别估计&#xff0c;并且顺应实际应用需求&#xff0c;开放口罩检测以及口罩佩戴场景下的人脸识别模型。 官网地址&#xff1a;https://github.co…

【CSP CCF记录】数组推导

题目 过程 思路 每次输入一个Bi即可确定一个Ai值&#xff0c;用temp记录1~B[i-1]&#xff0c;的最大值分为两种情况&#xff1a; 当temp不等于Bi时&#xff0c;则说明Bi值之前未出现过&#xff0c;Ai必须等于Bi才能满足Bi是Ai前缀最大的定义。当temp等于Bi时&#xff0c;则说…

后端开发之用Mybatis简化JDBC的开发快速入门2024及数据库连接池技术和lombok工具详解

JDBC 简化JDBC的开发 JDBC仅仅是一套接口 是一套规范 Mybatis是持久层框架 用于简化JDBC的开发 使用Java语言操作关系型数据库的一套API 原始的JDBC程序 package com.bigdate.mybatis;import com.bigdate.mybatis.mapper.UserMapper; import com.bigdate.mybatis.pojo.Use…

(二)Jetpack Compose 布局模型

前文回顾 &#xff08;一&#xff09;Jetpack Compose 从入门到会写-CSDN博客 首先让我们回顾一下上一篇文章中里提到过几个问题&#xff1a; ComposeView的层级关系&#xff0c;互相嵌套存在的问题&#xff1f; 为什么Compose可以实现只测量一次&#xff1f; ComposeView和…

【JVM】感觉弗如...类加载机制

【JVM】感觉弗如…类加载机制 在Java开发过程中&#xff0c;从源代码&#xff08;.java文件&#xff09;到字节码&#xff08;.class文件&#xff09;再到运行时的类加载&#xff0c;会经历几个关键步骤&#xff0c;我们先简单过一遍大体的过程。再介绍今天这篇博客的重点内容—…