百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分析 > 正文

备战金三银四,JVM和MyBatis、spring知识点大梳理(附面试题大全

liebian365 2024-11-17 13:18 4 浏览 0 评论

JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。

JVM

前言:

Java 虚拟机,是一个可以执行 Java 字节码的虚拟机进程。Java 源文件被编译成能被 Java 虚拟机执行的字节码文件( .class )。

跨平台的是 Java 程序(包括字节码文件),,而不是 JVM。JVM 是用 C/C++ 开发的,是编译后的机器码,不能跨平台,不同平台下需要安装不同版本的 JVM 。

JVM 组成部分


1、类加载器

从类被加载到虚拟机内存中开始,到卸御出内存为止,它的整个生命周期分为7个阶段, 加载(Loading)验证(Verification)准备(Preparation)解析(Resolution)初始化(Initialization)使用(Using)卸御(Unloading) 。其中验证、准备、解析三个部分统称为连接。7个阶段发生的顺序如下:

加载(Loading)、验证(Verification)、准备(Preparation)、初始化(Initialization)、卸载(Unloading) 这五个阶段的过程是 固定 的,在类加载过程中必须按照这种顺序按部就班地进行,而解析阶段则不一定,他在某种情况下可以在初始化之后进行,这个是为了支持Java语言的运行时绑定(也称为动态绑定或者晚期绑定)。

1.1、加载

加载阶段,虚拟机需要完成3件事:

加载阶段完成后,虚拟机外部的 二进制字节流就按照虚拟机所需的格式存储在方法区之中,而且在Java堆中也创建一个java.lang.Class类的对象,这样便可以通过该对象访问方法区中的这些数据。

1.2、验证

验证阶段主要,这一阶段的目的是为了确保Class文件的字节流中包含的信息符合虚拟机的要求,并且不会危害虚拟机自身的安全。

验证阶段主要完成下面4个阶段的校验动作:


1.3、准备

准备阶段是正式为类变量分配内存并设置初始值的阶段,这些变量所使用的内存都将在方法区分配。

进行内存分配的仅包括类变量(static),而不包括实例变量,实例变量会在对象实例化时随着对象一块分配在Java堆中。

初始值通常情况下是数据类型默认的零值(如0、0L、null、false等)

1.4、解析

解析阶段是将虚拟机常量池内的符号引用替换为直接引用的过程。解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号进行.

符号引用:简单的理解就是字符串,比如引用一个类,java.util.ArrayList 这就是一个符号引用,字符串引用的对象不一定被加载。

直接引用:指针或者地址偏移量。引用对象一定在内存(已经加载)。

1.5、初始化

类初始化是类加载的最后一步,除了加载阶段,用户可以通过自定义的类加载器参与,其他阶段都完全由虚拟机主导和控制。到了初始化阶段才真正执行Java代码。

类的初始化的主要工作是为 静态变量赋程序设定的初值

如static int a = 100;在准备阶段,a被赋默认值0,在初始化阶段就会被赋值为100。

Java虚拟机规范中严格规定了有且只有五种情况必须对类进行初始化:

2、对象的创建过程


Java 中对象的创建就是在堆上分配内存空间的过程,此处说的对象创建仅限于 new 关键字创建的普通 Java 对象,不包括数组对象的创建。

当虚拟机遇到一条含有new的指令时,会进行一系列对象创建的操作:

2.1、检查类是否被加载

1、检查常量池中是否有即将要创建的这个对象所属的类的符号引用;若常量池中没有这个类的符号引用,说明这个类还没有被定义!抛出ClassNotFoundException;

2、进而检查这个符号引用所代表的类是否已经被JVM加载;若该类还没有被加载,就找该类的class文件,并加载进方法区;若该类已经被JVM加载,则准备为对象分配内存;

2.2、为对象分配内存

根据方法区中该类的信息确定该类所需的内存大小;一个对象所需的内存大小是在这个对象所属类被定义完就能确定的!且一个类所生产的所有对象的内存大小是一样的!JVM在一个类被加载进方法区的时候就知道该类生产的每一个对象所需要的内存大小。4、从堆中划分一块对应大小的内存空间给新的对象;分配堆中内存有两种方式:指针碰撞 如果JVM的垃圾收集器采用复制算法或标记-整理算法,那么堆中空闲内存是完整的区域,并且空闲内存和已使用内存之间由一个指针标记。那么当为一个对象分配内存时,只需移动指针即可。因此,这种在完整空闲区域上通过移动指针来分配内存的方式就叫做“指针碰撞”。空闲列表 如果JVM的垃圾收集器采用标记-清除算法,那么堆中空闲区域和已使用区域交错,因此需要用一张“空闲列表”来记录堆中哪些区域是空闲区域,从而在创建对象的时候根据这张“空闲列表”找到空闲区域,并分配内存。综上所述:JVM究竟采用哪种内存分配方法,取决于它使用了何种垃圾收集器。

多线程并发时会出现正在给对象 A 分配内存,还没来得及修改指针,对象 B 又用这个指针分配内存,这样就出现问题了。

解决这种问题有两种方案:

第一种,是采用同步的办法,使用 CAS 来保证操作的原子性。

另一种,是每个线程分配内存都在自己的空间内进行,即是每个线程都在堆中预先分配一小块内存,称为本地线程分配缓冲(Thread Local Allocation Buffer, TLAB),分配内存的时候再TLAB上分配,互不干扰。可以通过 -XX:+/-UseTLAB 参数决定。

2.3、为分配的内存空间初始化零值

为对象中的成员变量赋上初始值(默认初始化);

对象的内存分配完成后,还需要将对象的内存空间都初始化为零值,这样能保证对象即使没有赋初值,也可以直接使用

2.4、为对象进行其他设置

设置对象头中的信息;

所属的类,类的元数据信息,对象的 hashcode ,GC 分代年龄等信息

2.5、执行 init 方法

调用对象的构造函数进行初始化

执行完上面的步骤之后,在虚拟机里这个对象就算创建成功了,但是对于 Java 程序来说还需要执行 init 方法才算真正的创建完成,因为这个时候对象只是被初始化零值了,还没有真正的去根据程序中的代码分配初始值,调用了 init 方法之后,这个对象才真正能使用。

初始化顺序:


先初始化父类的静态代码--->初始化子类的静态代码-->初始化父类的非静态代码--->初始化父类构造函数--->初始化子类非静态代码--->初始化子类构造函数

3、对象的内存布局

3.1、对象头(markword)

在32位系统下,对象头8字节,64位则是16个字节【未开启压缩指针,开启后12字节】。

假设当前为32bit,在对象未被锁定情况下。25bit为存储对象的哈希码、4bit用于存储分代年龄,2bit用于存储锁标志位,1bit固定为0。

不同状态下存放数据:

这其中锁标识位需要特别关注下。锁标志位与是否为偏向锁对应到唯一的锁状态。

锁的状态分为四种无锁状态、偏向锁、轻量级锁和重量级锁

不同状态时对象头的区间含义,如图所示。

3.2、实例数据(Instance Data)

实例数据部分是对象真正存储的有效信息,也是在程序代码中所定义的各种类型的字段内容。

这部分的存储顺序会受到虚拟机分配策略参数(FieldsAllocationStyle)和字段在 Java 源码中定义顺序的影响。

分配策略:相同宽度的字段总是放在一起,比如double和long

3.3、对其填充(Padding)

对齐填充不是必然存在的,没有特别的含义,它仅起到占位符的作用。

由于HotSpot规定对象的大小必须是8的整数倍,对象头刚好是整数倍,如果实例数据不是的话,就需要占位符对齐填充。

3.4、预估对象大小

32 位系统下,当使用 new Object() 时,JVM 将会分配 8(Mark Word+类型指针) 字节的空间,128 个 Object 对象将占用 1KB 的空间。如果是 new Integer(),那么对象里还有一个 int 值,其占用 4 字节,这个对象也就是 8+4=12 字节,对齐后,该对象就是 16 字节。

以上只是一些简单的对象,那么对象的内部属性是怎么排布的?

Class A { int i; byte b; String str;}

其中对象头部占用 ‘Mark Word’4 + ‘类型指针’4 = 8 字节;byte 8 位长,占用 1 字节;int 32 位长,占用 4 字节;String 只有引用,占用 4 字节;那么对象 A 一共占用了 8+1+4+4=17 字节,按照 8 字节对齐原则,对象大小也就是 24 字节。

这个计算看起来是没有问题的,对象的大小也确实是 24 字节,但是对齐(padding)的位置并不对:

在 HotSpot VM 中,对象排布时,间隙是在 4 字节基础上的(在 32 位和 64 位压缩模式下),上述例子中,int 后面的 byte,空隙只剩下 3 字节,接下来的 String 对象引用需要 4 字节来存放,因此 byte 和对象引用之间就会有 3 字节对齐,对象引用排布后,最后会有 4 字节对齐,因此结果上依然是 7 字节对齐。此时对象的结构示意图,如下图所示:

4、对象访问

对象的访问方式由虚拟机决定,java虚拟机提供两种主流的方式

1.句柄访问对象

2.直接指针访问对象。(Sun HotSpot使用这种方式)

4.1、句柄访问

简单来说就是java堆划出一块内存作为句柄池,引用中存储对象的句柄地址,句柄中包含对象实例数据、类型数据的地址信息。

优点:引用中存储的是稳定的句柄地址,在对象被移动【垃圾收集时移动对象是常态】只需改变句柄中实例数据的指针,不需要改动引用【ref】本身。

4.2、直接指针

与句柄访问不同的是,ref中直接存储的就是对象的实例数据,但是类型数据跟句柄访问方式一样。

优点:优势很明显,就是速度快,相比于句柄访问少了一次指针定位的开销时间。【可能是出于Java中对象的访问时十分频繁的,平时我们常用的JVM HotSpot采用此种方式】

5、JVM 内存区域


5.1、虚拟机栈

描述的是方法执行时的 内存模型 ,是 线程私有 的,生命周期与线程相同,每个方法被执行的同时会创建栈桢,主要保存执行方法时的 局部变量表、操作数栈、动态连接和方法返回地址 等信息,方法执行时入栈,方法执行完出栈,出栈就相当于清空了数据,入栈出栈的时机很明确,所以这块区域不需要进行 GC。

Java虚拟机栈可能出现两种类型的异常:

5.2、本地方法栈

与虚拟机栈功能非常类似,主要区别在于虚拟机栈为虚拟机执行 Java 方法时服务,而本地方法栈为虚拟机执行 本地方法 时服务的。这块区域也不需要进行 GC。

5.3、程序计数器

5.4、本地内存

5.5、堆

6、对象存活判断

6.1、引用计数

每个对象有一个引用计数属性,新增一个引用时计数加 1 ,引用释放时计数减 1 ,计数为 0 时可以回收。此方法简单,无法解决对象相互循环引用的问题。目前在用的有 Python、ActionScript3 等语言。

6.2、可达性分析

从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是不可用的。不可达对象。目前在用的有 Java、C# 等语言。

GC Roots 对象:

如何判断无用的类:


6.3、finalize

finallize()方法,是在释放该对象内存前由 GC (垃圾回收器)调用。

通常建议在这个方法中释放该对象持有的资源,例如持有的堆外内存、和远程服务的长连接。一般情况下,不建议重写该方法。对于一个对象,该方法有且仅会被调用一次。

6.4、对象引用类型

6.4.1、强引用

如果一个对象具有强引用,那就类似于必不可少的生活用品,垃圾回收器绝不会回收它。当内存空间不足,Java 虚拟机宁愿抛出 OutOfMemoryError 错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题

6.4.2、软引用

如果一个对象只具有软引用,那就类似于可有可无的生活用品。如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。

6.4.3、弱引用

弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它 所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。

6.4.4、虚引用

“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。。当垃 圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

拓展

利用软引用和弱引用解决 OOM 问题。用一个 HashMap 来保存图片的路径和相应图片对象关联的软引用之间的映射关系,在内存不足时,JVM 会自动回收这些缓存图片对象所占用的空间,从而有效地避免了 OOM 的问题. 通过软引用实现 Java 对象的高速缓存。比如我们创建了一 Person 的类,如果每次需要查询一个人的信息,哪怕是几秒中之前刚刚查询过的,都要重新构建一个实例,这将引起大量 Person 对象的消耗,并且由于这些对象的生命周期相对较短,会引起多次 GC 影响性能。此时,通过软引用和 HashMap 的结合可以构建高速缓存,提供性能。

7、垃圾回收算法

7.1、标记-清除

在标记阶段,首先通过根节点,标记所有从根节点开始的可达对象。因此,未被标记的对象就是未被引用的垃圾对象(好多资料说标记出要回收的对象,其实明白大概意思就可以了)。然后,在清除阶段,清除所有未被标记的对象。

缺点:

7.2、标记-整理

标记整理算法,类似与标记清除算法,不过它标记完对象后,不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉边界以外的内存。

优点:

缺点:

7.3、复制算法

复制算法,可以解决效率问题,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块,当这一块内存用完了,就将还存活着的对象复制到另一块上面,然后再把已经使用过的内存空间一次清理掉,这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可(还可使用TLAB进行高效分配内存)

优点:

缺点:

7.4、分代算法

当前商业虚拟机都是采用分代收集算法,它根据对象存活周期的不同将内存划分为几块,一般是把 Java 堆分为新生代和老年代,然后根据各个年代的特点采用最适当的收集算法。

在新生代中,每次垃圾收集都发现有大批对象死去,只有少量存活,就选用复制算法。而老年代中,因为对象存活率高,没有额外空间对它进行分配担保,就必须使用“标记清理”或者“标记整理”算法来进行回收。

图的左半部分是未回收前的内存区域,右半部分是回收后的内存区域。

对象分配策略:对象优先在 Eden 区域分配,如果对象过大直接分配到 Old 区域。长时间存活的对象进入到 Old 区域。

改进自复制算法

现在的商业虚拟机都采用这种收集算法来回收新生代,IBM 公司的专门研究表明,新生代中的对象 98% 是“朝生夕死”的,所以并不需要按照 1:1 的比例来划分内存空间,而是将内存分为一块较大的 Eden 空间和两块较小的 Survivor 空间,每次使用 Eden 和其中一块 Survivor 。当回收时,将 Eden 和 Survivor 中还存活着的对象一次性地复制到另外一块 Survivor 空间上,最后清理掉 Eden 和刚才用过的 Survivor 空间。HotSpot 虚拟机默认 Eden 和 2 块 Survivor 的大小比例是 8:1:1,也就是每次新生代中可用内存空间为整个新生代容量的 90%(80%+10%),只有 10% 的内存会被“浪费”。当然,98% 的对象可回收只是一般场景下的数据,我们没有办法保证每次回收都只有不多于 10% 的对象存活,当 Survivor 空间不够用时,需要依赖其他内存(这里指老年代)进行分配担保(Handle Promotion)。

8、安全点

8.1、安全点

SafePoint 安全点,顾名思义是指一些特定的位置,当线程运行到这些位置时,线程的一些状态可以被确定(the thread’s representation of it’s Java machine state is well described),比如记录OopMap 的状态,从而确定 GC Root 的信息,使 JVM 可以安全的进行一些操作,比如开始 GC 。

SafePoint 指的特定位置主要有:

8.2、安全区域

安全点完美的解决了如何进入GC问题,实际情况可能比这个更复杂,但是如果程序长时间不执行,比如线程调用的sleep方法,这时候程序无法响应JVM中断请求这时候线程无法到达安全点,显然JVM也不可能等待程序唤醒,这时候就需要安全区域了。

安全区域是指一段代码片中,引用关系不会发生变化,在这个区域任何地方GC都是安全的,安全区域可以看做是安全点的一个扩展。线程执行到安全区域的代码时,首先标识自己进入了安全区域,这样GC时就不用管进入安全区域的线层了,线层要离开安全区域时就检查JVM是否完成了GC Roots枚举,如果完成就继续执行,如果没有完成就等待直到收到可以安全离开的信号。

9、JVM 垃圾回收器



9.1、Serial (新生代)

9.2、ParNew(新生代)

9.3、Parallel Scavenge(新生代)

用作新生代收集器,复制算法。关注高吞吐量,可以高效率地利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。Parallel Scavenge收集器提供了两个参数用于精确控制吞吐量,分别是控制最大垃圾收集停顿时间的-XX:MaxGCPauseMillis参数以及直接设置吞吐量大小的-XX:GCTimeRatio参数。

9.4、Serial Old(老年代)

9.5、Parallel Old(老年代)

9.6、CMS(Concurrent Mark Sweep)(老年代)

  1. 初始标记(CMS initial mark):只是标记一下GC Roots能直接关联到的对象,速度很快,会Stop The World。
  2. 并发标记(CMS concurrent mark):进行GC Roots Tracing(可达性分析)的过程。
  3. 重新标记(CMS remark):会Stop The -World。为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般比初始标记阶段稍长些,但远比并发标记的时间短。
  4. 并发清除(CMS concurrent sweep):回收内存。

耗时最长的并发标记和并发清除过程收集器线程都可以与用户线程一起工作,所以时并发执行的。

缺点:

9.7、G1

-XX:G1HeapRegionSize


可预测的停顿时间:估算每个region内的垃圾可回收的空间以及回收需要的时间(经验值),记录在一个优先列表中。收集时,优先回收价值最大的region,而不是在整个堆进行全区域回收。这样提高了回收效率,得名:Garbage-First。G1中有2种GC:

young GC:新生代eden区没有足够可用空间时触发。存活的对象移到survivor区或晋升old区。mixed GC:当old区对象很多时,老年代对象空间占堆总空间的比值达到阈值(-XX:InitiatingHeapOccupancyPercent默认45%)会触发,它除了回收年轻代,也回收 部分 老年代(回收价值高的部分region)。

mixed GC回收步骤:

  1. 初始标记(Initial Marking):只是标记一下GC Roots能直接关联到的对象,并且修改TAMS(Next Top at Mark Start)的值,让下一阶段用户程序并发运行时,能在正确可用的Region中创建新对象。这阶段需要停顿线程(STW),但耗时很短,共用YGC的停顿,所以一般伴随着YGC发生。
  2. 并发标记(Concurrent Marking):进行可达性分析,找出存活对象,耗时长,但可与用户线程并发执行。
  3. 最终标记(Final Marking):修正并发标记阶段用户线程运行导致的变动记录。会STW,但可以并行执行,时间不会很长。
  4. 筛选回收(Live Data Counting and Evacuation):根据每个region的回收价值和回收成本排序,根据用户配置的GC停顿时间开始回收。


当对象分配过快,mixed GC来不及回收,G1会退化,触发Full GC,它使用单线程的Serial收集器来回收,整个过程STW,要尽量避免这种情况。

当内存很少的时候(存活对象占用大量空间),没有足够空间来复制对象,会导致回收失败。这时会保留被移动过的对象和没移动的对象,只调整引用。失败发生后,收集器认为存活对象被移动了,有足够空间让应用程序使用,于是用户线程继续工作,等待下一次触发GC。如果内存不够,就会触发Full GC。

9.8、ZGC

在JDK 11当中,加入了实验性质的ZGC。它的回收耗时平均不到2毫秒。它是一款低停顿高并发的收集器。

ZGC几乎在所有地方并发执行的,除了初始标记的是STW的。所以停顿时间几乎就耗费在初始标记上,这部分的实际是非常少的。那么其他阶段是怎么做到可以并发执行的呢?

ZGC主要新增了两项技术,

ZGC 是一个 并发、基于区域(region)、增量式压缩 的收集器。Stop-The-World 阶段只会在根对象扫描(root scanning)阶段发生,这样的话 GC 暂停时间并不会随着堆和存活对象的数量而增加。

处理阶段:

着色指针Colored Pointer

ZGC利用指针的64位中的几位表示Finalizable、Remapped、Marked1、Marked0(ZGC仅支持64位平台),以标记该指向内存的存储状态。

相当于在对象的指针上标注了对象的信息。注意,这里的指针相当于Java术语当中的引用。

在这个被指向的内存发生变化的时候(内存在Compact被移动时),颜色就会发生变化。

由于着色指针的存在,在程序运行时访问对象的时候,可以轻易知道对象在内存的存储状态(通过指针访问对象),

读屏障Load Barrier

若请求读的内存在被着色了,那么则会触发读屏障。读屏障会更新指针再返回结果,此过程有一定的耗费,从而达到与用户线程并发的效果。

与标记对象的传统算法相比,ZGC在指针上做标记,在访问指针时加入Load Barrier(读屏障),比如当对象正被GC移动,指针上的颜色就会不对,这个屏障就会先把指针更新为有效地址再返回,也就是,永远只有单个对象读取时有概率被减速,而不存在为了保持应用与GC一致而粗暴整体的Stop The World。

最后

看完本章,相信你对JVM重新有了一定的认识,如果觉得有收获的话,可以帮我点一个在看,谢谢你的支持。

MyBatis

前言

是一个优秀的持久层ORM框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。

Mybatis通过xml或注解的方式将要执行的statement配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。

MyBatis面试题

  1. MyBatis是什么?
  2. 为什么说Mybatis是半自动ORM映射工具? 它与全自动的区别在哪里?
  3. MyBatis与Hibernate的区别
  4. MyBatis编程步骤是什么样的?
  5. 请说说MyBatis的工作原理
  6. MyBatis的功能架构是怎样的
  7. Mybatis都有哪些Executor执行器? 它们之间的区别是什么?
  8. Mybatis中如何指定使用哪一种Executor执行器?
  9. Mybatis是否支持延迟加载? 如果支持,它的实现原理是什么?
  10. 什么是MyBatis的接口绑定? 有哪些实现方式?
  11. 使用MyBatis的mapper接口调用时有哪些要求?
  12. Mybatis动态sql是做什么的? 都有哪些动态sql? 能简述一下动态sql的执行原理不?
  13. Mybatis是如何进行分页的? 分页插件的原理是什么?
  14. 简述Mybatis的插件运行原理,以及如何编写一个插件。
  15. 缓存 Mybatis的一级、二级缓存
  16. Mybatis源码中用了哪些设计模式?


1、MyBatis是什么?

MyBatis 是一款优秀的持久层框架,一个半 ORM(对象关系映射)框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis消除了几乎所有的JDBC代码,比如注册驱动,获取、关闭数据库连接,创建Statement对象,手动设置参数,结果集检索,这些都不需要,开发人员只需要使用xml进行简单的配置、映射,就可以操作数据库。

常见的封装了jdbc的框架:DbUtils、Hibernate、MyBatis、Spring的JdbcTemplate。

ORM是什么

ORM(Object Relational Mapping),对象关系映射,对象指的是pojo,关系指的是关系型数据库中的表、记录。是一种为了解决关系型数据库数据与简单Java对象(POJO)的映射关系的技术。简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系型数据库中。

常用的ORM框架包括MyBatis、Hibernate。


2、为什么说Mybatis是半自动ORM映射工具? 它与全自动的区别在哪里?

Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。

而Mybatis在查询关联对象或关联集合对象时,需要手动编写sql来完成,所以,称之为半自动ORM映射工具。

3、MyBatis与Hibernate的区别

Hibernate是一个标准的ORM框架,是全表映射框架。

MyBatis是一个半自动映射框架。

总结

MyBatis 是一个小巧、方便、高效、简单、直接、半自动化的持久层框架,

Hibernate 是一个强大、方便、高效、复杂、间接、全自动化的持久层框架。

MyBatis的解析和运行原理

4、MyBatis编程步骤是什么样的?

1、 创建SqlSessionFactory

2、 通过SqlSessionFactory创建SqlSession

3、 通过sqlsession执行数据库操作

4、 调用session.commit()提交事务

5、 调用session.close()关闭会话

5、请说说MyBatis的工作原理

在学习 MyBatis 程序之前,需要了解一下 MyBatis 工作原理,以便于理解程序。MyBatis 的工作原理如下图

1)读取 MyBatis 配置文件:mybatis-config.xml 为 MyBatis 的全局配置文件,配置了 MyBatis 的运行环境等信息,例如数据库连接信息。

