Spring基础框架四:常见高频Spring面试题

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种标准的事件:

  1. 上下文更新事件(ContextRefreshedEvent):在调用ConfigurableApplicationContext 接口中的refresh()方法时被触发。
  2. 上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件。
  3. 上下文停止事件(ContextStoppedEvent):当容器调用ConfigurableApplicationContext的Stop()方法停止容器时触发该事件。
  4. 上下文关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁。
  5. 请求处理事件(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 事务为何失效可能原因:

  1. MySQL 使用的是 MyISAM 引擎,而 MyISAM 是不支持事务的。需要支持使用可以使用 InnoDB 引擎
  2. 如果使用了 Spring MVC ,context:component-scan 重复扫描问题可能会引起事务失败
  3. @Transactional 注解开启配置放到 DispatcherServlet 的配置里了。
  4. @Transactional 注解只能放在public修饰的方法上才起作用,如果放在其他非public(private,protected)方法上,虽然不报错,但是事务不起作用
  5. @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方式:

  1. byName:根据bean的名称进行注入
  2. byType:根据类型进行注入
  3. 构造函数:通过构造函数来注入依赖项 需要设置大量参数
<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。

文章参考:

https://blog.csdn.net/fedorafrog/article/details/104550165/

https://www.toutiao.com/a6775869522067849732/?timestamp=1586051079&app=news_article&group_id=6775869522067849732&req_id=202004050944380101290421342E7E5266

https://www.toutiao.com/a6727050408939749901/?timestamp=1586050937&app=news_article&group_id=6727050408939749901&req_id=20200405094216010129049101007E40EA

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章