【高频面试题】框架篇

文章目录

1.Spring框架中的单例bean是线程安全的吗?

不是线程安全的

Spring框架中有一个@Scope注解,默认的值就是singleton,单例的

因为一般在spring的bean中都是注入无状态的对象,没有线程安全问题,如果在bean中定义了可修改的成员变量,是要考虑线程安全问题的
解决办法:

1、将Bean的作用域由 "singleton" 单例 改为 "prototype" 多例

2、在类中定义 ThreadLocal 的成员变量,并将需要的可变成员变量保存在 ThreadLocal 中,ThreadLocal 本身就具备线程隔离的特性,这就相当于为每个线程提供了一个独立的变量副本,每个线程只需要操作自己的线程副本变量,从而解决线程安全问题。

2.什么是AOP?

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

实现的核心:自定义注解+AOP。

流程:

  1. 首先自定义一个注解
  2. 自定义切点拦截所有加了自定义注解的方法
  3. 拦截下来之后,前置通知、后置通知、异常通知、返回通知还是环绕通知,就可以随便写了。

常见的AOP使用场景:

  1. 记录操作日志

  2. 缓存处理

  3. Spring中内置的事务处理

其本质是通过AOP功能,对方法前后进行拦截,在执行方法之前开启事务,在执行完目标方法之后根据执行情况提交或者回滚事务

参考链接:Spring AOP在项目中的典型应用场景


3.Spring中事务失效的场景有哪些

情况一:异常捕获处理

也就是在事务中出现了异常,如果是使用了try catch 主动的将异常处理掉了,那么事务就会失效,但是如果将异常正常抛出,那么事务就不会失效

因为事务回滚的依据就是执行过程中抛出异常被检测到了才会回滚,保持事务的一致性。


情况二:抛出检查异常

Spring 默认只会回滚非检查异常

对于事务在执行的过程中抛出了检查异常,会导致事务失效。

情况三:非public方法导致的事务失效

Spring 为方法创建代理、添加事务通知前提条件都是该方法是 public 的

参考链接:Spring 事务失效的十种常见场景

4.Spring的bean的生命周期

  1. 通过BeanDefinition获取bean的定义信息

Spring容器启动的过程中,会将Bean解析成Spring内部的BeanDefinition结构

  1. 调用构造函数实例化bean

createBeanInstance() :用来初始化 Bean,里面会调用对象的构造方法

  1. bean的依赖注入

populateBean() :属性对象的依赖注入,以及成员变量初始化

  1. 处理Aware接口(BeanNameAware、BeanFactoryAware、ApplicationContextAware)

  2. Bean的后置处理器BeanPostProcessor-前置

  3. 初始化方法(InitializingBean、init-method)

  4. Bean的后置处理器BeanPostProcessor-后置

  5. 销毁bean

    参考链接:Spring 中 Bean 的生命周期是怎样的?


5.Spring中的循环引用

什么是循环引用?

在创建A对象的同时需要使用的B对象,在创建B对象的同时需要使用到A对象

循环依赖会造成什么问题?

会导致A对象和B对象bean创建不成功,导致出现无限寻找对方对象的循环过程。

三级缓存解决循环依赖

三级缓存分别为:

解决流程图:

大致可以理解为:

核心就是使用三级缓存提前曝光Bean对象。

以下通过这两种方式采用三级缓存可以解决循环依赖问题
基于field属性注入

java 复制代码
@Service
public class UserService {
    @Autowired
    private UserDAO userDAO;  //通过属性注入
  
}

基于setter方法注入

java 复制代码
@Service
public class UserService {
    private UserDAO userDAO;
    
    @Autowired  //通过setter方法实现注入
    public void setUserDAO(serDAO userDAO) {
        this.userDAO = userDAO;
    }
}
  1. 通过构建函数创建A对象(A对象是半成品,还没注入属性和调用init方法)。
  2. A对象需要注入B对象,发现缓存里还没有B对象,将半成品对象A放入半成品缓存(三级缓存)。
  3. 通过构建函数创建B对象(B对象是半成品还没注入属性和调用init方法)。
  4. B对象需要注入A对象,从半成品缓存(三级缓存)里取到半成品对象A。
  5. B对象继续注入其他属性和初始化,之后将完成品B对象放入完成品缓存(一级缓存)。
  6. A对象继续注入属性,从完成品缓存中取到完成品B对象并注入。
  7. A对象继续注入其他属性和初始化,之后将完成品A对象放入完成品缓存。

