Spring 之 高频面试题

1、什么是Spring框架,Spring框架主要包含哪些模块

Spring是一个轻量级的Java 开发框架。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。Spring 帮助 开发者解决了开发中基础性的问题,使得开发人员可以专注于应用程序的开发。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。Spring的核心是控制反转(IoC)和面向切面(AOP)。简单来说,Spring是一个分层的full-stack(一站式) 轻量级开源框架。

主要包含的模块:
Spring 之 高频面试题_第1张图片

2、Spring框架的优势

1、Spring通过DI、AOP和消除样板式代码来简化企业级Java开发

2、Spring框架之外还存在一个构建在核心框架之上的庞大生态圈,它将Spring扩展到不同的领域,如Web服务、REST、移动开发以及NoSQL

3、低侵入式设计,代码的污染极低

4、独立于各种应用服务器,基于Spring框架的应用,可以真正实现Write Once,Run Anywhere的承诺

5、Spring的IoC容器降低了业务对象替换的复杂性,提高了组件之间的解耦

6、Spring的AOP允许将一些通用任务如安全、事务、日志等进行集中式处理,从而提供了更好的复用

7、Spring的ORM和DAO提供了与第三方持久层框架的的良好整合,并简化了底层的数据库访问

8、Spring的高度开放性,并不强制应用完全依赖于Spring,开发者可自由选用Spring框架的部分或全部

3、IOC和DI是什么?

控制反转是就是应用本身不负责依赖对象的创建和维护,依赖对象的创建及维护是由外部容器负责的,这样控制权就有应用转移到了外部容器,控制权的转移就是控制反转。

依赖注入是指:在程序运行期间,由外部容器动态地将依赖对象注入到组件中。一般,通过构造函数注入、setter注入和注解(接口)注入

4、描述下Spring IOC容器的初始化过程

Spring IOC容器的初始化简单的可以分为三个过程:

第一个过程是Resource资源定位。这个Resouce指的是BeanDefinition的资源定位。这个过程就是容器找数据的过程,就像水桶装水需要先找到水一样。

第二个过程是BeanDefinition的载入过程。这个载入过程是把用户定义好的Bean表示成Ioc容器内部的数据结构,而这个容器内部的数据结构就是BeanDefition。

第三个过程是向IOC容器注册这些BeanDefinition的过程,这个过程就是将前面的BeanDefition保存到HashMap中的过程。

5、BeanFactory 和 FactoryBean的区别?

  • BeanFactory是个Factory,也就是IOC容器或对象工厂,在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的,提供了实例化对象和拿对象的功能。

    使用场景:

    • 从Ioc容器中获取Bean(byName or byType)
    • 检索Ioc容器中是否包含指定的Bean
    • 判断Bean是否为单例
  • FactoryBean是个Bean,这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似。

    使用场景

    • ProxyFactoryBean

6、BeanFactory和ApplicationContext的异同

Spring 之 高频面试题_第2张图片

相同:

  • Spring提供了两种不同的IOC 容器,一个是BeanFactory,另外一个是ApplicationContext,它们都是Java interface,ApplicationContext继承于BeanFactory(ApplicationContext继承ListableBeanFactory)。
  • 它们都可以用来配置XML属性,也支持属性的自动注入。
  • BeanFactory 和 ApplicationContext 都提供了一种方式,使用getBean(“bean name”)获取bean。

不同:

  • 当你调用getBean()方法时,BeanFactory仅实例化bean,而ApplicationContext 在启动容器的时候实例化单例bean,不会等待调用getBean()方法时再实例化。
  • BeanFactory不支持国际化,即i18n,但ApplicationContext提供了对它的支持。
  • ApplicationContext能够将事件发布到注册为监听器的bean。
  • BeanFactory 的一个核心实现是XMLBeanFactory 而ApplicationContext 的一个核心实现是ClassPathXmlApplicationContext,Web容器的环境我们使用WebApplicationContext并且增加了getServletContext 方法。
  • 如果使用自动注入并使用BeanFactory,则需要使用API注册AutoWiredBeanPostProcessor,如果使用ApplicationContext,则可以使用XML进行配置。
  • 简而言之,BeanFactory提供基本的IOC和DI功能,而ApplicationContext提供高级功能,BeanFactory可用于测试和非生产使用,但ApplicationContext是功能更丰富的容器实现,应该优于BeanFactory

7、Spring Aware接口作用

在Spring中有很多以Aware结尾的接口.如果一个Bean实现了该接口,那么当该Bean被spring初始化时,Spring会向该Bean注入相关资源(就是会回调接口中的方法)。与Spring建立强耦合,通过Spring的相关配置类进行特定操作。

接口名称 作用
ApplicationContextAware 获取spring 上下文环境的对象
BeanNameAware 获取该bean在BeanFactory配置中的名字
BeanFactoryAware 获取创建当前bean的BeanFactory实例
ServletContextAware 获取servletContext容器
ResourceLoaderAware 获取ResourceLoader对象,通过它获得各种资源

8、Spring Bean 的生命周期?

Spring 之 高频面试题_第3张图片
总结:

(1)实例化Bean:

对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。对于ApplicationContext容器,当容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的bean。

(2)设置对象属性(依赖注入):

实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息 以及 通过BeanWrapper提供的设置属性的接口完成依赖注入。

(3)处理Aware接口:

接着,Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给Bean:

①如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,此处传递的就是Spring配置文件中Bean的id值;

②如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传递的是Spring工厂自身。

③如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文;

(4)BeanPostProcessor:

如果想对Bean进行一些自定义的处理,那么可以让Bean实现了BeanPostProcessor接口,那将会调用postProcessBeforeInitialization(Object obj, String s)方法。

(5)InitializingBean 与 init-method:

如果Bean实现InitializingBean接口,则直接调用afterPropertiesSet方法。例如执行自定义初始化或仅检查所有必需属性是否已设置。

如果Bean在Spring配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法。

(6)如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法;由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术;

以上几个步骤完成后,Bean就已经被正确创建了,之后就可以使用这个Bean了。

(7)DisposableBean:

当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的destroy()方法;

(8)destroy-method:

最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。

9、Spring AOP的实现原理?

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

Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。

如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。

10、Spring 是如何管理事务的?

Spring事务管理主要包括3个接口,Spring的事务主要是由它们(PlatformTransactionManager、TransactionDefinition、TransactionStatus)三个共同完成的。

1. PlatformTransactionManager:事务管理器–主要用于平台相关事务的管理

主要有三个方法:

  • commit 事务提交;
  • rollback 事务回滚;
  • getTransaction 获取事务状态。

2. TransactionDefinition:事务定义信息–用来定义事务相关的属性,给事务管理器PlatformTransactionManager使用

这个接口有下面四个主要方法:

  • getIsolationLevel:获取隔离级别;
  • getPropagationBehavior:获取传播行为;
  • getTimeout:获取超时时间;
  • isReadOnly:是否只读(保存、更新、删除时属性变为false–可读写,查询时为true–只读)

事务管理器能够根据这个返回值进行优化,这些事务的配置信息,都可以通过配置文件进行配置。

3. TransactionStatus:事务具体运行状态–事务管理过程中,每个时间点事务的状态信息。

例如它的几个方法:

  • hasSavepoint():返回这个事务内部是否包含一个保存点,
  • isCompleted():返回该事务是否已完成,也就是说,是否已经提交或回滚
  • isNewTransaction():判断当前事务是否是一个新事务

声明式事务的优缺点

  • 优点:不需要在业务逻辑代码中编写事务相关代码,只需要在配置文件配置或使用注解(@Transaction),这种方式没有侵入性。
  • 缺点:声明式事务的最细粒度作用于方法上,如果像代码块也有事务需求,只能变通下,将代码块变为方法。

11、Spring 的不同事务传播行为有哪些?

Spring 之 @Transaction 详解

12、Spring 中用到了那些设计模式?

  • 代理模式:AOP 思想的底层实现技术,Spring 中采用 JDK Proxy 和 CgLib 类库。
  • 单例模式:在Spring配置文件中定义的bean默认为单例模式。
  • 模板方法:用来解决代码重复的问题。比如 RestTemplate, JmsTemplate, JpaTemplate。
  • 委派模式:Srping MVC 提供了 DispatcherServlet 来对请求进行分发。
  • 工厂模式:BeanFactory用来创建对象的实例,贯穿于 BeanFactory / ApplicationContext 接口的核心理念。
  • 适配器模式:Spring MVC 会通过 HandlerAdapter 适配器来执行 Handler 处理器处理请求。
  • 观察者模式:Spring 中 ApplicationContext 上下文会发送五种标准事件给监听器。
  • 责任链模式:AOP 实现中,标注了@Before、@after、@afterReturning、@AfterThrowing、@around 注解的方法会形成一条链路再执行。

13、Spring如何解决循环依赖?

构造器注入方式的循环依赖,无法解决;

Setter注入方式的循环依赖,解决方式:

  1. Spring先用构造器实例化Bean对象,将实例化结束的对象放到一个Map中,并且Spring提供获取这个未设置属性的实例化对象的引用方法
  2. 在进行属性注入的时候,依次获取到对应的Bean实例对象进行注入

代码层面使用三级缓存解决循环依赖:

Spring单例对象初始化的三个步骤,实例化对象、填充属性(构造器注入和属性注入)、执行初始化方法

Spring中是通过以下三个缓存器解决属性注入的循环依赖问题

  • singletonFactories : 单例对象工厂的cache,缓存第一步实例化后的对象
  • earlySingletonObjects :提前曝光的单例对象的Cache ,缓存第二步填充属性后的对象(用于检测循环引用,与singletonFactories互斥)
  • singletonObjects:单例对象的cache,缓存第三步初始化完成后的对象

14、bean的作用域

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

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

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

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

15、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)结束触发该事件。

16、AOP有哪些通知类型?

(1)前置通知(Before advice):在某连接点(join point)之前执行的通知,但这个通知不能阻止连接点前的执行(除非它抛出一个异常)。

(2)返回后通知(After returning advice):在某连接点(join point)正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。

(3)抛出异常后通知(After throwing advice):在方法抛出异常退出时执行的通知。

(4)后通知(After (finally) advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。

(5)环绕通知(Around Advice):包围一个连接点(join point)的通知,如方法调用。这是最强大的一种通知类型。 环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。 环绕通知是最常用的一种通知类型。

17、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配置文件进行配置,。在启动spring IoC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找需要的bean,并装配给该对象的属性。在使用@Autowired时,首先在容器中查询对应类型的bean:

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

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

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

@Autowired可用于:构造函数、成员变量、Setter方法

注:@Autowired和@Resource之间的区别

(1) @Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。

(2) @Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入。

18、BeanFactoryPostProcessor 和 BeanPostProcessor

  • BeanFactoryPostProcessor:可以在Bean实例化前,通过ConfigurableListableBeanFactory拿到所有BeanDefinition信息。可以通过修改BeanDefinition属性值等操作,可以达到针对某一类特殊bean进行修改的目的。
  • BeanPostProcessor:在Bean实例化和依赖注入后,可以在Bean的初始化方法前后增加自定义操作。通过获取到当前实例对象,进行修改属性值等操作。

19、Spring 框架中的单例 Bean 是线程安全的么?

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

你可能感兴趣的:(Spring,Spring)