1、什么是 Spring?

Spring 是一个开源的 Java EE 开发框架。Spring 框架的核心功能可以应用在任何 Java 应用程序中,但对 Java EE 平台上的 Web 应用程序有更好的扩展性。Spring 框架的目标是使得 Java EE 应用程序的开发更加简捷,通过使用 POJO 为基础的编程模型促进良好的编程风格。

2、Spring 有哪些优点?

轻量级:Spring 在大小和透明性方面绝对属于轻量级的,基础版本的 Spring 框架大约只有 2MB。

控制反转(IOC):Spring 使用控制反转技术实现了松耦合。依赖被注入到对象,而不是创建或寻找依赖对象。

面向切面编程(AOP): Spring 支持面向切面编程,同时把应用的业务逻辑与系统的服务分离开来。

容器:Spring 包含并管理应用程序对象的配置及生命周期。

MVC 框架:Spring 的 web 框架是一个设计优良的 web MVC 框架,很好的取代了一些 web 框架。

事务管理:Spring 对下至本地业务上至全局业务(JAT)提供了统一的事务管理接口。

异常处理:Spring 提供一个方便的 API 将特定技术的异常(由 JDBC, Hibernate, 或 JDO 抛出)转化为一致的、Unchecked 异常。

3、Spring 事务实现方式

  • 编程式事务管理:这意味着你可以通过编程的方式管理事务,这种方式带来了很大的灵活性,但很难维护。
  • 声明式事务管理:这种方式意味着你可以将事务管理和业务代码分离。你只需要通过注解或者 XML 配置管理事务。

4、Spring 框架的事务管理有哪些优点

  • 它为不同的事务 API(如 JTA, JDBC, Hibernate, JPA, 和 JDO)提供了统一的编程模型。
  • 它为编程式事务管理提供了一个简单的 API 而非一系列复杂的事务 API(如 JTA).
  • 它支持声明式事务管理。
  • 它可以和 Spring 的多种数据访问技术很好的融合。

5、Spring 事务定义的传播规则

使用方法: @Transactional(propagation = Propagation.REQUIRES_NEW)

默认为 REQUIRED,比较常用的就是 REQUIRED、REQUIRES_NEW。

  • REQUIRED:如果当前没有事务,则自己新建一个事务,如果当前存在事务,则加入这个事务。这是最常见的选择。
  • SUPPORTS:当前存在事务,则加入当前事务,如果当前没有事务,就以非事务方法执行
  • MANDATORY:当前存在事务,则加入当前事务,如果当前事务不存在,则抛出异常
  • REQUIRES_NEW:创建一个新事务,如果存在当前事务,则挂起该事务
  • NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,则挂起当前事务
  • NEVER:不使用事务,如果当前事务存在,则抛出异常
  • NESTED:如果当前事务存在,则在嵌套事务中执行,否则 REQUIRED 的操作一样(开启一个事务)

6、Spring 事务底层原理

  • 划分处理单元——IOC

由于 Spring 解决的问题是对单个数据库进行局部事务处理的,具体的实现首先用 Spring 中的 IOC 划分了事务处理单元。并且将对事务的各种配置放到了 ioc 容器中(设置事务管理器,设置事务的传播特性及隔离机制)。

  • AOP 拦截需要进行事务处理的类

Spring 事务处理模块是通过 AOP 功能来实现声明式事务处理的,具体操作(比如事务实行的配置和读取,事务对象的抽象),用 TransactionProxyFactoryBean 接口来使用 AOP 功能,生成 proxy 代理对象,通过 TransactionInterceptor 完成对代理方法的拦截,将事务处理的功能编织到拦截的方法中。读取 ioc 容器事务配置属性,转化为 Spring 事务处理需要的内部数据结构(TransactionAttributeSourceAdvisor),转化为 TransactionAttribute 表示的数据对象。

  • 对事务处理实现(事务的生成、提交、回滚、挂起)

Spring 委托给具体的事务处理器实现。实现了一个抽象和适配。适配的具体事务处理器:DataSource 数据源支持、hibernate 数据源事务处理支持、JDO 数据源事务处理支持,JPA、JTA 数据源事务处理支持。这些支持都是通过设计 PlatformTransactionManager、AbstractPlatforTransaction 一系列事务处理的支持。 为常用数据源支持提供了一系列的 TransactionManager。

  • 结合

PlatformTransactionManager 实现了 TransactionInterception 接口,让其与 TransactionProxyFactoryBean 结合起来,形成一个 Spring 声明式事务处理的设计体系。

7、Spring MVC 运行流程

第一步:发起请求到前端控制器(DispatcherServlet)

