SpringBoot 核心面试题(一)

1、Spring 是什么?

Spring 是一个轻量级的 IoC 和 AOP 容器框架

Spring 是为 Java 应用程序提供基础性服务的一套框架,目的是用于简化企业应用程序的 开发,它使得开发者只需要关心业务需求。常见的配置方式有三种:基于 XML 的配置、基于注解的配置、基于 Java 的配置。主要由以下几个模块组成:

Spring Core:核心容器,提供 IOC 服务;核心容器提供Spring框架的基本功能。Spring以bean的方式组织和管理Java应用中的各个组件及其关系。Spring使用BeanFactory来产生和管理Bean,它是工厂模式的实现。BeanFactory使用控制反转(IoC)模式将应用的配置和依赖性规范与实际的应用程序代码分开。

Spring Context:应用上下文。Spring上下文是一个配置文件,向Spring框架提供上下文信息。Spring上下文包括企业服务,如JNDI、EJB、电子邮件、国际化、校验和调度功能。

Spring AOP:Spring面向切面编程。通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring框架中。所以,可以很容易地使 Spring框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。

Spring DAO:JDBC和DAO模块。JDBC、DAO的抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理,和不同数据库供应商所抛出的错误信息。异常层次结构简化了错误处理,并且极大的降低了需要编写的代码数量,比如打开和关闭链接。

Spring ORM:对象实体映射。Spring框架插入了若干个ORM框架,从而提供了ORM对象的关系工具,其中包括了Hibernate、JDO和 IBatis SQL Map等,所有这些都遵从Spring的通用事务和DAO异常层次结构。

Spring Web:Web模块。Web上下文模块建立在应用程序上下文模块之上,为基于web的应用程序提供了上下文。所以Spring框架支持与Struts集成,web模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。

Spring MVC:MVC模块。MVC框架是一个全功能的构建Web应用程序的MVC实现。通过策略接口,MVC框架变成为高度可配置的。MVC容纳了大量视图技术,其中包括JSP、POI、FreeMarker等,模型来有JavaBean来构成,存放于m当中,而视图是一个接口,负责实现模型,控制器表示逻辑代码。Spring框架的功能可以用在任何J2EE服务器当中,大多数功能也适用于不受管理的环境。Spring的核心要点就是支持不绑定到特定J2EE服务的可重用业务和数据的访问的对象,毫无疑问这样的对象可以在不同的J2EE环境,独立应用程序和测试环境之间重用。



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

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

Spring 为 Resource 接口提供了如下实现类:

UrlResource:访问网络资源的实现类。

ClassPathResource:访问类加载路径里资源的实现类。

FileSystemResource:访问文件系统里资源的实现类。

ServletContextResource:访问相对于 ServletContext 路径里的资源的实现类:

InputStreamResource:访问输入流资源的实现类。

ByteArrayResource:访问字节数组资源的实现类。

这些 Resource 实现类,针对不同的的底层资源,提供了相应的资源访问逻辑,并提供便捷的包装,以利于客户端程序的资源访问。



3、Spring 的AOP 理解:

OOP面向对象,允许开发者定义纵向的关系,但并适用于定义横向的关系,导致了大量代码的重复,而不利于各个模块的重用。AOP,一般称为面向切面,作为面向对象的一种补充,用于将那些不业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系
统的可维护性。可用于权限认证、日志、事务处理。

AOP 实现的关键在于代理模式,AOP 代理主要分为静态代理和动态代理。静态代理的代表为 AspectJ;动态代理则以 Spring AOP 为代表。

(1)AspectJ 是静态代理的增强,所谓静态代理,就是 AOP 框架会在编译阶段生成 AOP 代理类,因此也称为编译时增强,他 会在编译阶段将 AspectJ(切面)织入到 Java 字节码中,运行的时候就是增强后的 AOP 对象。

(2)Spring AOP 使用的动态代理,所谓的动态代理就是说 AOP 框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个 AOP 对象,这个 AOP 对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。

Spring AOP 中的动态代理主要有两种方式,JDK 动态代理和 CGLIB 动态代理:

①JDK 动态代理只提供接口的代理,不支持类的代理。核心 InvocationHandler 接口和 Proxy 类,InvocationHandler 通 过 invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy 利用 InvocationHandler 动态创建一个符合某一接口的的实例, 生成目标类的代理对象。

②如果代理类没有实现 InvocationHandler接口,那么 Spring AOP 会选择使用 CGLIB 来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,

从而实现 AOP。CGLIB 是通过继承的方式做的动态代理,因此如果某个类被标记为 final,那么它是无法使用 CGLIB 做动态代理的。

(3)静态代理不动态代理区别在于生成 AOP 代理对象的时机不同,相对来说 AspectJ 的静态代理方式具有更好的性能,但是 AspectJ 需要特定的编译器动行处理,而 Spring AOP 则无需特定的编译器处理。

InvocationHandler 的 invoke(Object proxy,Method method,Object[] args):proxy 是最终生成的代理实例; method 是被代理目 标实例的某个具体方法; args 是被代理目标实例某个方法的具体入参, 在方法反射调用时使用。



4、Spring 的 IoC 理解:

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

(2)最直观的表达就是,IOC 让对象的创建不用去 new 了,可以由 spring 自动生产,使用 java 的反射机制,根据配置文件在运行时动态的去创建对象以及管理对象,并调用对象的方法的。

(3)Spring 的 IOC 有三种注入方式 :极造器注入、setter 方法注入、根据注解注入。

IoC 让相互协作的组件保持松散的耦合,而 AOP 编程允许你把遍布于应用各层的功能分离出来形成可重用的功能组件。



5、BeanFactory 和 ApplicationContext 有什么区别?

BeanFactory 和 ApplicationContext 是 Spring 的两大核心接口,都可以当做 Spring 的容器。其中
ApplicationContext 是 BeanFactory 的子接口。

(1)BeanFactory:是 Spring 里面最底层的接口,包含了各种 Bean 的定义,读取 bean 配置文档,管理 bean 的加载、实例化,控制

bean 的生命周期,维护 bean 之间的依赖关系。ApplicationContext接口作为 BeanFactory 的派生,除了提供BeanFactory 所具有的 功能外,还提供了更完整的框架功能:

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

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

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

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

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

(2)①BeanFactroy 采用的是延迟加载形式来注入 Bean 的,即只有在使用到某个 Bean 时(调用getBean()),才对该 Bean 动行加载实 例化。这样,我们就不能发现一些存在的 Spring 的配置问题。如果 Bean 的某一个属性没有注入,BeanFacotry 加载后,直至第一次使 用调用 getBean 方法才会抛出异常。

②ApplicationContext,它是在容器启动时,一次性创建了所有的 Bean。这样,在容器启动时,我们就可以发现 Spring 中存在的 配置错误,这样有利于检查所依赖属性是否注入。ApplicationContext 启动后预载入所有的单实例 Bean,通过预载入单实例 bean ,确 保当你需要的时候,你就不用等待,因为它们已经创建好了。

③相对于基本的 BeanFactory,ApplicationContext 唯一的不足是占用内存空间。当应用程序配置 Bean较多时,程序启动较慢。

(3)BeanFactory 通常以编程的方式被创建,ApplicationContext 还能以声明的方式创建,如使用 ContextLoader。

(4)BeanFactory 和 ApplicationContext 都支持 BeanPostProcessor、BeanFactoryPostProcessor 的使用,但两者之间的区别是:

BeanFactory 需要手动注册,而 ApplicationContext 则是自动注册。

BeanFactory

ApplicationContext

它使用懒加载

它使用即时加载

它使用语法显式提供资源对象

它自己创建和管理资源对象

不支持国际化

支持国际化

不支持基于依赖的注解

支持基于依赖的注解



6、请解释 Spring Bean 的生命周期?

l Bean 容器找到配置文件中 Spring Bean 的定义。

l Bean 容器利用 Java 反射创建一个Bean的实例。

l 如果涉及到一些属性值 利用 set()方法设置一些属性值。

l 如果Bean 实现了 BeanNameAware 接口,调用 setBeanName()方法,传入Bean的名字。

l 如果Bean 实现了 BeanClassLoaderAware 接口,调用 setBeanClassLoader()方法,传入 ClassLoader对象的实例。

l 如果Bean实现了 BeanFactoryAware 接口,调用 setBeanClassLoader()方法,传入 ClassLoader对象的实例。

l 与上面的类似,如果实现了其他 *.Aware接口,就调用相应的方法。

l 如果有和加载这个 Bean 的 Spring 容器相关的BeanPostProcessor 对象,执行postProcessBeforeInitialization()方法

l 如果Bean实现了InitializingBean接口,执行afterPropertiesSet()方法。

l 如果Bean 在配置文件中的定义包含 init-method 属性,执行指定的方法。

l 如果有和加载这个 Bean的 Spring 容器相关的BeanPostProcessor 对象,执行postProcessAfterInitialization()方法