2)加载映射文件。映射文件即 SQL 映射文件,该文件中配置了操作数据库的 SQL 语句,需要在 MyBatis 配置文件 mybatis-config.xml 中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。

3)构造会话工厂:通过 MyBatis 的环境等配置信息构建会话工厂 SqlSessionFactory。

4)创建会话对象:由会话工厂创建 SqlSession 对象,该对象中包含了执行 SQL 语句的所有方法。

5)Executor 执行器:MyBatis 底层定义了一个 Executor 接口来操作数据库,它将根据 SqlSession 传递的参数动态地生成需要执行的 SQL 语句,同时负责查询缓存的维护。

6)MappedStatement 对象:在 Executor 接口的执行方法中有一个 MappedStatement 类型的参数,该参数是对映射信息的封装,用于存储要映射的 SQL 语句的 id、参数等信息。

7)输入参数映射:输入参数类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类型。输入参数映射过程类似于 JDBC 对 preparedStatement 对象设置参数的过程。

8)输出结果映射:输出结果类型可以是 Map、 List 等集合类型,也可以是基本数据类型和 POJO 类型。输出结果映射过程类似于 JDBC 对结果集的解析过程。

6、MyBatis的功能架构是怎样的

MyBatis 最上面是接口层,接口层就是开发人员在 Mapper 或者是 Dao 接口中的接口定义,是查询、新增、更新还是删除操作;中间层是数据处理层,主要是配置 Mapper -> XML 层级之间的参数映射,SQL 解析,SQL 执行,结果映射的过程。上述两种流程都由基础支持层来提供功能支撑,基础支持层包括连接管理,事务管理,配置加载,缓存处理等。