第二步:前端控制器请求 HandlerMapping 查找 Handler( 可以根据 xml 配置、注解进行查找)

第三步:处理器映射器 HandlerMapping 向前端控制器返回 Handler

第四步:前端控制器调用处理器适配器去执行 Handler

第五步:处理器适配器去执行 Handler

第六步:Handler 执行完成给适配器返回 ModelAndView

第七步:处理器适配器向前端控制器返回 ModelAndView(ModelAndView 是 springmvc 框架的一个底层对象,包括 Model 和 view)

第八步:前端控制器请求视图解析器去进行视图解析(根据逻辑视图名解析成真正的视图(jsp))

第九步:视图解析器向前端控制器返回 View

第十步:前端控制器进行视图渲染( 视图渲染将模型数据(在 ModelAndView 对象中)填充到 request 域)

第十一步:前端控制器向用户响应结果

8、BeanFactory 和 ApplicationContext 有什么区别?

BeanFactory 是 Spring 中非常核心的组件,表示 Bean 工厂,可以生成 Bean,维护 Bean,而 ApplicationContext 继承了 BeanFactory,所以 ApplicationContext 拥有 BeanFactory 所有的特点,也是一个 Bean 工厂,但是 ApplicationContext 除开继承了 BeanFactory 之外,还继承了诸如 EnvironmentCapable、MessageSource、ApplicationEventPublisher 等接口,从而 ApplicationContext 还有获取系统环境变量、国际化、事件发布等功能,这是 BeanFactory 所不具备的。

相同点:两者都是通过 xml 配置文件加载 bean,ApplicationContext 和 BeanFacotry 相比,提供了更多的扩展功能。

不同点:BeanFactory 是延迟加载,如果 Bean 的某一个属性没有注入,BeanFacotry 加载后,直至第一次使用调用 getBean 方法才会抛出异常;而 ApplicationContext 则在初始化自身时检验,这样有利于检查所依赖属性是否注入;所以通常情况下我们选择使用 ApplicationContext。

9、什么是 Spring Beans?

Spring Beans 是构成 Spring 应用核心的 Java 对象。这些对象由 Spring IOC 容器实例化、组装、管理。这些对象通过容器中配置的元数据创建,例如,使用 XML 文件中定义的创建。

在 Spring 中创建的 beans 都是单例的 beans。在 bean 标签中有一个属性为”singleton”,如果设为 true,该 bean 是单例的,如果设为 false,该 bean 是原型 bean。Singleton 属性默认设置为 true。因此,Spring 框架中所有的 bean 都默认为单例 bean。

10、说一下 Spring 中支持的 bean 作用域

Spring 框架支持如下五种不同的作用域:

  • singleton:在 Spring IOC 容器中仅存在一个 Bean 实例,Bean 以单实例的方式存在。
  • prototype:一个 bean 可以定义多个实例。
  • request:每次 HTTP 请求都会创建一个新的 Bean。该作用域仅适用于 WebApplicationContext 环境。
  • session:一个 HTTP Session 定义一个 Bean。该作用域仅适用于 WebApplicationContext 环境。
  • globalSession:同一个全局 HTTP Session 定义一个 Bean。该作用域同样仅适用于 WebApplicationContext 环境。

bean 默认的 scope 属性是”singleton”。

11、Spring 的单例实现原理

Spring 框架对单例的支持是采用单例注册表的方式进行实现的,而这个注册表的缓存是 HashMap 对象,如果配置文件中的配置信息不要求使用单例,Spring 会采用新建实例的方式返回对象实例。

12、解释 Spring 框架中 bean 的生命周期

ApplicationContext 容器中,Bean 的生命周期流程如图所示,流程大致如下:

1.首先容器启动后,会对 scope 为 singleton 且非懒加载的 bean 进行实例化,

2.按照 Bean 定义信息配置信息,注入所有的属性,

3.如果 Bean 实现了 BeanNameAware 接口,会回调该接口的 setBeanName()方法,传入该 Bean 的 id,此时该 Bean 就获得了自己在配置文件中的 id,

4.如果 Bean 实现了 BeanFactoryAware 接口,会回调该接口的 setBeanFactory()方法,传入该 Bean 的 BeanFactory,这样该 Bean 就获得了自己所在的 BeanFactory,

5.如果 Bean 实现了 ApplicationContextAware 接口,会回调该接口的 setApplicationContext()方法,传入该 Bean 的 ApplicationContext,这样该 Bean 就获得了自己所在的 ApplicationContext,

6.如果有 Bean 实现了 BeanPostProcessor 接口,则会回调该接口的 postProcessBeforeInitialzation()方法,