l 当要销毁 Bean 的时候,如果 Bean 实现了 DisposableBean 接口,执行 destroy() 方法。

l 当要销毁 Bean 的时候,如果 Bean在配置文件中的定义destroy-method 属性,执行指定的方法。


7、 解释 Spring 支持的几种 bean 的作用域。

Spring 容器中的 bean 可以分为 5 个范围:

(1)singleton:默认,每个容器中只有一个 bean 的实例,单例的模式由 BeanFactory 自身来维护。

(2)prototype:为每一个 bean 请求提供一个实例。

(3)request:为每一个网络请求创建一个实例,在请求完成以后,bean 会失效并被垃圾回收器回收。

(4)session:与request 范围类似,确保每个 session 中有一个 bean 的实例,在 session 过期后,bean 会随之失效。

(5)global-session:全局作用域,global-session 和 Portlet 应用相关。当你的应用部署在 Portlet 容器中工作时,它包含很多 portlet。如果你想要声明让所有的 portlet 共用全局的存储变量的话,那么这全局变量需要存储在 global-session 中。全局作用域与Servlet 中的 session 作用域效果相同

8、Spring 框架中的单例 Beans 是线程安全的么?

Spring 框架并没有对单例 bean 动行任何多线程的封装处理。关于单例 bean 的线程安全和并发问题需要开发者自行去搞定。但实际上,大部分的 Spring bean 并没有可变的状态(比如 Serview 类和 DAO 类),所以在某种程度上说 Spring 的单例 bean 是线程安全的。如果你的 bean 有多种状态的话(比如 View Model 对象),就需要自行保证线程安全。最浅显的解决办法就是将多态 bean 的作用域由“singleton”变更为“prototype”。


9、Spring 如何处理线程并发问题?

在一般情况下,只有无状态的 Bean 才可以在多线程环境下共享,在 Spring 中,绝大部分 Bean 都可以声明为 singleton 作用域,因为 Spring 对一些 Bean 中非线程安全状态采用 ThreadLocal 动行处理,解决线程安全问题。ThreadLocal 和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。同步机制采用了“时间换空间”的 方式,仅提供一份变量,不同的线程在访问前需要获取锁,没获得锁的线程则需要排队。而 ThreadLocal 采用了“空间换时 间”的方式。

ThreadLocal 会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程 都拥有自己的变量副本,从而也就没有必要对该变量动行同步了。ThreadLocal 提供了线程安全的共享对象,在编写多线程 代码时,可以把不安全的变量封装动 ThreadLocal。


10-1、Spring 基于 xml 注入bean 的几种方式:

平常的 Java 开发中,程序员在某个类中需要依赖其它类的方法。通常是 new 一个依赖类的实例再调用该实例的方 法,这种开发存在的问题是new 的类实例不好统一管理。

Spring 提出了依赖注入的思想,即依赖类不由程序员实例化,而是通过 Spring 容器帮我们 new 指定实例并且将实例注入 到需要该对象的类中。依赖注入的另一种说法是”控制反转”。通俗的理解是:平常我们 new 一个实例,这个实例的控制 权是我们程序员。而控制反转是指 new 实例工作不由我们程序员来做而是交给 Spring 容器来做。Spring 有多种依赖注入 的形式

(1) Set 方法注入;

(2)极造器注入:①通过 index 设置参数的位置;②通过 type 设置参数类型;

(3)静态工厂注入;

(4)实例工厂;


10-2、Spring 的自动装配:

在 spring 中,对象无需自己查找或创建不其关联的其他对象,由容器负责把需要相互协作的对象引用赋予各个对象,使用 autowire 来配置自动装载模式。

在 Spring 框架 xml 配置中共有 5 种自动装配:

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

(2)byName:通过 bean 的名称进行自动装配,如果一个 bean 的 property 不另一bean 的 name 相同,就进行自动 装配。

(3)byType:通过参数的数据类型进行自动装配。

(4)constructor:利用极造函数进行装配,并且极造函数的参数通过 byType 进行装配。

(5)autodetect:自动探测,如果有极造方法,通过 construct 的方式自动装配,否则使用 byType 的方式自动装配。

基于注解的方式:

使用@Autowired 注解来自动装配指定的 bean。在使用@Autowired 注解之前需要在 Spring 配置文件进行配置,<context:annotation-config />。在吭动 spring IoC 时,容器自动装载了一个 AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource 或@Inject 时,就会在 IoC 容器自动查找需要的 bean,并装配给该 对象的属性。在使用@Autowired 时,首先在容器中查询对应类型的 bean:

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

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

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