面试题来源:

Spring源码:https://javadoop.com/post/spring-ioc

IOC容器初始化过程?

  BeanFactory(常用ClassPathXmlApplicationContext)初始化,调用refresh——加载Spring配置,解析成bean对象并注册到BeanDefinition(注册中心)——调用BeanFactoryPostProcessor(前提是实现了这个接口)实例化和使用——调用BeanPostProcessor两个方法(PostProcessBeforeInitialization和PostProcessAfterInitialization)分别在bean初始化前后调用——实例化所有bean——FinishRefresh广播,通知所有监听器完成refresh

java bean 初始化 缓存 spring bean初始化过程面试题_初始化

 tips:结合上面第二篇文章的源码看

 

Bean生命周期?

  Bean的生命周期可以先分为几大部分:1)实例化初始化2)使用3)销毁,其中实例化初始化可分为1)实例化(instantiation)2)属性填充(populate)3初始化(initialize)。

在实例化和初始化时都有一个切面方法,分为在切面前后进行预(后)处理,分别=、为InstantiationAwareBeanPostProcessor和BeanPostProcessor的xxxbeforexxx和xxxafterxxx方法。剩余一些细节:1)实例化后,属性填充前,MergedBeanDefinitionPostProcessor修改合并bean定义,曝光objectFactory,解决循环引用(待消化)。2)初始化前,BeanFactoryAware,BeanNameAware,BeanClassLoaderAware(如果实现,invoke方法)。3)销毁阶段,DestructionAwareBeanPostProcessor有个beforeDestruction方法,然后destory-method。

java bean 初始化 缓存 spring bean初始化过程面试题_动态代理_02

 

 

 

BeanFactory 和 FactoryBean 的区别?

  BeanFactory是IOC容器的基类,常用的ClassPathXmlApplicationContext是它的一个具体实现。

  FactoryBean是是一种工厂bean,可以自实现getObject方法,自定义要创建的的Object实例。中间件源码大量使用FactoryBean。

  一般来说,都是通过 FactoryBean#getObject 来返回一个代理类,当我们触发调用时,会走到代理类中,从而可以在代理类中实现中间件的自定义逻辑,比如:RPC 最核心的几个功能,选址、建立连接、远程调用,还有一些自定义的监控、限流等等。(待消化)

BeanFactory 和 ApplicationContext 的区别?

  BeanFactory是IOC的基类,基础容器;ApplicationContext是BeanFactory的高级实现,高级容器。BeanFactory实现了基础功能(可以类比成座机),ApplicationContext有更多拓展功能(类比为智能手机)。

Spring 的 AOP 是怎么实现的?

  本质是动态代理,1)获得增强器例如@Aspect注解。2)创建实例时,通过判断是否包含在execution中,确定是否需要增强器,增强器作为参数传递,通过动态代理完成增强。3)当调用这个实例的时候,走增强器。

Spring 的 AOP 有哪几种创建代理的方式?

  JDK的动态代理和Cglib动态代理。

JDK 动态代理和 Cglib 代理的区别?

  1)JDK动态代理通过接口实现,Cglib通过继承实现。

  2)JDK动态代理只能通过接口实现,因为底层是继承了Proxy类,无法多继承;Cglib没有要求,但是Cglib通过继承实现,无法代理final修饰的类。

  3)JDK动态代理通过反射实现调用,Cglib通过FastClass机制调用。

  4)JDK1.7以前效率慢于Cglib,1.7以后反之。

JDK 动态代理为什么只能对实现了接口的类生成代理?

  因为底层是继承了Proxy类,无法多继承。

Spring 的事务传播行为有哪些?

  1)Required  Spring默认传播级别,如果上下文有事务,就加入到事务当中,如果没有事务,就新建事务。

  2)Requires_New  每次都会新建一个事务,如果上下文有事务,则会将事务挂起,当前事务完成后,上下文事务再恢复执行。

  3)Supports  如果上下文存在事务,则加入事务,如果没有事务,则按非事务执行。

  4)Not_supported  如果上下文存在事务,则挂起事务,执行完当前逻辑后,上下文事务再恢复执行。

  5)Mandatory  上下文必须存在事务,不然会抛出异常。

  6)Never  上下文不能存在事务,不然会抛出异常。

  7)Nested  嵌套事务,上下文存在事务则嵌套执行,不存在则新建事务。

Spring 的事务隔离级别?

  Spring的事务隔离级别是基于数据库的隔离级别,不存在自己的一套隔离级别。

  Default:数据库默认隔离级别。

  Read_Uncommited:读未提交,会读到其他事务还未提交的内容,存在脏读。

  Read_Commited:读已提交,会读到其他事务已提交的内容,存在不可重复读。

  Repeatable_Read:可重复度,每次读取一条记录的内容是一样的,但是存在幻读。

  Serialization:串行化,最高隔离级别,每一条数据读写都上锁,只有读读可以并行。其他操作如读写,写读,写写,都不能并发操作。解决脏读,不可重复读,幻读。

Spring 的事务隔离级别是如何做到和数据库不一致的?

  本质还是通过数据库命令来控制,在执行前修改数据库隔离级别,例如下面命令。

SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED

 

Spring 事务的实现原理?

  AOP(动态代理)+ThreadLocal+Try/Catch

  AOP用于逻辑增强,ThreadLocal用于线程资源隔离(不同线程不同dataSource、connection等),Try/Catch用于rollback和commit。

Spring 怎么解决循环依赖的问题?

  每个对象实例化后,将ObjectFactory曝光到缓存中,每次实例前到缓存里检查是否存在。

  A依赖B,B依赖A,那么创建对象的过程如下:

  1)A开始创建前,到缓存中检查是否存在ObjectFactory,不存在,则调用构造方法创建对象,然后把A的ObjectFactory曝光

  2)A创建后,进行属性填充,发现B,即将实例化B

  3)B开始创建前,先到缓存中检查是否存在ObjectFactory,不存在,则调用构造方法创建对象,然后把B的ObjectFactory曝光

  4)B创建后,进行属性填充,发现A,即将实例化A

  5)A开始创建前,先到缓存中检查是否存在ObjectFactory,存在,则获得ObjectFactory,由此获得A的bean

  6)B进行其他属性填充和初始化

  7)A进行其他属性填充和初始化

Spring 能解决构造函数循环依赖吗?

  不行,Spring通过分解创建过程,在其中增加到缓存中的可见性(类似volatile),来解决循环依赖问题,但是构造函数中是其中一个单元,没有分解。

Spring 三级缓存?

  Spring解决循环依赖用到的就是三级缓存。

  一级缓存SingletonObjects,对象创建完毕(实例化、注入,初始化)后会放到该缓存,key:beanName value:bean实例。

  二级缓存earlySingletonObjects,从ObjectFactory返回的bean会放到该缓存(实例化),ObjectFactory只会调用一次,然后从earlySingletonObjects里面获得,key:beanName value:早期bean实例。

  三级缓存SingletonFactories,对象实例化后会把ObjectFactory放到该缓存(曝光),key:beanName value:ObjectFactory。

Tips:为什么使用三级缓存而不是一级二级?  https://www.zhihu.com/question/445446018

@Resource 和 @Autowire 的区别?

  Autowired按类型装配,默认要求依赖对象必须存在。

  Resource可以按名称或类型装配,默认使用名称,名称装配失败则使用类型,也可以指定按名称或类型。

@Autowire 怎么使用名称来注入?

  搭配Qualifier使用