三级缓存的作用:解决循环依赖的问题
循环依赖问题:说白是一个或多个对象实例之间存在直接或间接的依赖关系,这种依赖关系构成了构成一个环形调用
代码描述:
@Service
public class AServiceImpl implements AService {
@Autowired
private BService bService;
...
}
@Service
public class BServiceImpl implements BService {
@Autowired
private AService aService;
...
}
什么是三级缓存?
本质上这三级缓存就是三个Map :
/** 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);
我们知道了什么是循环依赖,什么是三级缓存,那么我们的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属性尚未注入,对其进行属性注入
7、执行三级缓存中Bservice对应的lambda表达式,得到Bservice对象,并将Bservice对象由二级缓存移入到一级缓存
8、此时Bservice结束
9、继续对Aservice进行属性注入,将一级缓存中的Bservice填充到Aservice,接下来就是初始化Aservice
10、Aservice初始化完毕,将Aservice移入到一级缓存
11、此时Aservice结束
12、循环依赖注入的问题就这样解决了!
如下是Spring的高频面试题,需要掌握!
1、通过BeanFactory 或者 ApplicationContex接口,以及其实现类,读取我们的beans.xml,创建IOC容器
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
ClassPathXmlApplicationContext
创建容器对象时,构造方法做了如下两件事:
2、容器创建完成后,通过 loadBeanDefinitions()
方法加载 Bean 配置资源,该方法在加载资源时,首先解析配置文件路径,读取配置文件的内容,然后通过 XML 解析器将 Bean 的配置信息转换成文档对象,之后按照 Spring Bean 的定义规则将文档对象解析为 BeanDefinition 对象。
3、然后将beanName做为Key,BeanDefiniton对象作为Value保存在我们的BeanDefinitonMap中,然后遍历Map集合
4、最后,实例化所有的 Bean 实例(非懒加载):包括实例的创建,实例的属性填充,实例的初始化。
参考文章:https://blog.csdn.net/weixin_43591980/article/details/117022413
简述:从实例创建到对象销毁
当我们IOC容器创建完成之后,会读取我们bean.xml中的bean标签,然后将其封装成一个BeanDefiniton对象,然后将beanName做为Key,BeanDefiniton对象作为Value保存在我们的BeanDefinitonMap中,然后遍历我们的Map集合,此时对每Bean进行实例化,接着就是对Bean进行属性注入,此时在我们要调用Bean的init方法的时候,会在执行之前调用后置处理器的一个befor方法:postProcessBeforeInitialization(),接下来就是执行Init方法,完成Bean的初始化操作,接着会再次调用后置处理器的一个after方法 :postProcessAfterInitialization(),当after方法执行完毕后,我们就得到了一个可用的Bean对象在IOC容器当中,当我们容器关闭的时候,就会调用我们Bean的Destory方法,将我们的Bean进行销毁处理!
循环依赖:说白是一个或多个对象实例之间存在直接或间接的依赖关系,这种依赖关系构成了构成一个环形调用,如图:
@Service
public class AServiceImpl implements AService {
@Autowired
private BService bService;
...
}
@Service
public class BServiceImpl implements BService {
@Autowired
private AService aService;
...
}
注意:目前Spring只支持单例(Singleton)类型的属性循环依赖
所谓的三级缓存其实就是三个Map…首先明确一定,我对这里的三级缓存定义是这样的:
参考文章:https://www.cnblogs.com/semi-sub/p/13548479.html
答: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的时候,实际上拿到的是一级缓存的
大致的过程就是这样
参考文章:https://www.cnblogs.com/grey-wolf/p/13034371.html
三级缓存的职能:https://blog.csdn.net/m0_43448868/article/details/113578628
如果没有AOP的话确实可以两级缓存就可以解决循环依赖的问题,如果加上AOP,两级缓存是无法解决的,不可能每次执行singleFactory.getObject()方法都给我产生一个新的代理对象,所以还要借助另外一个缓存来保存产生的代理对象
Spring提供的2个创建IOC容器的接口,之间的的区别如下:
1、BeanFactory : IOC容器的基本实现,是Spring内部使用的接口,不提供给开发人员使用 ;
2、ApplicationContext : BeanFactory的子接口提供更多更强大的功能,一般由开发人员进行使用 ; 【推荐】
通过 scope 属性指定 Bean 的作用范围,包括:
后面三个范围需要在web环境才起作用
关于singleton和prototype还存在一个区别就是:
当我们的scop=singleton
的时候我们的对象是会在ApplicaitionContex加载xml文件的时候创建的;
当我们的scop=prototype
的时候我们的对象不是在加载xml的时候创建的,而是在调用getBean方法的时候创建的!
我们通过**@Order注解来设置增强类优先级:这个值越小优先级越高**!
@Order(3)
public class UserProxy {}
@Order(1)
public class PersonProxy {}
Spring 中的 AOP 目前支持 JDK 动态代理和 Cglib 代理。
通常来说:如果被代理对象实现了接口,则使用 JDK 动态代理,否则使用 Cglib 代理。另外,也可以通过指定 proxyTargetClass=true 来
实现强制走 Cglib 代理。
根本原因是通过 JDK 动态代理生成的类已经继承了 Proxy 类,所以无法再使用继承的方式去对类实现代理
Spring 事务的底层实现主要使用的技术:AOP(动态代理) + ThreadLocal + try/catch。
动态代理:基本所有要进行逻辑增强的地方都会用到动态代理,AOP 底层也是通过动态代理实现。
ThreadLocal:主要用于线程间的资源隔离,以此实现不同线程可以使用不同的数据源、隔离级别等等。
try/catch:最终是执行 commit 还是 rollback,是根据业务逻辑处理是否抛出异常来决定。
Spring 事务的核心逻辑伪代码如下:
public void invokeWithinTransaction() {
// 1.事务资源准备
try {
// 2.业务逻辑处理,也就是调用被代理的方法
} catch (Exception e) {
// 3.出现异常,进行回滚并将异常抛出
} finally {
// 现场还原:还原旧的事务信息
}
// 4.正常执行,进行事务的提交
// 返回业务逻辑处理结果
}
1、REQUIRED:Spring 默认的事务传播级别,如果上下文中已经存在事务,那么就加入到事务中执行,如果当前上下文中不存在事务,则新建事务执行。
2)REQUIRES_NEW:每次都会新建一个事务,如果上下文中有事务,则将上下文的事务挂起,当新建事务执行完成以后,上下文事务再恢复执行。
3)SUPPORTS:如果上下文存在事务,则加入到事务执行,如果没有事务,则使用非事务的方式执行。
4)MANDATORY:上下文中必须要存在事务,否则就会抛出异常。
5)NOT_SUPPORTED :如果上下文中存在事务,则挂起事务,执行当前逻辑,结束后恢复上下文的事务。
6)NEVER:上下文中不能存在事务,否则就会抛出异常。
7)NESTED:嵌套事务。如果上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务。
Spring 的事务隔离级别底层其实是基于数据库的,Spring 并没有自己的一套隔离级别。
代理模式、工厂模式、单例模式、观察者模式、适配器模式
通过搭配@Qualifire指定bean的名称,来完成byName的装配方式
@Component
public class Test {
@Autowired
@Qualifier("userService")
private UserService userService;
}
方式1、使用 @DependsOn、depends-on
方式2、让后加载的类依赖先加载的类
@Component
public class A {
@Autowire
private B b;
}
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/m0_46571920/article/details/122639434
内容来源于网络,如有侵权,请联系作者删除!