7.如果 Bean 实现了 InitializingBean 接口,则会回调该接口的 afterPropertiesSet()方法,

8.如果 Bean 配置了 init-method 方法,则会执行 init-method 配置的方法,

9.如果有 Bean 实现了 BeanPostProcessor 接口,则会回调该接口的 postProcessAfterInitialization()方法,

10.经过流程 9 之后,就可以正式使用该 Bean 了,对于 scope 为 singleton 的 Bean,Spring 的 ioc 容器中会缓存一份该 bean 的实例,而对于 scope 为 prototype 的 Bean,每次被调用都会 new 一个新的对象,期生命周期就交给调用方管理了,不再是 Spring 容器进行管理了

11.容器关闭后,如果 Bean 实现了 DisposableBean 接口,则会回调该接口的 destroy()方法,

12.如果 Bean 配置了 destroy-method 方法,则会执行 destroy-method 配置的方法,至此,整个 Bean 的生命周期结束

13、Resource 是如何被查找、加载的?

Resource 接口是 Spring 资源访问策略的抽象,它本身并不提供任何资源访问实现,具体的资源访问由该接口的实现类完成——每个实现类代表一种资源访问策略。 Spring 为 Resource 接口提供了如下实现类:

  • UrlResource:访问网络资源的实现类。
  • ClassPathResource:访问类加载路径里资源的实现类。
  • FileSystemResource:访问文件系统里资源的实现类。
  • ServletContextResource:访问相对于 ServletContext 路径里的资源的实现类:
  • InputStreamResource:访问输入流资源的实现类。
  • ByteArrayResource:访问字节数组资源的实现类。 这些 Resource 实现类,针对不同的的底层资源,提供了相应的资源访问逻辑,并提供便捷的包装,以利于客户端程序的资源访问。

14、解释自动装配的各种模式?

自动装配提供五种不同的模式供 Spring 容器用来自动装配 beans 之间的依赖注入:

no:默认的方式是不进行自动装配,通过手工设置 ref 属性来进行装配 bean。

byName:通过参数名自动装配,Spring 容器查找 beans 的属性,这些 beans 在 XML 配置文件中被设置为 byName。之后容器试图匹配、装配和该 bean 的属性具有相同名字的 bean。

byType:通过参数的数据类型自动自动装配,Spring 容器查找 beans 的属性,这些 beans 在 XML 配置文件中被设置为 byType。之后容器试图匹配和装配和该 bean 的属性类型一样的 bean。如果有多个 bean 符合条件,则抛出错误。

constructor:这个同 byType 类似,不过是应用于构造函数的参数。如果在 BeanFactory 中不是恰好有一个 bean 与构造函数参数相同类型,则抛出一个严重的错误。

autodetect:如果有默认的构造方法,通过 construct 的方式自动装配,否则使用 byType 的方式自动装配。

15、Spring 中的依赖注入是什么?

依赖注入作为控制反转(IOC)的一个层面,可以有多种解释方式。在这个概念中,你不用创建对象而只需要描述如何创建它们。你不必通过代码直接的将组件和服务连接在一起,而是通过配置文件说明哪些组件需要什么服务。之后 IOC 容器负责衔接。

16、有哪些不同类型的 IOC(依赖注入)?

构造器依赖注入:构造器依赖注入在容器触发构造器的时候完成,该构造器有一系列的参数,每个参数代表注入的对象。

Setter 方法依赖注入:首先容器会触发一个无参构造函数或无参静态工厂方法实例化对象,之后容器调用 bean 中的 setter 方法完成 Setter 方法依赖注入。

17、你推荐哪种依赖注入?构造器依赖注入还是 Setter 方法依赖注入?

你可以同时使用两种方式的依赖注入,最好的选择是使用构造器参数实现强制依赖注入,使用 setter 方法实现可选的依赖关系。

18、Spring IOC 如何实现

Spring 中的 org.springframework.beans 包和 org.springframework.context 包构成了 Spring 框架 IOC 容器的基础。

BeanFactory 接口提供了一个先进的配置机制,使得任何类型的对象的配置成为可能。ApplicationContex 接口对 BeanFactory(是一个子接口)进行了扩展,在 BeanFactory 的基础上添加了其他功能,比如与 Spring 的 AOP 更容易集成,也提供了处理 message resource 的机制(用于国际化)、事件传播以及应用层的特别配置,比如针对 Web 应用的 WebApplicationContext。

org.springframework.beans.factory.BeanFactory 是 Spring IOC 容器的具体实现,用来包装和管理前面提到的各种 bean。BeanFactory 接口是 Spring IOC 容器的核心接口。

19、Spring IOC 容器是什么?

