Spring
,Spring MVC
,Spring Boot
三者比较
答: 这三者专注的领域不同,解决的问题也不一样;总的来说,Spring
就像一个大家族,有众多衍生产品例如 Boot
,Security
,JPA
等等。但他们的基础都是 Spring
的 IOC
和 AOP
,IOC
提供了依赖注入的容器,而AOP
解决了面向切面的编程,然后在此两者的基础上实现了其他衍生产品的高级功能;Spring MVC
是基于 Servlet
的一个 MVC
框架,主要解决 WEB
开发的问题。与此同时, Spring
的配置非常复杂,各种xml
,properties
处理起来比较繁琐。于是为了简化开发者的使用,Spring
社区创造性地推出了Spring Boot
,它遵循约定优于配置,极大降低了Spring
使用门槛,但又不失Spring
原本灵活强大的功能,下面用一张图来描述三者的关系:
最后一句话总结:
Spring MVC
和Spring Boot
都属于Spring
,Spring MVC
是基于Spring
的一个MVC
框架,而Spring Boot
是基于Spring
的一套快速开发整合包
1、什么是Spring IOC
容器?
答:控制反转即IOC (Inversion of Control)
,它把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。所谓的“控制反转”概念就是对组件对象控制权的转移,从程序代码本身转移到了外部容器。
Spring IOC
负责创建对象,管理对象(通过依赖注入(DI)
,配置对象,装配对象,并且管理这些对象的整个生命周期)。
2、控制反转(IOC
)有什么作用?
- 管理对象的创建和依赖关系的维护: 对象的创建并不是一件简单的事,在对象关系比较复杂时,如果依赖关系需要程序猿来维护的话,那是相当头疼的。
- 解耦: 由容器去维护具体的对象。
- 托管了类的产生过程: 比如我们需要在类的产生过程中做一些处理,最直接的例子就是代理,如果有容器程序可以把这部分处理交给容器,应用程序则无需去关心类是如何完成代理的。
3、IOC
的优点是什么?
IOC
或 依赖注入把应用的代码量降到最低。- 它使应用容易测试,单元测试不再需要单例和
JNDI
查找机制。 - 最小的代价和最小的侵入性使松散耦合得以实现。
IOC
容器支持加载服务时的饿汉式初始化和懒加载。
4、Spring IOC
的实现原理?
答: 工厂模式加反射机制。
interface Fruit {public abstract void eat();
}class Apple implements Fruit {public void eat(){System.out.println("Apple");}
}class Orange implements Fruit {public void eat(){System.out.println("Orange");}
}class Factory {public static Fruit getInstance(String ClassName) {Fruit f = null;try {f = (Fruit)Class.forName(ClassName).newInstance();} catch (Exception e) {e.printStackTrace();}return f;}
}class Client {public static void main(String[] a) {Fruit f=Factory.getInstance("spring.Apple");if(f!=null){f.eat();}}
}
5、BeanFactory
和ApplicationContext
有什么区别?
答: BeanFactory
和ApplicationContext
是Spring的两大核心接口,都可以当做Spring的容器。其中ApplicationContext
是BeanFactory
的子接口。
5.1、依赖关系
BeanFactory
: 是Spring
里面最底层的接口,包含了各种Bean
的定义,读取Bean
配置文档,管理Bean
的加载、实例化,维护Bean
之间的依赖关系和控制Bean
的生命周期,。ApplicationContext
: 该接口作为BeanFactory
的派生,除了提供BeanFactory
所具有的功能外,还提供了更完整的框架功能:- 继承
MessageSource
,因此支持国际化。 - 统一的资源文件访问方式。
- 提供在监听器中注册
Bean
的事件。 - 同时加载多个配置文件。
- 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的
web
层。
- 继承
5.2、加载方式
答:ApplicationContext
相对于基本的BeanFactory
,ApplicationContext
唯一的不足是占用内存空间。当应用程序配置Bean
较多时,程序启动较慢。
BeanFactroy
:BeanFactroy
采用的是延迟加载形式来注入Bean
的,即只有在使用到某个Bean
时(调用getBean()
),才对该Bean
进行加载实例化。这样,我们就不能发现一些存在的Spring的配置题。如果Bean
的某一个属性没有注入,BeanFacotry
加载后,直至第一次使用调用getBean()
方法才会抛出异常。ApplicationContext
:ApplicationContext
是在容器启动时,一次性创建了所有的Bean
。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。ApplicationContext
启动后预载入所有的单实例Bean
,通过预载入单实例Bean
,确保当你需要的时候,你就不用等待,因为它们已经创建好了。
5.3、创建方式
答: BeanFactory
通常以编程的方式被创建,ApplicationContext
还能以声明的方式创建,如使用ContextLoader
。
5.4、注册方式
答: BeanFactory
和ApplicationContext
都支持 BeanPostProcessor
、BeanFactoryPostProcessor
的使用,但两者之间的区别是:BeanFactory
需要手动注册,而 ApplicationContext
则是自动注册。
6、Spring
如何设计容器的,BeanFactory
和ApplicationContext
的关系详解?Spring控制反转(IOC)
Spring
作者 Rod Johnson
设计了两个接口用以表示容器。
BeanFactory
ApplicationContext
BeanFactory
简单粗暴,可以理解为就是个HashMap
,Key
是BeanName
,Value
是Bean
实例。通常只提供注册(put)
,获取(get)
这两个功能。我们可以称之为 “低级容器”ApplicationContext
可以称之为 “高级容器”。因为他比BeanFactory
多了更多的功能。他继承了多个接口。因此具备了更多的功能。例如资源的获取,支持多种消息(例如JSP tag
的支持),对BeanFactory
多了工具级别的支持等待。所以你看他的名字,已经不是BeanFactory
之类的工厂了,而是 “应用上下文”, 代表着整个大容器的所有功能。该接口定义了一个refresh ()
方法,此方法是所有阅读 Spring 源码的人的最熟悉的方法,用于刷新整个容器,即重新加载/刷新所有的Bean
。- 为了更直观的展示 “低级容器” 和 “高级容器” 的关系,这里通过常用的
ClassPathXmlApplicationContext
类来展示整个容器的层级 UML 关系。
7、ApplicationContext
通常的实现是什么?
FileSystemXmlApplicationContext
: 此容器从一个XML
文件中加载Beans
的定义,XML Bean
配置文件的全路径名必须提供给它的构造函数。ClassPathXmlApplicationContext
: 此容器也从一个XML文件中加载Beans
的定义,这里,你需要 正确设置classpath
因为这个容器将在classpath
里找Bean
配置。WebXmlApplicationContext
: 此容器加载一个XML
文件,此文件定义了一个WEB
应用的所有Bean
。
8、什么是Spring
的依赖注入?
答: 控制反转IOC
是一个很大的概念,可以用不同的方式来实现。其主要实现方式有两种:依赖注入和依赖查找。面向接口编程中,让依赖注入只需要找到符合规范的接口注入即可实现调用者和被调用者解耦。对象的调用关系由Spring
管理。
- 依赖注入: 是指程序运行过程中,如果需要创建一个对象,无须再代码中
new
创建,而是依赖外部的注入。Spring
的依赖注入对调用者和被调用者几乎没有任何要求,完全支持对pojo
之间依赖关系的管理; new
对象: 类的头部进行实例化对象和依赖注入一个效果,这个时候该对象不管是否使用都贯穿该类的始终。该类对象不被回收,这个实例化对象也不会被回收。如果要使用多例对象则最好使用new
创建对象而不是依赖注入,即使依赖注入有多例模式也不推荐。
9、依赖注入有什么优势?
答: 依赖注入之所以更流行是因为它是一种更可取的方式:让容器全权负责依赖查询,受管组件只需要暴露JavaBean
的Setter
方法或者带参数的构造器或者接口,使容器可以在初始化时组装对象的依赖关系。其与依赖查找方式相比,主要优势为:
- 查找定位操作与应用代码完全无关。
- 不依赖于容器的
API
,可以很容易地在任何容器以外使用应用对象。 - 不需要特殊的接口,绝大多数对象可以做到完全不必依赖容器。
10、有哪些不同类型的依赖注入实现方式?
答: 依赖注入是时下最流行的IOC
实现方式,依赖注入分为接口注入(Interface Injection)
,Setter
方法注入(Setter Injection)
和构造器注入(Constructor Injection)
三种方式。其中接口注入由于在灵活性和易用性比较差,现在从Spring4
开始已被废弃。
- 构造器依赖注入: 构造器依赖注入通过容器触发一个类的构造器来实现的,该类有一系列参数,每个参数代表一个对其他类的依赖。
Setter
方法注入:Setter
方法注入是容器通过调用无参构造器或无参static
工厂方法实例化Bean
之后,调用该Bean
的setter
方法,即实现了基于setter
的依赖注入。
构造器依赖注入和 Setter方法注入的区别
构造函数注入 | setter 注入 |
没有部分注入 | 有部分注入 |
不会覆盖setter 属性 | 会覆盖setter 属性 |
任意修改都会创建一个新实例 | 任意修改不会创建一个新实例 |
适用于设置很多属性 | 适用于设置少量属性 |
两种依赖方式都可以使用,构造器注入和Setter
方法注入。最好的解决方案是用构造器参数实现强制依赖, setter
方法实现可选依赖。
11、Spring
的循环依赖怎么理解?
答: 循环依赖是指在创建 Bean
的时候,两个为创建未完成的Bean
互相引用,形成循环。循环依赖分为三种:
- 构造器的循环依赖: 这种依赖Spring是处理不了的,直接抛出
BeanCurrentlylnCreationException
异常。 - 单例模式下的
setter
循环依赖: 通过 “三级缓存” 处理循环依赖。singletonObjects
:一级缓存,用于保存实例化、注入、初始化完成的Bean
实例;earlySingletonObjects
: 二级缓存,用于保存实例化完成的Bean
实例;singletonFactories
:三级缓存,用于保存Bean
创建工厂,以便于后面扩展有机会创建代理对象。
- 非单例循环依赖: 无法处理。