1、Spring框架中的单例bean是线程安全的吗?
所谓单例就是所有的请求都用一个对象来处理,而多例则指每个请求用一个新的对象来处理。
结论:线程不安全。
Spring框架中有一个@Scope注解,默认的值就是singleton,单例的。一般在spring的bean都是注入无状态的对象(如service),无状态对象没有线程安全问题;但如果在bean中定义了可修改的成员变量,所有线程都共享一个单例bean,此时需要考虑线程安全问题,可使用多例或者加锁解决。
为什么bean默认是单例的?
Java在创建Java实例时,需要进行内存申请;销毁实例时,需要完成垃圾回收,这些工作都会导致系统开销的增加。因此,prototype作用域Bean的创建、销毁代价比较大。而singleton作用域的Bean实例一旦创建成功,可以重复使用。因此,除非必要,否则尽量避免将Bean被设置成prototype作用域。
AOP相关(参考文章 面向切面编程AOP)
2、什么是AOP?
即面向切面编程。将与核心业务无关的代码(即交叉业务)独立的抽取出来,形成一个独立的组件,然后以横向交叉的方式应用到业务流程当中。有利于降低模块之间的耦合。
3、项目中哪里使用了AOP?
- 记录操作日志。
- 使用 aop 中的环绕通知 +切点表达式。
- 切点表达式用来定义通知(Advice)往哪些方法上切入 。
-
- spring实现事务。
4、Spring中的事务是如何实现的?
使用了@Transactional注解后事务的自动提交功能就会关闭,由spring帮助实现事务的控制。spring实现的事务本质就是由aop完成:对方法前后进行拦截,在执行方法之前 开启事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
5、Spring中事务失效的场景有哪些?
- 异常捕获处理,自己处理了异常,没有抛出,spring不会进行回滚操作。
- 解决:捕获异常后,手动抛出。
- spring默认只会滚非检查异常,如抛出检查异常,也会导致事务失效。
- 解决:@Transactional注解上配置rollbackFor属性为Exception,任意异常都会回滚。
- 事务方法不是public修饰的,也会导致事务失效。
6、Spring的bean的生命周期
Spring Bean 的生命周期主要指的是 singleton bean,对于 prototype bean ,Spring 在创建好交给使用者之后则不会再管理后续的生命周期。
spring在实例化bean之前,会根据BeanDefinition获取bean的定义信息,如:类的全路径、是否单例、是否延迟加载等等...
- 调用构造函数实例化bean。
- bean的依赖注入。(如:set注入、autowire注入等)
- 检查bean是否实现了Aware的相关接口,并设置相关依赖。
- bean后处理器的before方法。
- bean的初始化方法。
- bean后处理器的after方法。
- 使用bean。
- 销毁bean。
7、Spring中的循环依赖
即一个实例或多个实例存在相互依赖的关系,有点像死锁。
- singleton下的set注入:
- spring可以解决此情况的循环依赖问题:在set注入下,“实例化Bean”和“给Bean属性赋值”两个动作可以分开,又因为bean是单例的,所以在实例化完之后可以直接“曝光”。
- prototype下的set注入:
- spring无法解决,但只要其中一个bean对象改为单例就能解决。
-
singleton下的构造注入:
-
spring无法解决。构造注入会导致“实例化Bean”和“给Bean属性赋值”两个动作无法分开,单例bean来不及“曝光”。可以使用注解@Lazy懒加载,什么时候需要对象再进行bean对象的创建。
-
spring解决循环依赖的三级缓存:
- 一级缓存:存储的是完整的单例Bean对象,这个Bean对象已经赋值过了。
- 二级缓存:存储的是早期的单例Bean对象,这个Bean对象属性还没有赋值。
- 三级缓存:存储的是单例工厂对象,每一个单例Bean对象都会对应一个单例工厂对象。
解决流程:
- 先实例化A对象,同时创建其工厂对象存入三级缓存。
- A对象属性赋值需要B对象,实例化B对象,同时创建B的工厂对象,存入三级缓存。
- B属性赋值需要注入A对象,于是从三级缓存中获取A的工厂对象,生成A对象存入二级缓存。
- B通过二级缓存里获取A对象,属性赋值成功,于是B对象创建成功,存入一级缓存。
- 此时A对象从一级缓存中获取B对象,注入成功,并将A对象存入一级缓存。
- 将二级缓存的临时对象A清除。
8、SpringMVC的执行流程
基于前后端分离开发的执行流程:
- 用户发送出请求到前端控制器DispatcherServlet
- DispatcherServlet收到请求调用HandlerMapping(处理器映射器)
- HandlerMapping找到具体的处理器,生成处理器对象及处理器拦截器(如果有),再一起返回给DispatcherServlet。
- DispatcherServlet调用HandlerAdapter(处理器适配器)
- HandlerAdapter经过适配调用具体的处理器(Handler/Controller)
- 方法上添加了@ResponseBody,通过HttpMessageConverter将返回结果转换为JSON并响应
9、Springboot自动配置原理
在Spring Boot项目中的引导类上有一个注解@SpringBootApplication,这个注解是对三个注解进行了封装,分别是:
- @SpringBootConfiguration
- @EnableAutoConfiguration
- @ComponentScan
10、Spring框架常见注解
Spring:
- @Component、@Controller、@Service、@Repository:将实体类对象实例化到spring中,纳入spring管理。
- @Autowired:对类成员变量、方法及构造函数进行自动装配,默认根据类型自动装配。
- @Qualifier:结合@Autowired一起使用用于根据名称进行自动装配。(同一接口有多个实现类,Autowired不知道装配哪个类型)
- @Scope:标注Bean的作用范围。
- @Configuration:指定当前类是一个 Spring 配置类,当创建容器时会从该类上加载注解。
- @ComponentScan:用于指定 Spring 在初始化容器时要扫描的包。
- @Bean:使用在方法上,标注将该方法的返回值存储到Spring容器中。
- @Import:使用@Import导入的类会被Spring加载到IOC容器中。
- @Aspect、@Before、@After、@Around、@Pointcut:用于切面编程(AOP)
SpringMVC:
- @RequestMapping:用于将任意HTTP 请求映射到控制器方法上。
- 各种衍生注解,如:@GetMapping、@PostMapping、@PutMapping等。
- @RequestBody:将前端传过来的 json数据转化为java对象。
- @RequestParam:将请求参数绑定到你控制器的方法参数上。
- @PathViriable:从请求路径下中获取请求参数(/user/{id}),传递给方法的形参。
- @ResponseBody:将controller方法返回的java对象转化为json对象响应给客户端。
- @RequestHeader:将请求头中的参数值映射到控制器的参数中。
- @RestController:@Controller + @ResponseBody
Spring boot:
- @SpringBootConfiguration:用于定义配置类,可替换xml文件。
- @EnableAutoConfiguration:打开自动配置功能。
- @ComponentScan:扫描被@Component (@Service,@Controller)注解的 bean。
11、MyBatis执行流程
- 读取MyBatis核心配置文件:mybatis-config.xml,加载运行环境和映射文件。运行环境包括:事务管理器的配置 + 数据源的配置 ,见MyBatis核心配置文件详解
- 加载映射文件:映射文件即SQL映射文件(mapper.xml),配置了操作数据库的SQL语句。
- 构造会话工厂SqlSessionFactory对象:使用SqlSessionFactoryBuilder对象构建。
- 创建会话对象SqlSession:由会话工厂创建,对象中包含了执行SQL语句的所有方法,每个线程都应该有它自己的 SqlSession 实例。
- Executor执行器:是MyBatis的核心,它将根据SqlSession传递的参数动态地生成需要执行的SQL语句,同时负责维护查询缓存。
- MappedStatement对象:MappedStatement是对解析的SQL语句的封装,一个MappedStatement代表了一个sql语句标签。
- 输入参数映射:输入参数类型可以是基本数据类型,也可以是Map、List、POJO等复杂数据类型。
- 封装结果集:可以将结果集封装成基本数据类型,也可以是Map、List、POJO等复杂数据类型。
12、Mybatis是否支持延迟加载?底层原理是什么?
Mybatis支持延迟加载:
- 延迟加载是加载策略的一种,分为延迟加载和直接加载两种策略,延迟加载主要体现在关联查询中。即需要用到数据时才加载,不需要用到就不加载,又叫懒加载。
- ⼀对多,多对多的情况下通常采⽤延迟加载。
- 在Mybatis配置文件中,可以配置lazyLoadingEnabled决定是否启用延迟加载,默认关闭。
底层原理:
- 使用CGLIB创建目标对象的代理对象。
- 当调用目标方法时,进入拦截器invoke方法,发现目标方法是null值,执行sql查询。
- 获取数据以后,调用set方法设置属性值,再继续查询目标方法,就有值了。
13、Mybatis的一级、二级缓存
MyBatis的缓存会将 select 语句的查询结果放到缓存(内存)当中,下一次还是这条select语句的话,直接从缓存中取。因此,缓存只针对于DQL语句,也就是说缓存机制只对应select语句。
-
一级缓存:
- 作用域:SqlSession。
- 默认开启,只要同一个SqlSession对象执行同一select语句就会走缓存。
- 缓存失效的情况:
- ①手动情况了一级缓存:sqlSession.clearCache();
- ②进行了增删改操作。
-
二级缓存:
- 作用域:SqlSessionFactory
- 使用二级缓存需要四个条件:
- ①在配置文件中开启缓存,默认开启。
- ②在需要使用二级缓存的SqlMapper.xml文件中添加配置:<cache />。
- ③使用二级缓存的实体类对象必须是可序列化的,即实现Serializable接口。
- ④SqlSession对象关闭后,一级缓存才会写入二级缓存。
- 缓存失效的情况:
- 进行了增删改操作。
- 相关配置:
- eviction:指定从缓存中移除某个对象的淘汰算法。默认采用LRU策略。
- flushInterval:二级缓存的刷新时间间隔。
更多缓存相关:mybatis的一二级缓存