如果是通过构造函数注入依赖的方式可以采用以下方案解决循环依赖问题:

原因:由于bean的生命周期中构造函数是第一个执行的,spring框架并不能解决构造函数的的依赖注入

使用@Lazy进行懒加载,什么时候需要对象再进行bean对象的创建


6.SpringMVC的执行流程

SpringMVC的执行流程分为两个版本:

视图阶段(JSP)

其执行流程为:

  1. 用户发送出请求到前端控制器DispatcherServlet
  2. DispatcherServlet收到请求调用HandlerMapping(处理器映射器)
  3. HandlerMapping找到具体的处理器生成处理器对象及处理器拦截器(如果有),再一起返回给DispatcherServlet。
  4. DispatcherServlet调用HandlerAdapter(处理器适配器)
  5. HandlerAdapter经过适配调用具体的处理器(Handler/Controller)
  6. Controller执行完成返回ModelAndView对象
  7. HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet
  8. DispatcherServlet将ModelAndView传给ViewReslover(视图解析器)
  9. ViewReslover解析后返回具体View(视图)
  10. DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)
  11. DispatcherServlet响应用户

其中:
DispatcherServlet(前端控制器)

DispatcherServlet就是所谓的SpringMVC前端控制器,作为整个SpringMVC的控制中心,相对于一个处理中转站

HandlerMapping(处理器映射器)

HandlerMapping主要用来解析请求url解析出控制器从而映射控制器。

会将请求的URL解析出来,并且根据URL解析出来控制器,使其得到一个请求路径和类名方法的映射

HandlerAdapter(处理器适配器)

HandlerAdapter主要是调度Controller来处理业务逻辑

也就是处理方法参数交给Handler执行方法,以及处理(Handler执行后)方法返回值,从而给前端控制器返回一个逻辑视图(还不能用)

ViewResolver(视图解析器)

ViewResolver接口主要作用是解析DispatcherServlet传递的逻辑视图名,并将解析结果传回给DispatcherServlet

最后由DispatcherServlet渲染视图将带有数据的视图在页面展现出来

前后端分离阶段(接口开发,异步请求)

其执行流程为:

  1. 用户发送出请求到前端控制器DispatcherServlet
  2. DispatcherServlet收到请求调用HandlerMapping(处理器映射器)
  3. HandlerMapping找到具体的处理器,生成处理器对象处理器拦截器(如果有),再一起返回给DispatcherServlet。
  4. DispatcherServlet调用HandlerAdapter(处理器适配器)
  5. HandlerAdapter经过适配调用具体的处理器(Handler/Controller)
  6. 方法上添加了@ResponseBody
  7. 通过HttpMessageConverter来返回结果转换为JSON并响应

相比较与视图阶段(JSP)少了逻辑视图转换的步骤,而是直接通过HttpMessageConverter来返回结果转换为JSON并响应


7.Springboot自动配置原理

举例启动类:

其中,SpringBootApplication注解可以拆分成三个注解:

  1. @SpringBootConfiguration:该注解与 @Configuration 注解作用相同,用来声明当前也是一个配置类。

  2. @ComponentScan:组件扫描,默认扫描当前引导类所在包及其子包

  3. @EnableAutoConfiguration:SpringBoot实现自动化配置的核心注解

@EnableAutoConfiguration

  1. 在@EnableAutoConfiguration注解中,通过@Import注解导入对应的配置选择器,其内部就是读取项目引用的jar包的classpath路径下META-INF/spring.factories文件中的所配置的类的全类名
  2. 在这些配置类中所定义的Bean根据条件注解所指定的条件来决定是否需要将其导入到Spring容器中
  3. 条件判断会有像@ConditionalOnClass这样的注解,判断是否有对应的class文件,如果有则加载该类,把这个配置类的所有的Bean放入spring容器中使用。

例如下面如果导入了redis的相关依赖,那么@ConditionalOnClass注解就可以找到配置类的对应的字节码文件,然后判断spring容器中是否有了这个bean,没有就将bean注入到容器当中,完成自动装配,供程序使用