我们把Mybatis的功能架构分为三层:

7、Mybatis都有哪些Executor执行器? 它们之间的区别是什么?

Mybatis有三种基本的Executor执行器,SimpleExecutor、ReuseExecutor、BatchExecutor。

作用范围:Executor的这些特点,都严格限制在SqlSession生命周期范围内。

8、Mybatis中如何指定使用哪一种Executor执行器?

在Mybatis配置文件中,在设置(settings)可以指定默认的ExecutorType执行器类型,也可以手动给DefaultSqlSessionFactory的创建SqlSession的方法传递ExecutorType类型参数,如SqlSession openSession(ExecutorType execType)。

配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements);BATCH 执行器将重用语句并执行批量更新。

9、Mybatis是否支持延迟加载? 如果支持,它的实现原理是什么?

Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false。

它的原理是,使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。这就是延迟加载的基本原理。

当然了,不光是Mybatis,几乎所有的包括Hibernate,支持延迟加载的原理都是一样的。

10、什么是MyBatis的接口绑定? 有哪些实现方式?

接口绑定,就是在MyBatis中任意定义接口,然后把接口里面的方法和SQL语句绑定,我们直接调用接口方法就可以,这样比起原来了SqlSession提供的方法我们可以有更加灵活的选择和设置。

接口绑定有两种实现方式

