10道Spring核心面试题
- Spring IoC、AOP 原理
- Spring Bean 生命周期
- Spring Bean 注入是如何解决循环依赖问题的
- 怎样用注解的方式配置 Spring?
- Spring 事务为何失效了
- SpringMVC 的流程?
- Springmvc 的优点:
- Spring 通知类型使用场景分别有哪些?
- IoC 控制反转设计原理?
- Spring 如何处理线程并发问题?
常见Spring核心面试题
1 什么是Spring框架?Spring框架有哪些主要模块?
2 使用Spring框架能带来哪些好处?
3 什么是控制反转(IOC)?什么是依赖注入?
4 在Java中依赖注入有哪些方式?
5 BeanFactory和ApplicationContext有什么区别?
6 Spring提供几种配置方式来设置元数据?
7 如何使用XML配置的方式配置Spring?
8 Spring提供哪些配置形式?
9 怎样用注解的方式配置Spring?
10 请解释Spring Bean的生命周期?
11 Spring Bean作用域的区别是什么?
12 什么是Spring Inner Bean?
13 Spring框架中的单例Bean是线程安全的吗?
14 请举例说明如何在Spring中注入一个Java 集合?
15 如何向Spring Bean中注入java.util.Properties?
16 请解释Spring Bean的自动装配?
17 自动装配有哪些局限性?
18 请解释各种自动装配模式的区别?
19 请举例解释@Required Annotation?
20 请举例说明@Qualifier注解?
21 构造方法注入和设值注入有什么区别?
22 Spring框架中有哪些不同类型的事件?
23 FileSystemResource和ClassPathResource有何区别?
24 Spring 框架中都用到了哪些设计模式?
25 在Spring框架中如何更有效地使用JDBC?使用模板类JdbcTemplete
26 请解释下Spring框架中的IOC容器?
27 在Spring中可以注入或空字符串吗?
针对其中个别问题进行解答
FileSystemResource和ClassPathResource有何区别
在FileSystemResource 中需要给出spring-config.xml文件在你项目中的相对路径或者绝对路径。在ClassPathResource中spring会在ClassPath中自动搜寻配置文件,所以要把ClassPathResource 文件放在ClassPath下。
Spring框架中有哪些不同类型的事件?
Spring的ApplicationContext
提供了支持事件和代码中监听器的功能。即事件监听和事件派发。我们可以创建bean用来监听在ApplicationContext 中发布的事件。ApplicationEvent类和在ApplicationContext接口中处理的事件,如果一个bean实现了ApplicationListener接口,当一个ApplicationEvent 被发布以后,bean会自动被通知。
Spring 提供了以下5种标准的事件:
- 上下文更新事件(ContextRefreshedEvent):在调用ConfigurableApplicationContext 接口中的refresh()方法时被触发。
- 上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件。
- 上下文停止事件(ContextStoppedEvent):当容器调用ConfigurableApplicationContext的Stop()方法停止容器时触发该事件。
- 上下文关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁。
- 请求处理事件(RequestHandledEvent):在Web应用中,当一个http请求(request)结束触发该事件。
在Spring中可以注入或空字符串吗?
<bean id="map" class="xxx">
<!-- 注入空字符串值 -->
<property name="emptyValue">
<value></value>
</property>
<!-- 注入null值 -->
<property name="nullValue"><null/></property>
</bean>
Spring @Required 注释
@Required 注释应用于 bean 属性的 setter 方法,它表明受影响的 bean 属性在配置时必须放在 XML 配置文件中,否则容器就会抛出一个 BeanInitializationException 异常。
Spring 事务为何失效可能原因:
- MySQL 使用的是 MyISAM 引擎,而 MyISAM 是不支持事务的。需要支持使用可以使用 InnoDB 引擎
- 如果使用了 Spring MVC ,context:component-scan 重复扫描问题可能会引起事务失败
- @Transactional 注解开启配置放到 DispatcherServlet 的配置里了。
- @Transactional 注解只能放在public修饰的方法上才起作用,如果放在其他非public(private,protected)方法上,虽然不报错,但是事务不起作用
- @Transactional 同一个类中无事务方法 a() 内部调用有事务方法 b(),那么此时事物不生效。
Spring 如何处理线程并发问题
Spring 使用 ThreadLocal 解决线程安全问题。ThreadLocal 和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。ThreadLocal 会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。 ThreadLocal 提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进 ThreadLocal。
BeanFactory和ApplicationContext有什么区别?
1. BeanFactory负责读取bean配置文档,管理bean的加载,实例化,维护bean之间的依赖关系,负责bean的生命周期。
2. ApplicationContext是BeanFactory的子类,除了提供上述BeanFactory所能提供的功能之外,还提供了其他的功能:
- 提供了支持国际化的文本消息
- 统一的资源文件读取方式
- 已在监听器中注册的 bean 的事件
3. BeanFactory提供懒加载方式,ApplicationContext采用的是预加载,每个Bean都在ApplicationContext启动后实例化。
请解释Spring Bean的自动装配?自动装配有哪些局限性?请解释各种自动装配模式的区别?
通常Bean的属性配置通过Setter和构造器,Spring为我们提供了自动装配的机制,autowire的模式。
局限性:基本数据类型的值、字符串字面量、类字面量无法使用自动装配来注入。模糊特性:自动装配不如显式装配精确,如果有可能,建议使用显式装配。
spring 自动装配 bean方式:
- byName:根据bean的名称进行注入
- byType:根据类型进行注入
- 构造函数:通过构造函数来注入依赖项 需要设置大量参数
<bean id="person" class="constxiong.Person" autowire="byName"></bean>
<bean id="person" class="constxiong.Person" autowire="byType"></bean>
<bean id="person" class="constxiong.Person" autowire="constructor"></bean>
Spring中单例Bean是线程安全的吗?
Spring容器中的Bean是否线程安全,容器本身并没有提供Bean的线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性,但是具体还是要结合具体scope的Bean去研究。
prototype:原型Bean
对于原型Bean,每次创建一个新对象,也就是线程之间并不存在Bean共享,自然是不会有线程安全的问题。
单例Bean
对於单例Bean,所有线程都共享一个单例实例Bean,因此是存在资源的竞争。如果单例Bean,是一个无状态Bean,也就是线程中的操作不会对Bean的成员执行查询以外的操作,那么这个单例Bean是线程安全的。对于有状态的bean,Spring官方提供的bean,一般提供了通过ThreadLocal去解决线程安全的方法,比如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等。
使用ThreadLocal的好处
使得多线程场景下,多个线程对这个单例Bean的成员变量并不存在资源的竞争,因为ThreadLocal为每个线程保存线程私有的数据。这是一种以空间换时间的方式。
什么是 spring 的内部 bean?
假设我们有一个 Student 类,其中引用了 Person 类属性,这个Person 类属性就是内部Bean。
public class Student {
private Person person;
}
public class Person {
private String name;
private String address;
}
Spring Bean 注入是如何解决循环依赖问题的
Spring对于循环依赖的解决不是无条件的
,Spring解决循环依赖的前提是:是针对scope单例
并且显式指明需要解决循环依赖的对象
,而且要求该对象没有被代理过
。同时Spring解决循环依赖也不是万能
,以上三种情况只能解决两种
,第一种在构造方法中相互依赖的情况Spring也无力回天
。对于多例“prototype
”作用域Bean
,Spring容器无法完成依赖注入
,因为“prototype”作用域的Bean,Spring容器不进行缓存
,因此无法提前暴露一个创建中的Bean
。
Spring框架内定义的三级缓存解决了Bean之间的循环依赖。对于单例对象
来说,在Spring的整个容器的生命周期内
,有且只存在一个对象
,这个对象存在Cache
中,Spring大量运用了Cache的手段,在循环依赖问题的解决过程中甚至使用了“三级缓存”
。
Spring循环依赖的理论依据
Spring循环依赖的理论依据其实是Java基于引用传递
,当我们获取到对象的引用时
,对象的field或者或属性是可以延后设置
。
三级缓存:Spring就用这三级缓存巧妙的解决了循环依赖问题
。
- singletonObjects:Cache of singleton objects: bean name --> bean instance,完成初始化的单例对象的 cache(一级缓存)
- earlySingletonObjects:Cache of early singleton objects: bean name--> bean instance ,完成实例化但是尚未初始化的,提前暴光的单例对象的 cache (二级缓存)
- singletonFactories : Cache of singleton factories: bean name -->ObjectFactory,进入实例化阶段的单例对象工厂的 cache (三级缓存)
Bean的创建最为核心三个方法解释如下:
- createBeanInstance:bean实例化,其实也就是调用对象的构造方法实例化对象
- populateBean:属性赋值,这一步主要是对bean的依赖属性进行注入(@Autowired)
- initializeBean:初始化,回到一些形如initMethod、InitializingBean等方法
获取单例Bean过程
Spring首先从
singletonObjects(一级缓存
)中尝试获取
,如果获取不到并且对象在创建中
,则尝试从
earlySingletonObjects(二级缓存
)中获取,如果还是获取不到并且允许从singletonFactories通过getObject获取
,则通过singletonFactory.getObject()(三级缓存
)获取。如果获取到了,则从singletonFactories中移除
,并放入earlySingletonObjects中。其实也就是从三级缓存移动到了二级缓存
。
Spring循环依赖三级缓存原理分析
分析“A的某个field或者setter依赖了B的实例对象,同时B的某个field或者setter依赖了A的实例对象”这种循环依赖的情况。
①A首先完成了生命周期的第一步实例化
,并且
进入实例化阶段的单例对象工厂的singletonFactories
cache中(
此时A已经实例化完成,已经可以被引用了)
,②此时进行生命周期的第二步属性赋值
,发现自己依赖对象B,此时就尝试去get(B),发现B还没有被create,所以走create流程,③B在初始化第一步的时候发现自己依赖了对象A
,于是尝试get(A),尝试一级缓存singletonObjects(肯定没有,因为A还没初始化完全),尝试二级缓存earlySingletonObjects(也没有),尝试三级缓存singletonFactories,由于A实例化时已经存入三级缓存中,所以B能够通过ObjectFactory.getObject拿到A对象
(虽然A还没有初始化完全,但是总比没有好呀),④B拿到A对象后顺利完成了初始化阶段1、2、3
,完全初始化之后将自己放入到一级缓存singletonObjects
中。⑤此时返回A中
,A此时能拿到B的对象顺利完成自己的初始化阶段2、3,最终A也完成了初始化,进去了一级缓存 ⑥到此,B持有的已经是初始化完成的A,A持有的也是初始化完成的B。
文章参考: