Java后端面试:框架篇高频面试(Spring、SpringMVC、SpringBoot、MyBatis)

👨‍🎓作者简介:一位大四、研0学生,正在努力准备大四暑假的实习
🌌上期文章:Java后端面试:MySQL面试篇(底层事务、SQL调优)
📚订阅专栏:Java后端面试
希望文章对你们有所帮助

框架篇高频面试(Spring、SpringMVC、SpringBoot、MyBatis)

  • Spring
    • 单例bean是线程安全的吗?
    • AOP相关面试题
    • Spring事务失效的场景
    • bean的生命周期
    • bean的循环依赖(循环引用)
  • SpringMVC-执行流程
    • 视图阶段(JSP)
    • 前后端分离阶段
  • SpringBoot-自动装配原理
  • Spring框架常见注解(Spring、SpringMVC、SpringBoot)
  • MyBatis
    • 执行流程
    • 延迟加载使用及原理
    • 一级、二级缓存

Spring

单例bean是线程安全的吗?

通过@Scope注解可以设置bean是单例的还是多例的,默认是单例的。
而单例bean线程不安全

Spring的bean中都是注入无状态的对象,也就是说无法修改,不会有线程安全问题。
但是如果在bean中定义了可修改的成员变量,是要考虑线程安全问题的,当然可以使用多例模式或者加锁来解决。

AOP相关面试题

AOP很重要,在Spring也算是一个难点吧,但是像IOC、AOP这种Spring核心,不会的话说不过去,这里就简单说说。

1、何为AOP?
AOP称为面向切面编程,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被称为切面(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。

2、常见的AOP使用场景?

1、记录操作日志(自行学习@Aspect、切点、环绕通知)
2、缓存处理
3、Spring中内置的事务处理

3、Spring中的事务是如何实现的?(一定要提到AOP!)

Spring支持编程式事务管理和声明式事务管理两种方式,主要使用的是声明式事务
声明式事务管理是建立在AOP上的。本质是通过AOP功能,对方法的前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前加入一个事务,在执行目标方法之后执行情况提交或者回滚事务。(@Transactional注解就是这样的,其底层的实现(开启事务、提交/回滚事务)就是AOP追加的)

Spring事务失效的场景

这种问题能够体现出我们对Spring框架的深入理解,以及复杂业务的编码经验。

有三种非常常见的事务失效的情况:

1、异常捕获处理

对于转账的业务,我们会给整个业务增加@Transactional注解,如果中途发生了异常我们就会回滚。
但是如果这个业务方法中使用了try…catch,中途发生异常以后,@Transactional将会失效。
原因:事务通知只有捉到了目标抛出的异常,才能进行后续的回滚处理,如果目标自己处理掉异常,事务通知将会无法知悉
解决方法:在catch块中添加throw new RuntimeException(E),将异常给抛出,不要处理。

2、抛出检查异常

依旧是转账业务,这次不用try…catch,而是在方法体使用throws来抛出检查异常(类似FileNotFoundException这种非运行异常),这种情况也会导致事务失效。
原因:Spring默认只会回滚非检查异常
解决方法:配置rollbackFor属性:@Transactional(rollbackFor=Exception.class),这样事务回滚就不只是针对运行时异常了。

3、非public方法

如果没有用public修饰方法,就会导致事务失效。
原因:Spring为方法创建代理、添加事务通知,前提条件都是该方法是public的
解决:改为public方法

bean的生命周期

这个在业务开发过程中重要吗?可能真的不太重要,这就是个纯八股。但是面试官还是挺喜欢问的,可能是掌握生命周期能显得你更了解框架,方便调试和解决问题。

1、通过BeanDefinition获取bean的定义信息
2、调用构造函数实例化bean
3、bean的依赖注入
4、处理Aware接口
5、Bean的后置处理器BeanPostProcessor-前置
6、初始化方法
7、Bean的后置处理器BeanPostProcessor-后置
8、销毁bean

bean的循环依赖(循环引用)

如果是两个bean,成员变量分别用对方来注入,这就是循环依赖。
这种方式是会出现死循环的,对于A,先实例化bean,再初始化,初始化的过程中需要设置b属性,而b是B类型的对象,可是容器中不存在B对象,就会去实例化B的bean对象,再去初始化,初始化的时候需要设置a属性,a是A类型的对象,但是容器中也不存在A对象,最后造成了死循环。

Spring框架已经帮助我们解决了大部分的循环依赖问题,通过的是三级缓存:

一级缓存:单力池,缓存已经经历了完整声明周期,即已经初始化完成的bean对象
二级缓存:缓存早期的bean对象(生命周期还没走完)
三级缓存:缓存的是对象工厂ObjectFactory,用来创建某个对象的

整体流程非常的复杂,大家可以自行去看相关资料来理解,写起来很麻烦,但要记住一级缓存+二级缓存可以解决大部分的循环依赖问题,但是解决不了存在代理对象时的问题,而一级+二级+三级可以解决代理对象的循环依赖问题,过程需要理解。

这些都是发生在初始化的时候,而如果我们在声明周期的构造方法换发出现了循环依赖,也就是注入的方式是用构造函数。解决是使用@Lazy进行懒加载,什么时候需要对象了再进行bean对象的创建就好了。

SpringMVC-执行流程

如果要问到SpringMVC,那么执行流程可以说是非常重要的。
但是其实开发分为了两个阶段,一个是视图阶段(老旧的JSP等),另外一个是前后端分离阶段(接口开发,异步),现在我们都是用的前后端分离的开发,但是视图阶段的开发也是面试可能会问到的,很繁琐,得记住。

视图阶段(JSP)

在这里插入图片描述
1、对于一个浏览器发起的请求,视图阶段的执行流程如下:

1、浏览器发起请求给前端控制器DispatcherServlet
2、查询handler:前端控制器根据前端请求,去处理器映射器HandlerMapping中查询对应的执行方法
3、返回处理器执行链:因为对于一个请求,除了要找到这个路径对应的类名.方法名,还需要判断它是否会被拦截器拦截,因此处理器映射器不是直接返回方法给前端控制器,而是返回一个处理器执行链(处理器执行链可以暂且理解成方法handler+拦截器
4、请求执行handler:处理器执行链不会被拦截,那么前端控制器就要开始执行方法handler,这个执行需要向处理器适配器去请求,而不是直接到处理器中(适配器模式),用适配器有两个好处:
(1)可以处理请求的参数
(2)处理返回值
5、处理器适配器向处理器请求
6、处理器将响应返回给处理器适配器,这个响应就是ModelAndView
7、处理器适配器再将ModelAndView返回给前端控制器
8、前端控制器返回视图解析器ViewResolver,这个视图解析器的作用是将逻辑视图解析为真正的视图View
9、视图解析器将真正的视图View返回给前端控制器
10、前端控制器将视图渲染

2、SpringMVC执行过程中重要的四个组件

1、前端控制器DispatcherServlet(调度中心,处理所有的请求)
2、处理器映射器HandlerMapping(通过路径,查询方法handler,返回处理器执行链)
3、处理器适配器HandlerAdaptor(执行handler、处理handler中的参数)
4、视图解析器ViewResolver(将逻辑视图ModelAndView解析为真正的视图View)

前后端分离阶段

现在的开发不太一样,没有ModelAndView了,而是返回的Json数据,流程将会简化为:
在这里插入图片描述
这时候处理器Handler的执行是变化了:

1、处理器Handler的方法上添加@ResponseBody注解
2、底层会通过一个转化器HttpMessageConverter将返回结果转化为JSON格式响应给前端

SpringBoot-自动装配原理

这是SpringBoot最高频的面试题了,也是框架的最核心思想。

我们的SpringBoot启动类中一定会有一个注解:@SpringBootApplication,这个注解底层包含了三个部分:

@SpringBootConfiguration:与@Configuration相同,用来声明当前是个配置类
@ComponentScan:组件扫描,默认扫描当前引导类所在包及其子包
@EnableAutoConfiguration:SpringBoot实现自动化配置的核心注解

其中,@EnableAutoConfiguration是实现自动化配置的核心注解,该注解底层通过@Import来导入对应的配置选择器。

其内部就是读取该项目和该项目引用的jar包的classpath路径下META-INF/spring.factories文件中的所配置的类的全类名。在这些配置类中所定义的Bean会根据条件注解所指定的条件来决定是否需要将其导入到Spring容器中。
这个条件判断会有@ConditionalOnClass这样的注解,判断是否有对应的class文件(字节码文件),若有则加载该类,把这个配置类中所有的Bean都放入Spring容器中使用。

Spring框架常见注解(Spring、SpringMVC、SpringBoot)

Spring的注解主要是用来进行bean的实例化以及依赖注入的,SpringMVC的注解主要是用来处理请求和响应的,而SpringBoot的注解尽量说一下跟自动装配有关系的,被问到不要混淆着讲。
1、Spring注解:

注解说明
@Componennt、@Controller、@Service、@Repository使用在类上,用于实例化bean
@Autowired在字段上根据类型依赖注入
@Qualifier结合@Autowired一起使用,用于根据名称进行依赖注入
@Scope标注Bean的作用返回(单例or多例)
@Configuration指定当前类是一个Spring配置类,当创建容器时会从该类上加载注解
@ComponentScan用于指定Spring在初始化容器时要扫描的包
@Bean用在方法上,标注该方法的返回值会存到Spring容器中
@Import该注解导入的类会被Spring加载到IOC容器中
@Aspect、@Before、@After、@Around、@Pointcut用于AOP(切面、前置通知、后置通知、环绕通知、切入点表达式)

2、SpringMVC注解:

注解说明
@RequestMapping(也有衍生的PostMapping、GetMapping等)用于映射请求路径,定义在类上或方法上,用在类上则该类中的所有方法都是以该地址作为父路径的
@RequestBody接受http请求的json数据,将json转化为java对象
@ResponseBody将controller方法返回的对象转化为json对象响应给客户端
@RequestParam指定请求参数的名称(和数据库字段名不一致时使用)
@PathViriable从请求路径下获取请求参数/user/{id},传递给方法的形式参数(Restful风格)
@RequestHeader获取指定的请求头数据
@RestController@Controller+@ResponseBody

3、SpringBoot注解:

注解说明
@SpringBootConfiguration配置文件
@EnableAutoConfiguration打开自动配置
@ComponentScanSpring组件扫描

MyBatis

执行流程

1、读取MyBatis配置文件mybatis-config.xml,加载运行环境(数据库相关信息)和mapper映射文件
2、构造会话工厂SqlSessionFactory,会话工厂是全局唯一的
3、会话工厂创建SqlSession对象(包含了执行SQL语句的所有方法),每次操作都会创建出一个会话
4、Executor执行器是真正操作数据库的接口,同时负责了查询缓存的维护
5、Executor接口的执行方法中有一个MappedStatement类型的参数,封装了映射信息
6、输入参数的映射(将java类型转化为数据库处理的类型)
7、输出结果映射(将数据库操作的结果映射成java类型)

延迟加载使用及原理

1、延迟加载是需要用到数据的时候才会加载,不需要的时候就不会加载。
2、MyBatis是支持延迟加载的,但是默认是没有开启的。可以局部开启或者全局开启。
3、延迟加载的底层原理:

(1)使用CGLIB创建目标对象的代理对象
(2)当调用方法的时候,进入拦截器invoke方法,发现目标方法是null值,执行SQL查询
(3)获取数据后,调用set方法设置属性值,再继续查询目标方法,就有值了

一级、二级缓存

一级缓存:基于HashMap本地缓存,其存储作用域为SqlSession,当SqlSession进行flush或close之后,该Session中的Cache就被清空(默认开启)
二级缓存:基于namespace和mapper的作用域起作用的,不依赖于SqlSession,默认也是基于HashMap存储,需要单独开启(通过核心配置文件)

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

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

相关文章

十四、ReadWriteLock

ReadWriteLock 读写锁 又叫排他锁 如果使用互斥锁,一个线程在读,其他线程也不能读也不能写 换成读写锁的时候,读线程是读锁,写线程是写锁,写锁是排他的 在多线程大大提高效率,当一个线程在读的时候&…

glib交叉编译

Glib交叉编译 逸一时,误一世。 —— 田所浩二「夏夜银梦」 交叉编译 GLib 涉及到在一个平台上生成能够在另一个平台上运行的目标文件。在这种情况下,我们将会在一台主机(通常是开发机器)上使用交叉编译工具链来构建 GLib 库&#…

从历年315曝光案例,看APP隐私合规安全

更多网络安全干货内容:点此获取 ——————— 随着移动互联网新兴技术的发展与普及,移动APP的应用渗透到人们的衣食住行方方面面,衍生出各类消费场景的同时,也带来了无数的个人隐私数据泄露、网络诈骗事件。 历年来&#xff…

机器人在果园内行巡检仿真

文章目录 创建工作空间仿真果园场景搭建小车模型搭建将机器人放在仿真世界中创建工作空间 mkdir -p ~/catkin_ws/src cd ~/catkin_ws仿真果园场景搭建 cd ~/catkin_ws/src git clone https://gitcode.com/clearpathrobotics/cpr_gazebo.git小车模型搭建 DiffBot是一种具有两个…

Vmware虚拟机配置虚拟网卡

背景 今天同事咨询了我一个关于虚拟机的问题,关于内网用Vmware安装的虚拟机,无法通过本机访问虚拟上的Jenkins的服务。   验证多次后发现有如下几方面问题。 Jenkins程序包和JDK版本不兼容(JDK1.8对应Jenkins不要超过2.3.57)虚…

信号量——生产消费者模型

前文 在这一篇博客(信号量博客)中我曾经提及过信号量的知识,而当对信号量进行提炼总结时,大致是以下三点: 1. 信号量本质是一个计数器(代表资源的数量) 2. 申请信号量本质就是对资源的一种预定机…

final关键字

final关键字 基本介绍final使用细节 基本介绍 final 中文意思:最后的,最终的。 final 可以修饰类、属性、方法和局部变量。 在某些情况下会使用到final: 1) 当不希望类被继承时,可以用 final 修饰; // 如…

Python--成员方法、@staticmethod将成员方法静态化、self参数释义

在 Python 中,成员方法是指定义在类中的函数,用于操作类的实例对象。成员方法通过第一个参数通常命名为 self,用来表示调用该方法的实例对象本身。通过成员方法,可以实现类的行为和功能。 成员方法的定义 在类中定义成员…

【Linux】Linux上代码的编译与调试

目录 Linux上常用的编译器gcc\g 如何使用gcc/g 编译过程: 如何使用gcc编译? 进行预处理 进行编译 进行汇编 进行链接 函数库 函数库的分类 gcc选项 Linux调试器-gdb的使用 gdb的常用参数 Linux项目自动化构建工具make/Makefile 原理 利用…

MYSQL日志 redo_log更新流程 bin_log以及bin_log数据恢复

Redo_log写入策略 Redo log的Innodb_flush_log_at_trx_commit:: 这个参数有三个取值 取值为0:每次事务提交时,只是把redo_log留在 redo log buffer中,宕机会丢失数据; 取值为1(默认值):每次事…

1.中医学习-总论

目录 1.为什么要学中医 2.什么是中医 介绍 中医例子1: 中医例子2: 中医最高境界“大道至简” 中医讲究的是本质 中医核心:阴阳、表里、寒热、虚实 ​编辑医不叩门 3.阴阳 1.一天中的阴阳 2.一年中的阴阳 3.阴阳之间的关系 4.阴阳四季的变化 …

解决:visio导出公式为pdf图片乱码问题

今天需要将Visio编辑好的以后的图输出pdf,但是点击保存后公式部分一直乱码,如下图所示 保存为pdf后会变成: 解决方案:保存时点击文件下方的快速打印,存到桌面,不要直接点击保存

代码随想录算法训练营第二十五天|216.组合总和III,17.电话号码的字母组合

216.组合总和III 题目 找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。 说明: 所有数字都是正整数。 解集不能包含重复的组合。 示例 1: 输入: k 3, n 7 输出: [[1,2,4]] 示例 2: 输入…

24计算机考研调剂 | 【官方】山东师范大学(22自命题)

山东师范大学2024年拟接收调剂 考研调剂信息 调剂专业目录如下: 计算机技术(085404)、软件工程(085405) 补充内容 我校2024年硕士研究生调剂工作将于4月8日教育部“中国研究生招生信息网”(https://yz.ch…

深入了解JVM底层原理

一、JVM内存结构 1、方法区:存储编译后的类、常量等(.class字节码文件) 2、堆内存:存储对象 3、程序计数器:存储当前执行的指令地址(计算机处理器(CPU)正在执行的下一条指令在内存…

openwrt下部署clouddrive2

在启动项上增加启动参数 在exit 0前面增加 mount --make-shared /mnt/data480g注意,后面的/mnt/data480g要替换成你设置的共享映射券。 拉取镜像 docker pull cloudnas/clouddrive2启动镜像 一定要用ssh在后台用docker run命令启动,因为openwrt前台…

函数-Python

师从黑马程序员 函数初体验 str1"asdf" str2"qewrew" str3"rtyuio" def my_len(data):count0for i in data:count1print(f"字符串{data}的长度是{count}")my_len(str1) my_len(str2) my_len(str3) 函数的定义 函数的调用 函数名&a…

基于Java的大学计算机课程管理平台(Vue.js+SpringBoot)

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 实验课程档案模块2.2 实验资源模块2.3 学生实验模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 实验课程档案表3.2.2 实验资源表3.2.3 学生实验表 四、系统展示五、核心代码5.1 一键生成实验5.2 提交实验5.3 批阅实…

面试笔记——Redis(缓存击穿、缓存雪崩)

缓存击穿 缓存击穿(Cache Breakdown): 当某个缓存键的缓存失效时(如,过期时间),同时有大量的请求到达,并且这些请求都需要获取相同的数据,这些请求会同时绕过缓存系统&a…

【GameFramework框架内置模块】8、文件系统(File System)

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址 大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。 一、前言 【GameFramework框架】系列教程目录: https://blog.csdn.net/q7…