Spring 三级缓存以及21道⾼频⾯试题
Spring 的三级缓存
三级缓存的作⽤:解决循环依赖的问题
循环依赖问题:说⽩是⼀个或多个对象实例之间存在直接或间接的依赖关系,这种依赖关系构成了构成⼀个环形调⽤
代码描述:
什么是三级缓存?singletonObjects(⼀级,⽇常实际获取Bean的地⽅,⾥⾯保存的都是初始化后的Bean);earlySingletonObjects(⼆级,还没进⾏属性注⼊,由三级缓存放进来,经过三级缓存处理可能是原对象或代理对象);singletonFactories(三级,存放⼀个对象⼯⼚,和lambda表达式,⾥⾯保存的都是刚实例化的对象);
本质上这三级缓存就是三个Map :@Service public class AServiceImpl implements AService { @Autowired private BService bService ; ...}@Service public class BServiceImpl implements BService { @Autowired private AService aService ; ...}
1
2
3
4
5
6
7
8
9
10
11
12
我们知道了什么是循环依赖,什么是三级缓存,那么我们的Spring是如何通过三级缓存去解决这个问题的呢?
如下图流程:⾸先先知道两个概念:实例化:我们只是将对象创建出来,并没有进⾏赋值操作
初始化:对我们创建出来的对象也就是实例对象进⾏属性注⼊后的对象
接下来我们对这个图进⾏解释说明:
1、我们创建Aservice对象,将其对应的lambda表达式放⼊三级缓存,lambda表达式的作⽤是,判断我们这个实例化对象是否有AOP曹操作,如果有就执⾏AOP,返回代理后的对象到⼆级缓存,如果没有,则直接将原对象放⼊⼆级缓存 ;
2、然后我们的对Aservice这个实例化对象进⾏属性注⼊,填充Bservice对象,⾸先是去⼀级缓存中去,如果没有就去创建Bservice对象
3、初始步骤同样是将Bservice对应的lambda表达式放⼊我们的三级缓存当中,发现B同样需要注⼊AService属性
4、就会去⼀级缓存和⼆级缓存中Aservice,发现不存在,那么就去三级缓存当中查,
5、到了,那么此时执⾏三级缓存中Aservice对应的lambda表达式,同步骤1⼀样,将返回的对象放⼊⼆级缓存当中
6、此时,我们的Bservice中有了Aservice但是,Aservice中的Bservice属性尚未注⼊,对其进⾏属性注⼊
spring ioc注解7、执⾏三级缓存中Bservice对应的lambda表达式,得到Bservice对象,并将Bservice对象由⼆级缓存移⼊到⼀级缓存
8、此时Bservice结束
9、继续对Aservice进⾏属性注⼊,将⼀级缓存中的Bservice填充到Aservice,接下来就是初始化Aservice
10、Aservice初始化完毕,将Aservice移⼊到⼀级缓存
11、此时Aservice结束 /** 1级缓存 Cache of singleton objects: bean name to bean instance. */ private final Map <String , Object > singletonObjects = new ConcurrentHashMap <>(256); /** 2级缓存 Cache of early singleton objects: bean name to bean instance. */ private final Map <String , Object > earlySingletonObjects = new HashMap <>(16); /** 3级缓存 Cache of singleton factories: bean name to ObjectFactory. */ private final Map <String , ObjectFactory <?>> singletonFactories = new HashMap <>(16);
1
2
3
4
5
6
7
8
12、循环依赖注⼊的问题就这样解决了!
Spring 相关⾯试题
如下是Spring的⾼频⾯试题,需要掌握!
1、说⼀下Spring 中IOC 的构建流程(初始化过程) ?1、通过BeanFactory 或者 ApplicationContex接⼝,以及其实现类,读取我们的l,创建IOC容器
ClassPathXmlApplicationContext 创建容器对象时,构造⽅法做了如下两件事:① 调⽤⽗容器的构造⽅法为容器先设置好 Bean 资源加载器。② 调⽤⽗类的 setConfigLocations() ⽅法设置 Bean 配置信息的定位路径。
③ 调⽤⽗类 AbstractApplicationContext 的 refresh() ⽅法启动整个 IOC 容器对 Bean 的载⼊,在创建 IOC 容器前如果已有容器存在,需要把已有的容器销毁,保证在 refresh() ⽅法后使⽤的是新创建的 IOC 容器。2、容器创建完成后,通过 loadBeanDefinitions() ⽅法加载 Bean 配置资源,该⽅法在加载资源时,⾸先解析配置⽂件路径,读取配置⽂件的内容,然后通过 XML 解析器将 Bean 的配置信息转换成⽂档对象,之后按照 Spring Bean 的定义规则将⽂档对象解析为
BeanDefinition 对象。
3、然后将beanName做为Key,BeanDefiniton对象作为Value保存在我们的BeanDefinitonMap中,然后遍历Map集合
4、最后,实例化所有的 Bean 实例(⾮懒加载):包括实例的创建,实例的属性填充,实例的初始化。
2、说⼀下Bean 加载
3、说⼀下SpringBean 的⽣命周期
简述:从实例创建到对象销毁
1. Bean 的实例化阶段:创建⼀个 Bean 对象。
2. Bean 实例的属性填充阶段:为 Bean 实例的属性赋值。
3. Bean 实例的初始化阶段:对 Bean 实例进⾏初始化。
4. Bean 实例的正常使⽤阶段。
5. Bean 实例的销毁阶段:容器关闭后,将 Bean 实例销毁。
详细流程:
当我们IOC容器创建完成之后,会读取我们l中的bean标签,然后将其封装成⼀个BeanDefiniton对象,然后将beanName做为Key,BeanDefiniton对象作为Value保存在我们的BeanDefinitonMap中,然后遍历我们的Map集合,此时对每Bean进⾏实例化,接着就是对Bean进⾏属性注⼊,此时在我们要调⽤Bean的init⽅法的时候,会在执⾏之前调⽤后置处理器的⼀个befor⽅法:
postProcessBeforeInitialization(),接下来就是执⾏Init⽅法,完成Bean的初始化操作,接着会再次调⽤后置处理器的⼀个after⽅法 :postProcessAfterInitialization(),当after⽅法执⾏完毕后,我们就得到了⼀个可⽤的Bean对象在IOC容器当中,当我们容器关闭的时候,就会调⽤我们Bean的Destory⽅法,将我们的Bean进⾏销毁处理!
4、什么是Spring 的循环依赖问题?ApplicationContext context = new ClassPathXmlApplicationContext ("l");
1
循环依赖:说⽩是⼀个或多个对象实例之间存在直接或间接的依赖关系,这种依赖关系构成了构成⼀个环形调⽤,如图:
注意:⽬前Spring只⽀持单例(Singleton)类型的属性循环依赖
5、说⼀下Spring 的三级缓存所谓的三级缓存其实就是三个Map…⾸先明确⼀定,我对这⾥的三级缓存定义是这样的:singletonObjects(⼀级,⽇常实际获取Bean的地⽅);earlySingletonObjects(⼆级,还没进⾏属性注⼊,由三级缓存放进来);
singletonFactories(三级,Value是⼀个对象⼯⼚);
6、Spring 是如何解决循环依赖的?@Service public class AServiceImpl implements AService { @Autowired private BService bService ; ...}@Service public class BServiceImpl implements BService { @Autowired private AService aService ; ...}
1
2
3
4
5
6
7
8
9
10
11
12
答:Spring通过三级缓存解决循环依赖问题!
我们通过A实例依赖B,B实例依赖A的例⼦来分析具体流程:
1、A对象实例化之后,属性注⼊之前,其实会把A对象放⼊三级缓存中,key是BeanName,Value是ObjectFactory
2、等到A对象属性注⼊时,发现依赖B,⼜去实例化B时
3、B属性注⼊需要去获取A对象,这⾥就是从三级缓存⾥拿出ObjectFactory,ObjectFactory得到对应的Bean(就是对象A)
4、把三级缓存的A记录给⼲掉,然后放到⼆级缓存中
5、显然,⼆级缓存存储的key是BeanName,value就是Bean(这⾥的Bean还没做完属性注⼊相关的⼯作)
6、等到完全初始化之后,就会把⼆级缓存给remove掉,塞到⼀级缓存中
7、我们⾃⼰去getBean的时候,实际上拿到的是⼀级缓存的
⼤致的过程就是这样
7、Spring为什么是三级缓存?
如果没有AOP的话确实可以两级缓存就可以解决循环依赖的问题,如果加上AOP,两级缓存是⽆法解决的,不可能每次执⾏Object()⽅法都给我产⽣⼀个新的代理对象,所以还要借助另外⼀个缓存来保存产⽣的代理对象
8、BeanFactory 和 FactoryBean 的区别
BeanFactory:Spring 容器最核⼼也是最基础的接⼝,本质是个⼯⼚类,⽤于管理 bean 的⼯⼚,最核⼼的功能是加载 bean FactoryBean:该接⼝以 bean 样式定义,但是它不是⼀种普通的 bean,它是个⼯⼚ bean,实现该接⼝的类可以⾃⼰定义要创建的bean,只需要实现它的 getObject ⽅法即可
9、BeanFactory 和 ApplicationContext 的区别
Spring提供的2个创建IOC容器的接⼝,之间的的区别如下:
1、BeanFactory : IOC容器的基本实现,是Spring内部使⽤的接⼝,不提供给开发⼈员使⽤ ;
加载配置⽂件的时候,不会创建对象,⽽是当我们的使⽤的时候才回去创建对象!
2、ApplicationContext : BeanFactory的⼦接⼝提供更多更强⼤的功能,⼀般由开发⼈员进⾏使⽤ ; 【推荐】
加载配置⽂件的时候,会把配置⽂件中的对象进⾏创建!
10、说⼀下SpinrgBean的作⽤范围?
通过 scope 属性指定 Bean 的作⽤范围,包括:
singleton:单例模式,表⽰Spring的IOC容器当中只能存在⼀个Bean实例,默认的。
prototype:多实例模式,表⽰每次从IOC容器当中取⼀个Bean实例的时候,都是⼀个新的Bean 。
request:每次创建对象,都放在我们的Request域当中(很少⽤)在⼀次请求范围内,创建⼀个实例。
session:每次创建对象,都放在我们的session域当中(很少⽤)在⼀个会话范围内,创建⼀个实例。
globle-session:在servletContext范围内,创建⼀个实例
后⾯三个范围需要在web环境才起作⽤
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论