通过注解绑定,就是在接口的方法上面加上 @Select、@Update等注解,里面包含Sql语句来绑定;

通过xml里面写SQL来绑定, 在这种情况下,要指定xml映射文件里面的namespace必须为接口的全路径名。当Sql语句比较简单时候,用注解绑定, 当SQL语句比较复杂时候,用xml绑定,一般用xml绑定的比较多。

11、使用MyBatis的mapper接口调用时有哪些要求?

1、Mapper接口方法名和mapper.xml中定义的每个sql的id相同。

2、Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同。

3、Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同。

4、Mapper.xml文件中的namespace即是mapper接口的类路径。

12、Mybatis动态sql是做什么的? 都有哪些动态sql? 能简述一下动态sql的执行原理不?

Mybatis动态sql可以让我们在Xml映射文件内,以标签的形式编写动态sql,完成逻辑判断和动态拼接sql的功能,Mybatis提供了9种动态sql标签trim|where|set|foreach|if|choose|when|otherwise|bind。

其执行原理为,使用OGNL从sql参数对象中计算表达式的值,根据表达式的值动态拼接sql,以此来完成动态sql的功能。

插件模块

13、Mybatis是如何进行分页的? 分页插件的原理是什么?

举例:select * from student,拦截sql后重写为:select t.* from (select * from student) t limit 0, 10

