Spring面试题

150 阅读9分钟

spring是什么?

轻量级的开源的J2EE框架。它是一个容器框架,用来装javabean(java对象),中间层框架(万能胶)可以起一个连接作用,比如说把Struts和hibernate粘合在一起运用,可以让我们的企业开发更快、更简洁

Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架

--从大小与开销两方面而言Spring都是轻量级的。--通过控制反转(IoC)的技术达到松耦合的目的

--提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务进行内聚性的

开发

--包含并管理应用对象(Bean)的配置和生命周期,这个意义上是一个容器。-

-将简单的组件配置、组合成为复杂的应用,这个意义上是一个框架

BeanFactory和ApplicationContext有什么区别?

ApplicationContext是BeanFactory的子接口

ApplicationContext提供了更完整的功能:

①继承MessageSource,因此支持国际化。

②统一的资源文件访问方式。

③提供在监听器中注册bean的事件。

④同时加载多个配置文件。

⑤载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层

  • BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。这样,我们就不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。
  • ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。ApplicationContext启动后预载入所有的单实例Bean,通过预载入单实例bean ,确保当你需要的时候,你就不用等待,因为它们已经创建好了。
  • 相对于基本的BeanFactory,ApplicationContext 唯一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。
  • BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader。
  • BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。

描述一下Spring Bean的生命周期?

1、解析类得到BeanDefinition

2、如果有多个构造方法,则要推断构造方法

3、确定好构造方法后,进行实例化得到一个对象

4、对对象中的加了@Autowired注解的属性进行属性填充

5、回调Aware方法,比如BeanNameAware,BeanFactoryAware

6、调用BeanPostProcessor的初始化前的方法

7、调用初始化方法

8、调用BeanPostProcessor的初始化后的方法,在这里会进行AOP

9、如果当前创建的bean是单例的则会把bean放入单例池

10、使用bean

11、Spring容器关闭时调用DisposableBean中destory()方法

解释下Spring支持的几种bean的作用域

  • singleton:默认,每个容器中只有一个bean的实例,单例的模式由BeanFactory自身来维护。该对象的生命周期是与Spring IOC容器一致的(但在第一次被注入时才会创建)。
  • prototype:为每一个bean请求提供一个实例。在每次注入时都会创建一个新的对象request:bean被定义为在每个HTTP请求中创建一个单例对象,也就是说在单个请求中都会复用这一个单例对象。session:与
  • request范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效。
  • application:bean被定义为在ServletContext的生命周期中复用一个单例对象。
  • websocket:bean被定义为在websocket的生命周期中复用一个单例对象。图灵学院
  • global-session:全局作用域,global-session和Portlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储在global-session中。全局作用域与Servlet中的session作用域效果相同

介绍⼀下Spring,读过源码介绍⼀下⼤致流程

  1. Spring是⼀个快速开发框架,Spring帮助程序员来管理对象

  2. Spring的源码实现的是⾮常优秀的,设计模式的应⽤、并发安全的实现、⾯向接⼝的设计等

  3. 在创建Spring容器,也就是启动Spring时:

a. ⾸先会进⾏扫描,扫描得到所有的BeanDefinition对象,并存在⼀个Map中

b. 然后筛选出⾮懒加载的单例BeanDefinition进⾏创建Bean,对于多例Bean不需要在启动过程中去进⾏创建,对于多例Bean会在每次获取Bean时利⽤BeanDefinition去创建

c. 利⽤BeanDefinition创建Bean就是Bean的创建⽣命周期,这期间包括了合并BeanDefinition、推断构造⽅法、实例化、属性填充、初始化前、初始化、初始化后等步骤,其中AOP就是发⽣在初始化后这⼀步骤中

  1. 单例Bean创建完了之后,Spring会发布⼀个容器启动事件

  2. Spring启动结束

  3. 在源码中会更复杂,⽐如源码中会提供⼀些模板⽅法,让⼦类来实现,⽐如源码中还涉及到⼀些BeanFactoryPostProcessor和BeanPostProcessor的注册,Spring的扫描就是通过BenaFactoryPostProcessor来实现的,依赖注⼊就是通过BeanPostProcessor来实现的

  4. 在Spring启动过程中还会去处理@Import等注解

Spring容器的启动流程是怎么样的?

使用AnnotationConfigApplicationContext 来跟踪一下启动流程:

this(); 初始化reader和scanner

scan(basePackages); 使用scanner组件扫描basePackage下的所有对象,将配置类的BeanDefinition注册到容器中。

refresh(); 刷新容器。

prepareRefresh 刷新前的预处理

obtainFreshBeanFactory: 获取在容器初始化时创建的BeanFactory

prepareBeanFactory: BeanFactory的预处理工作,会向容器中添加一些组件。

postProcessBeanFactory: 子类重写该方法,可以实现在BeanFactory创建并预处理完成后做进一步的设置。

invokeBeanFactoryPostProcessors: 在BeanFactory初始化之后执行BeanFactory的后处理器。

registerBeanPostProcessors: 向容器中注册Bean的后处理器,他的主要作用就是干预Spring初始化Bean的流程,完成代理、自动注入、循环依赖等这些功能。

initMessageSource: 初始化messagesource组件,主要用于国际化。

initApplicationEventMulticaster: 初始化事件分发器

onRefresh: 留给子容器,子类重写的方法,在容器刷新的时候可以自定义一些逻辑。

registerListeners: 注册监听器。

finishBeanFactoryInitialization: 完成BeanFactory的初始化,主要作用是初始化所有剩下的单例Bean。

finishRefresh: 完成整个容器的初始化,发布BeanFactory容器刷新完成的事件。

Spring如何处理循环依赖问题?

循环依赖: 多个对象之间存在循环的引用关系,在初始化过程当中,就会出现"先有蛋还是先有鸡"的问题。

一种是使用@Lazy注解: 解决构造方法造成的循环依赖问题

另一种是使用三级缓存

一级缓存:缓存最终的单例池对象: private final Map singletonObjects = new ConcurrentHashMap<>(256);

二级缓存:缓存初始化的对象:private final Map earlySingletonObjects = new ConcurrentHashMap<>(16);

三级缓存:缓存对象的ObjectFactory: private final Map> singletonFactories = new HashMap<>(16);

对于对象之间的普通引用,二级缓存会保存new出来的不完整对象,这样当单例池中找到不依赖的属性时,就可以先从二级缓存中获取到不完整对象,完成对象创建,在后续的依赖注入过程中,将单例池中对象的引用关系调整完成。

三级缓存:如果引用的对象配置了AOP,那在单例池中最终就会需要注入动态代理对象,而不是原对象。而生成动态代理是要在对象初始化完成之后才开始的。于是Spring增加三级缓存,保存所有对象的动态代理配置信息。在发现有循环依赖时,将这个对象的动态代理信息获取出来,提前进行AOP,生成动态代理。

Spring如何处理事务?

Spring当中支持编程式事务管理和声明式事务管理两种方式:

1、编程式事务可以使用TransactionTemplate。

2、声明式事务: 是Spring在AOP基础上提供的事务实现机制。他的最大优点就是不需要在业务代码中添加事务管理的代码,只需要在配置文件中做相关的事务规则声明就可以了。但是声明式事务只能针对方法级别,无法控制代码级别的事务管理。

Spring中对事务定义了不同的传播级别: Propagation

1、 PROPAGATION_REQUIRED:默认传播行为。 如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入到事务中。

2、PROPAGATION_SUPPORTS: 如果当前存在事务,就加入到该事务。如果当前不存在事务,就以非事务方式运行。

3、PROPAGATION_MANDATORY: 如果当前存在事务,就加入该事务。如果当前不存在事务,就抛出异常。

4、PROPAGATION_REQUIRES_NEW: 无论当前存不存在事务,都创建新事务进行执行。

5、PROPAGATION_NOT_SUPPORTED: 以非事务方式运行。如果当前存在事务,就将当前事务挂起。

6、PROPAGATION_NEVER : 以非事务方式运行。如果当前存在事务,就抛出异常。

7、PROPAGATION_NESTED: 如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则按REQUEIRED属性执行。

Spring中事务的隔离级别:

1、ISOLATION_DEFAULT: 使用数据库默认的事务隔离级别。

2、ISOLATION_READ_UNCOMMITTED: 读未提交。允许事务在执行过程中,读取其他事务未提交的数据。

3、ISOLATION_READ_COMMITTED: 读已提交。允许事务在执行过程中,读取其他事务已经提交的数据。

4、ISOLATION_REPEATABLE_READ: 可重复读。 在同一个事务内,任意时刻的查询结果是一致的。

5、ISOLATION_SERIALIZABLE: 所有事务依次执行。