Spring 之面试题


Spring面试底层原理的那些问题,你是不是真的懂Spring?

说一下Spring中Bean的生命周期

实例化、属性赋值、初始化、销毁。
1、实例化: 根据配置中的 BeanDefinition 实例化 Bean。调用了Bean的构造函数。
2、属性赋值: Spring 使用依赖注入(构造器或者setter)填充所有属性。
3、初始化之前: 调用 BeanPostProcessors 实现类并注入到容器中的Bean的初始化之前方法。
4、初始化: 调用初始化方法,如果指定了初始化方法。
5、初始化之后: 调用 BeanPostProcessors 实现类并注入到容器中的Bean的初始化之后方法。
6、销毁: 当容器关闭时,调用指定的销毁方法。

Spring怎么解决循环依赖

https://blog.csdn.net/weixin_42390873/article/details/113581473

1、实例化A,A添加到三级缓存。
2、A属性赋值,赋值B对象。
3、从一级缓存中获取 B,获取不到,会实例化 B。
4、B添加到三级缓存。
5、B属性赋值,赋值A对象,依次从一级缓存到三级缓存获取B,最终从三级缓存获取到A,然后将A放到二级缓存。
6、B初始化完毕,将B放入一级缓存,并且从二级三级缓存中移除。B创建完毕,赋值给A。
7、A初始化完毕,将A放入一级缓存,并且从二级三级缓存中移除。
8、A创建完毕。

流程图:
在这里插入图片描述

为什么通过构造器无法解决循环依赖?
因为Spring解决循环依赖是通过Bean的中间态完成的,这个中间态是已经实例化但尚未初始化的状态,也就是一个半成品。
如果实例化的过程中通过构造器创建,无法将一个对象提前暴露出来,因为这时还未创建好,所以构造器无法解决循环依赖。

二级缓存能否解决循环依赖?
通过上面的分析,其实把二级缓存拿掉,在 B 尝试获取 A 的时候直接返回 A 的实例,其实也是可以的。

但是为什么还是用三级缓存呢?
主要为了解决动态代理场景下的循环依赖。
如果去掉三级缓存:在创建代理对象的时候对Bean生命周期有影响。
如果去掉二级缓存:如果A依赖B和C,切B和C也依赖A,三级缓存的getObject()返回的是代理对象,会导致B和C依赖了不同的A。
如果只保留一级缓存呢:如果A依赖B,在实例化B的时候,从一级缓存是获取不到A的,因为这时A还未初始化完毕。

什么是IOC

Spring的IOC,你真的能解释清楚吗?

草率的回答: IOC 是控制反转,Inverse of Control 。
亲,你在做名词翻译吗?作为面试官不应该只想听到这么一点点吧

IOC是一种编程原则,它的设计和架构可以实现组件间的解耦,核心思想是将控制权转移出去。
对象间的依赖关系的维护权利交给了 Spring ,程序本身不再维护。
IOC 为了实现解耦,将原有的对象间的主动依赖改为被动接收型依赖(由直接 new 变为 set )。

IOC与DI、DL的区别

DI:依赖注入,依赖上下文被动接收,暴露setter方法即可,public void setXXX() { ... }
DL:依赖查找,使用上下文(容器)主动获取,applicationContext.getBean(beanName)
IOC 是一种思想、编程原则,DI、DL 是 IOC 思想的一种实现方式。
IOC 的实现方式有依赖查找( Dependency lookup )和依赖注入( Dependency Injection )。

SpringFramework中实现的IOC有什么

BeanFactory 接口提供了一个抽象的配置和对象的管理机制。
ApplicationContext 是 BeanFactory 的子接口,它提供了 AOP 的支持、Web 环境的扩展( WebApplicationContext)、事件发布机制(ApplicationEvent)、Bean后置处理器的支持(BeanPostProcessor )。

BeanFactory与FactoryBean的区别

BeanFactory: IOC 的最底层容器接口。(ApplicationContext 在最底层组合了 BeanFactory
FactoryBean: 创建对象的工厂 Bean ,可以使用它来直接创建一些初始化流程比较复杂的对象。

BeanFactory 和 ApplicationContext 有什么区别?
BeanFactory是Spring中最底层的IOC容器接口,提供了最简单的容器的功能,只提供了实例化对象和拿对象的功能。
ApplicationContext 是Spring的一个更高级的容器,是 BeanFactory 子接口,提供了更多的有用的功能。
注意: ApplicationContext 是基于 BeanFactory扩展而不是继承!因为在底层,无论是 AbstractRefreshableApplicationContext 还是 GenericApplicationContext ,底层都是组合了一个 DefaultListableBeanFactory

ApplicationContext 主要扩展了以下功能: AOP 的支持、配置元信息(BeanDefinition)、资源管理、事件驱动机制、消息与国际化。

ApplicationContext的类型

ApplicationContext 分为两种:

  • 基于 xml 配置文件(ClassPathXmlApplicationContextFileSystemXmlApplicationContext )区别是加载 xml 配置文件的基准路径不同。
  • 基于注解驱动配置类,只有 AnnotationConfigApplicationContext

对比BeanPostProcessor与BeanFactoryPostProcessor

在这里插入图片描述

依赖注入的注入方式

  • 构造器注入:xml、编程式注入,不依赖Spring框架的API
    在这里插入图片描述
    在这里插入图片描述
  • setter注入:xml、编程式注入,不依赖Spring框架的API
    在这里插入图片描述
    在这里插入图片描述
  • 参数注入:只能通过标注注解来侵入式注入,依赖Spring框架的API
    在这里插入图片描述
    在这里插入图片描述
    你觉得哪种方式好?为什么?
    如果是业务类,推荐使用参数注入,因为可能会随时增加依赖其他类。
    如果是工具类,推荐使用setter注入,因为属性一般不会变动。
    构造器注入目前没有用过,最大的弊端是如果属性过多,构造器的参数列表会很长。

自动注入的注解对比

@Autowired:根据类型注入,Spring原生注解,可指定 required=false 来避免注入失败,配置 @Qualifier 指定名称。
@Resource:根据名称注入,JSR250规范,可通过type参数指定类型。
@Inject:根据类型注入,JSR330规范,需要导jar包,基本不用。

依赖注入4连问

依赖注入的目的和优点?
解耦,我们不再需要直接去 new 那些依赖的类对象,直接依赖会导致对象的创建机制、初始化过程难以统一控制。
依赖对象可配置,通过 xml 或者注解声明,可以指定和调整组件注入的对象,借助 Java 的多态特性,可以不需要大批量的修改就完成依赖注入的对象替换。
谁把什么注入给谁了?
IOC 容器把需要依赖的对象注入给待注入的组件。
依赖注入具体是如何注入的?
使用setter注入还是构造器注入?
推荐 setter 注入,理由是如果一个 Bean 有多个依赖时,构造器的参数列表会很长;而且如果 Bean 中依赖的属性不都是必需的话,注入会变得更麻烦。

Spring 框架中都用到了哪些设计模式

  • 代理模式:AOP 思想的底层实现技术,Spring 中采用 JDK Proxy 和 CgLib 类库。
  • 单例模式:在 Spring配置文件中定义的 bean 默认为单例模式。
  • 模板模式:用来解决代码重复的问题。比如:RestTemplate, JdbcTemplate。
  • 委派模式:Spring 提供了 DispatcherServlet 来对请求进行分发。
  • 工厂模式:BeanFactory 用来创建对象的实例,贯穿于 BeanFactory / ApplicationContext。

Bean的作用域

作用域类型概述
singleton一个 IOC 容器中只有一个【默认值】
prototype每次获取创建一个
request一次请求创建一个(仅Web应用可用)
session一个会话创建一个(仅Web应用可用)
application一个 Web 应用创建一个(仅Web应用可用)
websocket一个 WebSocket 会话创建一个(仅Web应用可用)

AOP

AOP思想的实现是基于代理模式 ,在JAVA中一般采用JDK动态代理模式,但是JDK动态代理模式
只能代理接口而不能代理类。所以Spring AOP 同时支持 CGLIB、JDK动态代理。
如果目标对象的实现类实现了接口,Spring AOP 将会采用 JDK 动态代理来生成 AOP 代理类。
如果目标对象的实现类没有实现接口,Spring AOP 将会采用 CGLIB 来生成 AOP 代理类。

概述BeanDefinition

BeanDefinition: Spring容器创建对象的图纸,一个对象对应一个图纸。
BeanFactoryPostProcessor: Spring容器创建对象,修改图纸。

BeanDefinition 描述了 IOC 容器中 bean 的元信息,它包含 bean 的类信息、属性、行为、依赖关系、配置信息等。BeanDefinition 具有层次性,并且可以在 IOC 容器初始化阶段被 BeanDefinitionRegistryPostProcessor 构造和注册,被 BeanFactoryPostProcessor 拦截修改等。

Spring使用注解注入属性为null的问题排查

保证Bean能够交付给Spring容器进行统一的管理:
1、先检查一下自己的Spring配置
2、组件上面是否加入了合适的注解。例如:@Controller,@Service, @Repository,@Component等
3、检查一下你context-scan扫描器配置的路径是否正确,即你的包扫描器是否能够扫描到你的注解组件

检查实例化的方式:
可能是获取对象的方式出错了。
当我们想要从中获取一个Bean的实例时,应该从Spring容器当中获取。
而如果我们自己new一个类的实例,便绕过了容器的依赖注入过程,因此也可能出现获取不到应有的属性这种情况。

检查注入的位置:
如果把@Autowired注解加在了一个静态属性上,也无法注入属性值。
Spring帮助我们做属性的自动注入,是在帮助我们实例化Bean之后完成的。而static属性标示该属性是归属于类而不是实例的。换句话说,当我们想给static属性注入值的时候,对象还没创建的。因此一定会出现注入为空的情况。

由于Bean初始化顺序导致的问题:
这种情况是在业务当中真实可能出现,并且并不好思考的地方。楼主遇到的场景是实现了一个Filter,在Filter当中需要注入业务层组件实现一些过滤逻辑。代码写完之后我发现ServerBean一直为空,我把上面三种方式可能出现问题的地方都做了检查也没有解决。后来Google了一下让我茅塞顿开。原来web容器启动是按照一定顺序的,即:Listener --> Filter -->Servlet。而我们的Service组件依赖与Servlet的Controller组件而创建,因此在创建Filter实例时,由于Servlet实例还没有初始化也会导致依赖无法注入的情况。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值