14、简述Mybatis的插件运行原理,以及如何编写一个插件。

Mybatis仅可以编写针对ParameterHandler、ResultSetHandler、StatementHandler、Executor这4种接口的插件,Mybatis使用JDK的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这4种接口对象的方法时,就会进入拦截方法,具体就是InvocationHandler的invoke()方法,当然,只会拦截那些你指定需要拦截的方法。

实现Mybatis的Interceptor接口并复写intercept()方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,记住,别忘了在配置文件中配置你编写的插件。

15、缓存 Mybatis的一级、二级缓存

1)一级缓存: 基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该 Session 中的所有 Cache 就将清空,默认打开一级缓存。

2)二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap 存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现Serializable序列化接口(可用来保存对象的状态),可在它的映射文件中配置<cache/> ;

3)对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存Namespaces)的进行了C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear。

16、Mybatis源码中用了哪些设计模式?

  1. Builder模式,例如SqlSessionFactoryBuilder、XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder、CacheBuilder;
  2. 工厂模式 ,例如SqlSessionFactory、ObjectFactory、MapperProxyFactory;
  3. 单例模式 ,例如ErrorContext和LogFactory;
  4. 代理模式 ,Mybatis实现的核心,比如MapperProxy、ConnectionLogger,用的jdk的动态代理;还有executor.loader包使用了cglib或者javassist达到延迟加载的效果;
  5. 组合模式 ,例如SqlNode和各个子类ChooseSqlNode等;
  6. 模板方法模式 ,例如BaseExecutor和SimpleExecutor,还有BaseTypeHandler和所有的子类例如IntegerTypeHandler;
  7. 适配器模式 ,例如Log的Mybatis接口和它对jdbc、log4j等各种日志框架的适配实现;
  8. 装饰者模式 ,例如Cache包中的cache.decorators子包中等各个装饰者的实现;
  9. 迭代器模式 ,例如迭代器模式PropertyTokenizer;