8.Spring框架常见注解(Spring、Springboot、Springmvc)

8.1 .Spring


8.2 .Springboot


8.3 .Springmvc


9.MyBatis执行流程

  1. 读取MyBatis配置文件:mybatis-config.xml加载运行环境映射文件

其中包括数据库连接信息,mapper.xml映射文件

  1. 构造会话工厂SqlSessionFactory

会话工厂,全局一个,生产sqlSession

  1. 会话工厂创建SqlSession对象(包含了执行SQL语句的所有方法
  2. 操作数据库的接口,Executor执行器同时负责查询缓存的维护
  3. Executor接口的执行方法中有一个MappedStatement类型的参数,封装了映射信息

一个MappedStatement对象代表xml中一个标签

  1. 输入参数映射

将java类型的参数转换为数据库支持的参数

  1. 输出结果映射

将数据库类型的参数转换为java类型输出

执行流程:


10.Mybatis是否支持延迟加载?

Mybatis 仅支持 association 关联对象和 collection 关联集合对象的延迟加载,association 指的就是一对一,collection 指的就是一对多查询。

Mybatis支持延迟加载,但默认没有开启

很好的例子就是一对多的情况:

每一个用户都有对应的多个订单数据


其中:

  • 查询用户的时候,把用户所属的订单数据也查询出来,这个是立即加载
  • 查询用户的时候,暂时不查询订单数据当需要订单的时候,再查询订单,这个就是延迟加载

怎么实现懒加载:

在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false,默认是关闭的

延迟加载的底层原理

使用 CGLIB 创建目标对象的代理对象,当调用目标方法时,进入拦截器invoke()方法,比如查询订单表数据,拦截器 invoke () 方法发现 用户的订单表数据为是 null 值,那么就会单独发送事先保存好的查询订单表的 sql,把 订单表数据查询上来,然后调用 用户对象的set订单表方法,于是 用户的对象订单表属性就有值了。


11.Mybatis的一级、二级缓存用过吗?

  • 本地缓存,基于PerpetualCache,本质是一个HashMap
  • 一级缓存:作用域是session级别(一次会话)
  • 二级缓存:作用域是namespacemapper的作用域不依赖于session

一级缓存: (默认是开启的)

二级缓存: (默认是关闭的)

配置方法:

  1. 在mybatis配置文件里面加上一个标签cacheEnabled设置为true
java 复制代码
<!--  开启二级缓存 -->
<setting name="cacheEnabled" value="true"/>
  1. 在对应的mapper.xml文件加上标签:
java 复制代码
 <cache/>

注意点:

  1. 在当某一个作用域(一级缓存 Session/二级缓存Namespaces)的进行了新增、修改、删除操作后,默认该作用域下所有 select 中的缓存将被 clear
  2. 二级缓存需要缓存的数据实现Serializable接口
  3. 只有在会话提交或关闭时一级缓存中的数据才会转移到二级缓存中(此时这个会话一级缓存也会被清空),以便其他会话可以共享这些数据

更新中-------------

参考来自黑马程序员

相关推荐
xyq20244 分钟前
TypeScript中的String类型详解
开发语言
2301_813599551 小时前
Go语言怎么做秒杀系统_Go语言秒杀系统实战教程【实用】
jvm·数据库·python
NCIN EXPE6 小时前
redis 使用
数据库·redis·缓存
MongoDB 数据平台6 小时前
为编码代理引入 MongoDB 代理技能和插件
数据库·mongodb
lUie INGA6 小时前
在2023idea中如何创建SpringBoot
java·spring boot·后端
极客on之路6 小时前
mysql explain type 各个字段解释
数据库·mysql
代码雕刻家6 小时前
MySQL与SQL Server的基本指令
数据库·mysql·sqlserver
lThE ANDE6 小时前
开启mysql的binlog日志
数据库·mysql
小糖学代码6 小时前
LLM系列:1.python入门:15.JSON 数据处理与操作
开发语言·python·json·aigc
yejqvow126 小时前
CSS如何控制placeholder文字的颜色_使用--placeholder伪元素
jvm·数据库·python