文章目录
前言
你们项目中使用过Spring吗,你知道Spring 的IOC和AOP吗,你知道bean的工作过程吗,你知道Spring 是怎么解决循环依赖吗,Spring 中的事务底层实现原理是怎样的。本文重点对面试的问题进行介绍,祝愿每位程序员都能顺利上岸!!!
一、你知道Spring的Ioc:
Spring IOC作为Spring 的两大特性之一,它将Bean 的创建和销毁过程进行了管理,使得程序员可以只关注bean 的使用。
1.1 那你知道bean 的生命周期吗
Spring容器在进行实例化时,会将xml配置的的信息或者通过注解定义的bean 类,封装成一个BeanDefinition对象,Spring根据BeanDefinition来创建Bean对象,里面有很多的属性用来描述Bean。
一个bean的 生命周期包括bean 的创建,依赖注入,初始化,aop代理,使用,销毁几个过程;
并且在bean 的创建过程中,还提供了多个扩展 方法。
1.2 你知道Spring 中的bean 都有哪些类型吗:
Spring全家桶源码解析–2.6 Spring scope 限制bean的作用范围
- 单例bean :系统中使用最多的bean,spring 中默认生成的bean 都是单例bean;
- 原型bean: 多例bean,每次使用该bean 都会生成一个新的bean;
- 请求requestbean:web 场景下,不同的请求,创建的对象不同,在同一次请求内,bean 对象是相同的;
- 会话sessionbean :web 场景下,浏览器同一个会话,使用到的对象是相同的;
1.3 你知道 beanFactory,factoryBean的区别吗:
- beanFactory这个其实是所有Spring Bean的容器根接口,给Spring 的容器定义一套规范,给IOC容器提供了一套完整的规范,比如我们常用到的getBean方法等;
- factoryBean该类是SpringIOC容器是创建Bean的一种形式,这种方式创建Bean会有加成方式,融合了简单的工厂设计模式于装饰器模式,通过T getObject():返回实例;
1.4 你知道 BeanFactory和ApplicationContext的区别吗:
- BeanFactory 是最顶层的接口,最主要功能就是各种获取Bean的方法。
- ApplicationContext同样是有BeanFactory派生出来的,提供了很多面向实际应用的功能,主要接口 ApplicationContext 继承与 ListableBeanFactory、HierarchicalBeanFactory、ApplicationEventPublisher:提供容器发布上下文事件的功能,如容器启动事件,关闭事件等实现了ApplicationListener就可以接收处理到这些事件。
1.5 Spring 中的bean 是线程安全的吗
Spring 中定义的bean 默认都是单例的,所以我们在代码中注入的bean 对象实际上是同一个,但这并不意味着单例bean 是线程安全的,单例bean 的安全性,取决于改bean的实例是否状态安全的,比如我们在一个bean 中定义了一个可以被修改的成员变量,此时就需要考虑加锁,或者将bean的作用由“singleton"变更为"prototype”。
1.6 你都知道哪些方式可以破坏单例bean,应该怎么避免呢
-
可以通过反射方式调用构造方法再次生成一个bean 的实例对象;解决办法是定义一个全局变量,如果bean已经存在,再次调用构造方法,可以报错或者返回已经创建好的bean
-
调用bean 对象的 clone方法克隆出来一个新的bean 对象 :解决办法是: clone 通过在内存中将对象拷贝出来一个副本,破坏单例bean,重写clone 方法,返回已经创建好的bean;
-
通过反序列化来创建一个bean对象:解决办法是:在反序列化时的回调方法 readResolve()中返回已存在的单例对象。
1.7 你知道spring 是怎么解决循环依赖问题吗
spring 中的循环依赖通过三级缓存进行了处理。详细原理可以参考: Spring大白话–三级缓存解决循环依赖问题
1.7.1 什么是循环依赖
A和B 类彼此引用:
1.7.2 什么是三级缓存
Spring 中的三级缓存,通过提前暴露bean 来解决循环依赖的问题。
1.7.2.1 一级缓存
一级缓存用来存放已经初始化完成的bean
1.7.2.2 一级缓存+二级缓存
二级缓存存放半成品的对象,打破循环依赖
1.7.2.2 一级缓存+二级缓存+三级缓存处理代理对象
1.7.3 你们项目中有没有出现spring 三级缓存解决不了的循环依赖,你们是怎么处理的
- 项目中使用了构造方法进行bean 的注入,但是多个类实例化的入参,出现了循环的引用;因为无法完成对象实例的创建,所以走不到后续的三级缓存,启动会报错。
- 项目中使用了@Async 异步线程的注解,当出现循环引用时,回先生成该类的一个普通对象,进行类的属性注入;然而到该类进行初始化后为其创建了代理对象,暂存另一个类注入的对象,与此时生成的代理对象不是同一个对象,启动报错;
- 当出现以上情况时,我们通过@Lazy延迟加载,可以通过将 bean 的依赖关系运行时进行注入,而不是在初始化阶段。这样,当遇到循环依赖时,Spring 可以先创建需要的 bean 实例,并将其设置为代理对象,而不需要立即解决依赖关系。
二、你们项目中有用到Spring的Aop吗,你们都使用aop做过哪些处理
AOP称为面向切面编程,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。
2.1 你知道Aop 切面的实现吗
2.1.1 Spring 切面的使用:
可以参考:还在思索Spring-Aop 吗?,那么来一起畅游Spring吧–Spring系列开篇之 Spirng-Aop篇
2.2 你们项目中都用过aop 做过什么
2.2.1 使用aop 进行日志记录
我们在项目中通过自定义了@Log的注解,然后在需要记录日志的方法可以太耐改注解,然后通过定义@Aspect 切面类,定义切点,以及前置方法和后置方法,可以实现记录方法的方法,访问的参数,返回的结果以及耗时,然后定义了线程池,将数据报错的Mysql 或者Es 中。
2.2.2 使用aop 进行数据的事务处理
2.2.2.1 你知道@Transaction 的原理吗:
Spring支持编程式事务管理和声明式事务管理两种方式。
编程式事务控制:需使用TransactionTemplate来进行实现,对业务代码有侵入性,项目中很少;
使用声明式事务管理:声明式事务管理建立在AOP之上的。其本质是通过AOP功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
2.2.2.2 你们有没有遇到过事务失效的情况
通常在项目中我们使用声明式事务,来对数据进行事务处理;事务失效我在项目中遇到主要有以下三种:
- 方法非public
- 使用try catch 吞掉了异常,而且没有继续向外抛出异常
- 抛出了检查异常
1)捕获了异常但是没有继续向外抛
2) 抛出检查异常:
3)非public 方法:
2.2.2.3 你知道事务的传播吗
多个事务方法相互调用时,事务如何在这些方法间传播,方法A是一个事务的方法,方法A执行过程中调用了方法B,那么方法B有无事务以及方法B对事务的要求不同都会对方法A的事务具体执行造成影响,同时方法A的事务对方法B的事务执行也有影响,这种影响具体是什么就由两个方法所定义的事务传播类型所决定。
- REOUIRED(Spring默认的事务传播类型):如果当前没有事务,则自己新建一个事务,如果当前存在事务,则加入这个事务
- SUPPORTS:当前存在事务,则加入当前事务,如果当前没有事务,就以非事务方法执行
- MANDATORY:当前存在事务,则加入当前事务,如果当前事务不存在,则抛出异常。
- REQUIRES NEW:创建一个新事务,如果存在当前事务,则挂起该事务。
- NOTSUPPORTED:以非事务方式执行,如果当前存在事务,则挂起当前事务
- NEVER:不使用事务,如果当前事务存在,则抛出异常
- NESTED:如果当前事务存在,则在嵌套事务中执行,否则REQUIRED的操作一样(开启一个事务)
总结
本文对Spring 框架中,常见的如:Spring bean 的生命周期,bean 的循环依赖,Aop 以及事务的底层原理面试问题进行总结。