至于为什么要用这些设计模式,大家可以观看我的往期文章,里面会给你想要的答案。

看完如果觉得有收获的话,可以帮我点一个在看,谢谢你的支持。

spring

一、关于Spring

Spring是一个分层的Java SE/EE应用一站式的 轻量级开源框架

Spring主要优点包括:

Spring核心是 IOCAOP

IOC

IOC(Inversion Of Controll,控制反转)是一种设计思想,将原本在程序中手动创建对象的控制权,交由给Spring框架来管理。IOC容器是Spring用来实现IOC的载体,IOC容器实际上就是一个Map(key, value),Map中存放的是各种对象。

AOP

AOP(Aspect-Oriented Programming,面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可扩展性和可维护性。使用AOP之后我们可以把一些通用功能抽象出来,在需要用到的地方直接使用即可,这样可以大大简化代码量,提高了系统的扩展性。

Spring AOP / AspectJ AOP 的区别?

Spring AOP属于运行时增强,而AspectJ是编译时增强。

Spring AOP基于代理(Proxying),而AspectJ基于字节码操作(Bytecode Manipulation)。

AspectJ相比于Spring AOP功能更加强大,但是Spring AOP相对来说更简单。如果切面比较少,那么两者性能差异不大。但是,当切面太多的话,最好选择AspectJ,它比SpringAOP快很多。

二、Spring体系结构

如下图所示,整个spring框架按其所属功能可以划分为五个主要模块,这五个模块几乎为企业应用提供了所需的一切,从持久层、业务层到表现层都拥有相应的支持,这就是为什么称Spring是一站式框架的原因。

1、核心模块(Core Container)

Spring的核心模块实现了IoC的功能,它将类和类之间的依赖从代码中脱离出来,用配置的方式进行依赖关系描述。由IoC容器负责类的创建,管理,获取等。BeanFactory接口是Spring框架的核心接口,实现了容器很多核心的功能。

Context模块构建于核心模块之上,扩展了BeanFactory的功能,包括国际化,资源加载,邮件服务,任务调度等多项功能。ApplicationContext是Context模块的核心接口。

表达式语言(Expression Language)是统一表达式语言(EL)的一个扩展,支持设置和获取对象属性,调用对象方法,操作数组、集合等。使用它可以很方便的通过表达式和Spring IoC容器进行交互。

2、AOP模块

Spring AOP模块提供了满足AOP Alliance规范的实现,还整合了AspectJ这种AOP语言级的框架。通过AOP能降低耦合。

3、数据访问集成模块(Data Access/Integration )

该模块包括了JDBC、ORM、OXM、JMS和事务管理:

4、Web模块

该模块建立在ApplicationContext模块之上,提供了Web应用的功能,如文件上传、FreeMarker等。Spring可以整合Struts2等MVC框架。此外,Spring自己提供了MVC框架Spring MVC。

5、测试模块

Spring可以用非容器依赖的编程方式进行几乎所有的测试工作,支持JUnit和TestNG等测试框架。

最后

看完本次分享的内容,你是否对Spring有了一个更深层次的认识了呢?

如果有哪位有缘的朋友看到了这篇文章,恰好有对本次Spring分享的内容补充 ,欢迎留言交流。

这三块内容特别重要,希望大家能够融汇贯通,最后给大家分享一波一线大厂面试题大全,比较全面,转发此文关注小编之后,私信小编“面试”即可得到获取方式!


相关推荐

快递查询教程,批量查询物流,一键管理快递

作为商家,每天需要查询许许多多的快递单号,面对不同的快递公司,有没有简单一点的物流查询方法呢?小编的回答当然是有的,下面随小编一起来试试这个新技巧。需要哪些工具?安装一个快递批量查询高手快递单号怎么快...

一键自动查询所有快递的物流信息 支持圆通、韵达等多家快递

对于各位商家来说拥有一个好的快递软件,能够有效的提高自己的工作效率,在管理快递单号的时候都需要对单号进行表格整理,那怎么样能够快速的查询所有单号信息,并自动生成表格呢?1、其实方法很简单,我们不需要一...

快递查询单号查询,怎么查物流到哪了

输入单号怎么查快递到哪里去了呢?今天小编给大家分享一个新的技巧,它支持多家快递,一次能查询多个单号物流,还可对查询到的物流进行分析、筛选以及导出,下面一起来试试。需要哪些工具?安装一个快递批量查询高手...

3分钟查询物流,教你一键批量查询全部物流信息

很多朋友在问,如何在短时间内把单号的物流信息查询出来,查询完成后筛选已签收件、筛选未签收件,今天小编就分享一款物流查询神器,感兴趣的朋友接着往下看。第一步,运行【快递批量查询高手】在主界面中点击【添...

快递单号查询,一次性查询全部物流信息

现在各种快递的查询方式,各有各的好,各有各的劣,总的来说,还是有比较方便的。今天小编就给大家分享一个新的技巧,支持多家快递,一次能查询多个单号的物流,还能对查询到的物流进行分析、筛选以及导出,下面一起...

快递查询工具,批量查询多个快递快递单号的物流状态、签收时间

最近有朋友在问,怎么快速查询单号的物流信息呢?除了官网,还有没有更简单的方法呢?小编的回答当然是有的,下面一起来看看。需要哪些工具?安装一个快递批量查询高手多个京东的快递单号怎么快速查询?进入快递批量...

快递查询软件,自动识别查询快递单号查询方法

当你拥有多个快递单号的时候,该如何快速查询物流信息?比如单号没有快递公司时,又该如何自动识别再去查询呢?不知道如何操作的宝贝们,下面随小编一起来试试。需要哪些工具?安装一个快递批量查询高手快递单号若干...

教你怎样查询快递查询单号并保存物流信息

商家发货,快递揽收后,一般会直接手动复制到官网上一个个查询物流,那么久而久之,就会觉得查询变得特别繁琐,今天小编给大家分享一个新的技巧,下面一起来试试。教程之前,我们来预览一下用快递批量查询高手...

简单几步骤查询所有快递物流信息

在高峰期订单量大的时候,可能需要一双手当十双手去查询快递物流,但是由于逐一去查询,效率极低,追踪困难。那么今天小编给大家分享一个新的技巧,一次能查询多个快递单号的物流,下面一起来学习一下,希望能给大家...

物流单号查询,如何查询快递信息,按最后更新时间搜索需要的单号

最近有很多朋友在问,如何通过快递单号查询物流信息,并按最后更新时间搜索出需要的单号呢?下面随小编一起来试试吧。需要哪些工具?安装一个快递批量查询高手快递单号若干怎么快速查询?运行【快递批量查询高手】...

连续保存新单号功能解析,导入单号查询并自动识别批量查快递信息

快递查询已经成为我们日常生活中不可或缺的一部分。然而,面对海量的快递单号,如何高效、准确地查询每一个快递的物流信息,成为了许多人头疼的问题。幸运的是,随着科技的进步,一款名为“快递批量查询高手”的软件...

快递查询教程,快递单号查询,筛选更新量为1的单号

最近有很多朋友在问,怎么快速查询快递单号的物流,并筛选出更新量为1的单号呢?今天小编给大家分享一个新方法,一起来试试吧。需要哪些工具?安装一个快递批量查询高手多个快递单号怎么快速查询?运行【快递批量查...

掌握批量查询快递动态的技巧,一键查找无信息记录的两种方法解析

在快节奏的商业环境中,高效的物流查询是确保业务顺畅运行的关键。作为快递查询达人,我深知时间的宝贵,因此,今天我将向大家介绍一款强大的工具——快递批量查询高手软件。这款软件能够帮助你批量查询快递动态,一...

从复杂到简单的单号查询,一键清除单号中的符号并批量查快递信息

在繁忙的商务与日常生活中,快递查询已成为不可或缺的一环。然而,面对海量的单号,逐一查询不仅耗时费力,还容易出错。现在,有了快递批量查询高手软件,一切变得简单明了。只需一键,即可搞定单号查询,一键处理单...

物流单号查询,在哪里查询快递

如果在快递单号多的情况,你还在一个个复制粘贴到官网上手动查询,是一件非常麻烦的事情。于是乎今天小编给大家分享一个新的技巧,下面一起来试试。需要哪些工具?安装一个快递批量查询高手快递单号怎么快速查询?...

取消回复欢迎 发表评论: