《完爆面试官》系列之Spring源码篇(上)

前言

​ HR小姐姐带领着我来到一间洽谈室中,眼前只见一张长长的会议桌,环顾四周,我当即找了个面向窗户的位子,这样有利于面试官能够清楚地看见索大的刷脸,而后缓缓地坐下来。HR和我简单的寒暄几句后,递给我一杯水,让我稍等一会,面试官立刻就来了。java

​ 索大暗暗思道:这个小姐姐人还挺好,有礼貌,声音也很温柔,面试官应该也不赖吧!web

面试开始

​ 没过多久,一位身着格子衫+牛仔裤搭配,体型略显瘦高,戴着一个黑框框眼镜的男士,推开洽谈室的门。面试

​ 索大礼貌性地起身问候:帅气的面试官,您好呀。spring

面试官仔细地翻阅索大的简历,来来回回翻了几回,内心暗道:数据库

这家伙,整整写了五页纸,不知道是真有点东西仍是凑字数的,待我来考考他吧

小伙子,看你简历写的挺多的,并且不少项目都是使用Spring框架,那你说下你对Spring的理解吧?

幸亏索大以前有对Spring框架进行一个全面的复习,爽道:编程

Spring 是个java企业级应用的开源开发框架。Spring主要用来开发Java应用,可是有些扩展是针对构建J2EE平台的web应用。Spring 框架目标是简化Java企业级应用开发,并经过POJO为基础的编程模型促进良好的编程习惯。而他主要是经过:控制反转(IOC)、依赖注入(DI)以及面向切面(AOP)这三种方式来达成的。缓存

spring之因此强大,是由于Spring有众多模块为咱们提供了开发企业应用的一切安全

Spring的七个核心模块
Spring的七个核心模块
  • Spring Core:核心类库提供spring框架的基本功能IOC服务。Spring以bean的方式组织和管理Java应用中的各个组件及其关系。Spring使用BeanFactory来产生和管理Bean,它是工厂模式的实现。框架

  • Spring Context:Spring上下文是一个配置文件,向Spring框架提供上下文信息。Spring上下文包括企业服务,如JNDI、EJB、电子邮件、国际化、校验和调度功能。提供框架式Bean访问方式,其余程序能够经过Context访问Spring的Bean资源。编辑器

  • Spring AOP:Spring在它的AOP模块中提供了对面向切面编程的丰富支持。例如方法拦截器(method-interceptors)和切点(pointcuts),能够有效的防止代码上功能的耦合,这个模块是在Spring应用中实现切面编程的基础。

  • Spring DAO:DAO模块主要目的是将持久层相关问题与通常的的业务规则和工做流隔离开来。Spring 中的DAO提供一致的方式访问数据库,无论采用何种持久化技术,Spring都提供一致的编程模型。Spring的DAO模块对JDBC进行了再封装,隐藏了Connection、Statement、ResultSet等JDBC API,使DAO模块直接继承JdbcDaoSupport类。

  • Spring ORM:对现有的ORM框架的支持;

  • Spring Web:提供了基本的面向Web的综合特性,例如多方文件上传;

  • Spring MVC:MVC框架是一个全功能的构建Web应用程序的MVC实现。经过策略接口,MVC框架变成为高度可配置的。Spring的MVC框架提供清晰的角色划分:控制器、验证器、命令对象、表单对象和模型对象、分发器、处理器映射和视图解析器。Spring支持多种视图技术。

你说下使用Spring框架的好处是什么?

索大回答这类问题,通常喜欢从他的特色来分析,好比:

(1)spring属于低侵入式设计,代码的污染极低;

(2)spring的DI机制将对象之间的依赖关系交由框架处理,减低组件的耦合性;

(3)Spring提供了AOP技术,支持将一些通用任务,如安全、事务、日志、权限等进行集中式管理,从而提供更好的复用。

(4)spring对于主流的应用框架提供了集成支持。

你说下什么是Spring IOC 容器?

​ IOC就是控制反转,是指建立对象的控制权的转移,之前建立对象的主动权和时机是由本身把控的,而如今这种权力转移到Spring容器中,并由容器根据配置文件去建立实例和管理各个实例之间的依赖关系,对象与对象之间松散耦合,也利于功能的复用。DI依赖注入,和控制反转是同一个概念的不一样角度的描述,即 应用程序在运行时依赖IoC容器来动态注入对象须要的外部资源。

​ 索大这样回答其实还不能打动面试官,还要进行深刻分析。在Spring容器中,

谁控制谁?:固然是IoC 容器控制了对象

控制了什么?:主要控制了外部资源获取(不仅是对象包括好比文件等)

什么方面反转了?:得到依赖对象的方式反转了

经过一张图来解释从IOC容器获取对象的过程:

咱们来总结下实现原理 Spring的IOC容器是经过反射机制+工厂模式实现的,在实例化一个类时,它经过反射调用类中set方法将事先保存在HashMap中的类属性注入到类中。

*回答到这里,若是不顺便把**依赖注入(DI)*说下,彷佛不能体现索大的逼格啊

依赖注入(Dependency Injection):就是说组件bean之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件bean之中。经过依赖注入机制,咱们只须要经过简单的配置,而无需任何代码就可指定目标须要的资源,完成自身的业务逻辑,而不须要关心具体的资源来自何处,由谁实现。

不过咱们须要注意两个细节:

  1. 依赖注入发生的时间 (1).用户第一次经过getBean方法向IoC容索要Bean时,IoC容器触发依赖注入。 (2).当用户在Bean定义资源中为元素配置了lazy-init属性,即让容器在解析注册Bean定义时进行预实例化,触发依赖注入。
  2. 依赖注入实如今如下两个方法中: (1).createBeanInstance:生成Bean所包含的java对象实例。 (2).populateBean :对Bean属性的依赖注入进行处理。

那你知道Spring的注入方式有哪几种?

​ 常见的注入方式有三种:setter 属性注入、构造方法注入和注解方式注入。

  • Setter方法注入:Setter方法注入是容器经过调用无参构造器或无参static工厂 方法实例化bean以后,调用该bean的setter方法,即实现了基于setter的依赖注入。

  • 构造器依赖注入:构造器依赖注入经过容器触发一个类的构造器来实现的,该类有一系列参数,每一个参数表明一个对其余类的依赖。

  • 注解方式注入:使用@Autowired注解来自动装配指定的bean。@Autowired可用于:构造函数、成员变量、Setter方法。

    若是查询结果恰好为一个,就将该bean装配给@Autowired指定的数据;

    若是查询的结果不止一个,那么@Autowired会根据名称来查找;

    若是上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。

注意:@Autowired和@Resource之间的区别

  • @Autowired默认是按照类型装配注入的,默认状况下它要求依赖对象必须存在(能够设置它required属性为false)。

  • @Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入。

你知道spring循环依赖是怎么解决的?

索大先假设场景以下,A->B->A

一、实例化A,并将未注入属性的A暴露出去,即提早曝光给容器Wrap 二、开始为A注入属性,发现须要B,调用getBean(B) 三、实例化B,并注入属性,发现须要A的时候,从单例缓存中查找,没找到时继而从Wrap中查找,从而完成属性的注入 四、递归完毕以后回到A的实例化过程,A将B注入成功,并注入A的其余属性值,自此即完成了循环依赖的注入

spring循环依赖流程图
spring循环依赖流程图

首先咱们要清楚Spring单例对象的初始化能够分为三步:

  1. 【createBeanInstance】, 实例化, 实际上就是调用对应的构造方法构造对象, 此时只是调用了构造方法,spring xml中指定的property并无进行populate
  2. 【populateBean】,填充属性, 这步对spring xml中指定的property进行populate
  3. 【initializeBean】,调用spring xml中指定的init方法, 或者AfterPropertiesSet方法

会发生循环依赖的步骤集中在第一步和第二步

而后咱们还要对Spring的三级缓存有所了解

​ 对于单例对象来讲,在Spring的整个容器的生命周期内, 有且只存在一个对象,很容易想到这个对象应该存在Cache中,Spring大量运用了Cache的手段,在循环依赖问题的解决过程当中甚至使用了“三级缓存”。

  1. singletonObjects:指单例对象的cache
  2. earlySingletonObjects:指提早曝光的单例对象的cache
  3. singletonFactories:指单例对象工厂的cache

Spring解决循环依赖的步骤:

首先Spring会尝试从缓存中获取,这个缓存就是指singletonObjects;

  1. Spring首先从singletonObjects(一级缓存)中尝试获取,
  2. 若是获取不到而且对象在建立中,则尝试从earlySingletonObjects(二级缓存)中获取;
  3. 若是仍是获取不到而且容许从singletonFactories经过getObject获取,则经过singletonFactory.getObject()(三级缓存)获取
  4. 若是获取到了则移除对应的singletonFactory, 将singletonObject放入到earlySingletonObjects, 其实就是将三级缓存提高到二级缓存中!

解决循环依赖的关键, 单例对象此时已经被建立出来的。这个对象已经被生产出来了,虽然还不完美(尚未进行初始化的第二步和第三步),可是已经能被人认出来了(根据对象引用能定位到堆中的对象),因此Spring此时将这个对象提早曝光出来让你们认识,让你们使用。

最后索大来个小结:

​ Spring循环依赖的理论依据实际上是Java基于值传递,传递引用, 当咱们获取到对象的引用时,对象的field或者或属性是能够延后设置的。

​ Spring经过三级缓存加上“提早曝光”机制,配合Java的对象引用原理,比较完美地解决了某些状况下的循环依赖问题!

面试结束

面试官上下打量着我,绝不吝啬地夸了一句:

小伙子,不赖呀。

这个时候,必定不要激动,能够给个低调奢华的表情

总结

​ Spring 是一个主流的 Java Web 开发框架,该框架是一个轻量级的应用框架,具备很高的凝聚力和吸引力。Spring 框架因其强大的功能以及卓越的性能而受到众多开发人员的喜好。涉及到的知识远不止于此,咱们要脚踏实地地去实践,而后理解并掌握其中的原理。

参考资料:

  1. Spring官网 https://spring.io/
  2. 计文柯 《深刻理解Spring技术内幕》
  3. 郝佳 《Spring源码深度解析》

花絮

以上就是本篇文章的所有内容了,谢谢你们的阅览,各位的支持和鼓励是索大前进的动力,下一篇我

们不见不散。

码字不易啊,喜欢的话不妨点赞👍 关注💓分享给朋友👥,这对我真的很重要呀!

相关文章
相关标签/搜索