Spring IOC 负责创建对象、管理对象(通过依赖注入)、整合对象、配置对象以及管理这些对象的生命周期。

20、IOC 有什么优点?

IOC 或依赖注入减少了应用程序的代码量。它使得应用程序的测试很简单,因为在单元测试中不再需要单例或 JNDI 查找机制。简单的实现以及较少的干扰机制使得松耦合得以实现。IOC 容器支持勤性单例及延迟加载服务。

21、解释 AOP 模块

AOP 模块用来开发 Spring 应用程序中具有切面性质的部分。该模块的大部分服务由 AOP Aliance 提供,这就保证了 Spring 框架和其他 AOP 框架之间的互操作性。另外,该模块将元数据编程引入到了 Spring。

22、Spring 面向切面编程(AOP)

面向切面编程(AOP):允许程序员模块化横向业务逻辑,或定义核心部分的功能,例如日志管理和事务管理。

切面(Aspect) :AOP 的核心就是切面,它将多个类的通用行为封装为可重用的模块。该模块含有一组 API 提供 cross-cutting 功能。例如,日志模块称为日志的 AOP 切面。根据需求的不同,一个应用程序可以有若干切面。在 Spring AOP 中,切面通过带有@Aspect 注解的类实现。

通知(Advice):通知表示在方法执行前后需要执行的动作。实际上它是 Spring AOP 框架在程序执行过程中触发的一些代码。Spring 切面可以执行一下五种类型的通知:

  • before(前置通知):在一个方法之前执行的通知。
  • after(最终通知):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
  • after-returning(后置通知):在某连接点正常完成后执行的通知。
  • after-throwing(异常通知):在方法抛出异常退出时执行的通知。
  • around(环绕通知):在方法调用前后触发的通知。

切入点(Pointcut):切入点是一个或一组连接点,通知将在这些位置执行。可以通过表达式或匹配的方式指明切入点。

引入:引入允许我们在已有的类上添加新的方法或属性。

目标对象:被一个或者多个切面所通知的对象。它通常是一个代理对象。也被称做被通知(advised)对象。

代理:代理是将通知应用到目标对象后创建的对象。从客户端的角度看,代理对象和目标对象是一样的。有以下几种代理:

  • BeanNameAutoProxyCreator:bean 名称自动代理创建器
  • DefaultAdvisorAutoProxyCreator:默认通知者自动代理创建器
  • Metadata autoproxying:元数据自动代理

织入:将切面和其他应用类型或对象连接起来创建一个通知对象的过程。织入可以在编译、加载或运行时完成。

23、Spring AOP 实现原理

实现 AOP 的技术,主要分为两大类:

  • 一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;
  • 二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。

Spring AOP 的实现原理其实很简单:AOP 框架负责动态地生成 AOP 代理类,这个代理类的方法则由 Advice 和回调目标对象的方法所组成, 并将该对象可作为目标对象使用。AOP 代理包含了目标对象的全部方法,但 AOP 代理中的方法与目标对象的方法存在差异,AOP 方法在特定切入点添加了增强处理,并回调了目标对象的方法。

Spring AOP 使用动态代理技术在运行期织入增强代码。使用两种代理机制:基于 JDK 的动态代理(JDK 本身只提供接口的代理)和基于 CGlib 的动态代理。

1、JDK 的动态代理

JDK 的动态代理主要涉及 java.lang.reflect 包中的两个类:Proxy 和 InvocationHandler。其中 InvocationHandler 只是一个接口,可以通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态的将横切逻辑与业务逻辑织在一起。而 Proxy 利用 InvocationHandler 动态创建一个符合某一接口的实例,生成目标类的代理对象。

其代理对象必须是某个接口的实现, 它是通过在运行期间创建一个接口的实现类来完成对目标对象的代理.只能实现接口的类生成代理,而不能针对类

2、CGLib

CGLib 采用底层的字节码技术,为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类的调用方法,并顺势织入横切逻辑.它运行期间生成的代理对象是目标类的扩展子类.所以无法通知 final、private 的方法,因为它们不能被覆写.是针对类实现代理,主要是为指定的类生成一个子类,覆盖其中方法.

在 Spring 中默认情况下使用 JDK 动态代理实现 AOP,如果 proxy-target-class 设置为 true 或者使用了优化策略那么会使用 CGLIB 来创建动态代理.Spring   AOP 在这两种方式的实现上基本一样.以 JDK 代理为例,会使用 JdkDynamicAopProxy 来创建代理,在 invoke()方法首先需要织入到当前类的增强器封装到拦截器链中,然后递归的调用这些拦截器完成功能的织入.最终返回代理对象.