文章目录
- 在Spring中,Bean的作用域有哪几个?
- SpringMVC的执行流程你知道吗?
- 谈谈你对Spring IOC的理解?
- DI又是什么?
- 谈谈你对Spring AOP的理解?
- Spring Bean的生命周期你能说出多少?
- Spring如何解决循环依赖的问题?
- 什么是循环依赖?
- 案例引入
- 产生循环依赖的三种情况
- 循环依赖的解决
在Spring中,Bean的作用域有哪几个?
singleton
:单例prototype
:多例request
:spring创建一个bean
,并将其放入到request
域当中session
:spring创建一个bean
,并将其放入到session
域当中global session
:全局作用域,所有会话共享一个作用域
SpringMVC的执行流程你知道吗?
DispatcherServlet
执行请求的解析与转发,将请求转发给HandlerMapper
HandlerMapping
返回资源的执行顺序HandlerExcutionChain
DispatcherServlet
再调用HandlerAdapter
,请求执行handler
HandlerAdapter
到Handler
请求资源Handler
返回一个ModelAndView
(Handler相当于我们自己写的controller)DispatcherServlet
将ModelAndView
发送给视图解析器ViewResolver
ViewResolver
解析ModelAndView
返回一个视图对象View
DispatcherServlet
对view
渲染,将数据显示在页面上
- DispatcherServlet:相当于转发器
- HandlerMapping:请求URL查找handler
- HandlerAdapter:执行handler
- Handler:需要我们自己开发
- ViewResolver:视图解析器,进行视图解析
谈谈你对Spring IOC的理解?
IOC
,Inversion Of Control
,控制反转,即将创建对象的权利移交给spring
容器,由spring
容器来统一管理对象及其生命周期和对象之间的依赖关系。
控制:创建对象的权利
反转:将创建对象的权利交给spring容器
之前我们创建某个对象的时候,主动权在我们自己手中,所以我们可以使用new
关键字去创建一个对象,但是在这种情况下,会造成对象和其他类耦合的情况。但是引入IOC
之后,创建对象的主动权就在spring
容器手中了,当我们需要某个对象的时候,只需要向spring
容器去要就可以了。可以看出,IOC
能起到解耦的作用。
spring IOC
用到了工厂设计模式。
DI又是什么?
通过上个问题,我们知道IOC
只是一种设计思想,它是指将创建对象的权利交给spring
容器,而这种思想就是依靠DI
来实现的。
DI
:Dependency Injection
,即依赖注入,DI
是反射思想的体现,即允许在程序运行时动态的生成对象。
谈谈你对Spring AOP的理解?
AOP
:Aspect oriented programming
, 即面向切面编程,是面向对象的一种补充。AOP
的含义就是将对多个对象产生影响的公共行为和逻辑抽取出来并封装成一个通用的模块,这个模块就叫做切面,所以AOP
可以提高代码复用和解耦。
Spring AOP
中的几个概念:
- 切点:被增强的目标方法
- 通知:对目标方法进行增强的方法
- 切面:通知和切点的结合
- 织入:切点和通知结合的过程
- 切面类:含有通知方法
具体可以看我写的这一篇文章:你还不知道什么是Spring AOP?
Spring Bean的生命周期你能说出多少?
总的来说,Spring Bean
的生命周期可以分为四个阶段:
- 实例化:
Instantiation
- 属性赋值:
Populate
- 初始化:
Initialization
- 销毁:
Destruction
接下来,我们对这四个阶段扩展一下:
我们再来详细解读一下这几个过程:
- 实例化:实例化一个
Bean
- 属性赋值:为
Bean
设置相关属性和依赖 Aware
接口:让Bean
能拿到容器的一些资源
BeanPostProcessor
:如果想对Bean
进行一些自定义的前置处理,那么可以让Bean
实现了BeanPostProcessor
接口
InitializingBean
:如果Bean
实现了InitializingBean
接口,执行afeterPropertiesSet()
方法。
init-method
:如果Bean
在Spring
配置文件中配置了init-method
属性,则会自动调用其配置的初始化方法。
BeanPostProcessor
后置处理:如果这个Bean
实现了BeanPostProcessor
接口,将会调用postProcessAfterInitialization(Object obj, String s)
方法DisposableBean
:当Bean
不再需要时,会经过清理阶段,如果Bean
实现了DisposableBean
这个接口,会调用其实现的destroy()
方法
destroy-method
:最后,如果这个Bean
的Spring
配置中配置了destroy-method
属性,会自动调用其配置的销毁方法。
Spring如何解决循环依赖的问题?
什么是循环依赖?
类与类之间的关系形成了一个闭环,比如下图中,A
依赖了B
,B
又依赖于A
:
是不是有点儿像操作系统中的死锁问题
?
案例引入
public class ClassA {
private ClassB classB;
public ClassB getClassB() {
return classB;
}
public void setClassB(ClassB classB) {
this.classB = classB;
}
}
public class ClassB {
private ClassA classA;
public ClassA getClassA() {
return classA;
}
public void setClassA(ClassA classA) {
this.classA = classA;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="classA" class="ioc.cd.ClassA">
<property name="classB" ref="classB"></property>
</bean>
<bean id="classB" class="ioc.cd.ClassB">
<property name="classA" ref="classA"></property>
</bean>
</beans>
@Test
public void test() throws Exception {
// 创建IoC容器,并进行初始化
String resource = "spring/spring-ioc-circular-dependency.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(resource);
// 获取ClassA的实例(此时会发生循环依赖)
ClassA classA = (ClassA) context.getBean(ClassA.class);
}
产生循环依赖的三种情况
- 通过构造方法进行依赖注入
- 通过
setter
方法进行依赖注入(多例模式下) - 通过
setter
方法进行依赖注入(单例模式下)
第三种已经被解决,第一种情况下,在new
的时候会被堵塞,创建A
的时候依赖于B
,创建B
又依赖于A
,导致new不出来;第二种情况下,每次调用getBean()
时,都会产生一个新的Bean
,这样就会产生N
个Bean
,最终抛出内存溢出异常。
循环依赖的解决
在Spring
中,有三大缓存:一级缓存,二级缓存,三级缓存。
- 一级缓存:存储单例模式下创建的
Bean
(已经创建完成的),对外使用 - 二级缓存:存储单例模式下创建的
Bean
(正在创建中的),对内使用 - 三级缓存:通过
ObjectFactory
存储单例模式下的Bean
(正在创建中的),对内使用(三级缓存使用ObjectFacotory
来存储的原因是,如果对象实现了AOP
,注入到其他Bean
的时候并不是最终的代理对象,而是原始的。这时就需要通过三级缓存的ObjectFactory
才能提前产生最终的需要代理的对象。)
Spring
主要依靠二级缓存和三级缓存来解决单例模式下setter
方法进行依赖注入时产生循环依赖的问题。
解决单例模式下setter
方法进行依赖注入时产生循环依赖的问题,是Spring
自动解决的,通过构造方法进行依赖注入时所产生的循环依赖问题需要我们人为解决,常见的解决方案就是@Lazy
注解,@Lazy
注解的作用就是延迟加载。比如,我们想创建对象A
,此时A
依赖于B
,但当使用@Lazy
注解之后,在创建A
时,就会基于动态代理去创建一个代理类B1
,也就是此时A
依赖于B1
,B
依赖于A
。要注意的是,在注入依赖时,类A
并没有完全的初始化完,实际上注入的是一个代理对象,只有当他首次被使用的时候才会被完全的初始化。
参考文章:Spring如何解决循环依赖
整理面经不易,觉得有帮助的小伙伴点个赞再走吧~感谢收看!