一,SpringMVC执行流程(或者原理)
1,用户发送请求到前端核心控制器DispatcherServlet,即中央控制器;
2,由DispatcherServlet控制器通过配置文件xxxServlet.xml文件寻找到一个或多个处理器映射器HandlerMapping,通过HandlerMapping找到具体处理器,生成处理器对象返回给DispatcherServlet;
3,DispatcherServlet调用HandlerAdapter处理器适配器;
4,HandlerAdapter经过适配调用具体的后端处理器即Controller,执行请求;
5,controller执行完毕后返回ModelAndView;
6,HandlerAdapter将controller返回的ModelAndView返回给DispatcherServlet;
7,DispatcherServlet将ModelAndView传递给ViewReslove视图解析器进行解析;
8,ViewReslove解析后返回具体的视图View给DispatcherServlet;
9,DispatcherServlet根据返回的View进行视图渲染,即将模型数据填充至视图中;
10,DispatcherServlet响应客户。
二,Spring
IOC(Inversion of Control):控制反转
概念:
将所需对象交给spring框架处理,包括创建即调用,达到使用工厂模式进行解耦的目的;创建使用的是反射机制,调用使用的是DI(Dependency Injection),依赖注入;
IOC底层是Map容器,即key:value形式;
具体表现:
xml形式:
<bean id='xxx' class='com.xx.xxx.impl.xxxxImpl' />
bean标签:用于配置spring创建对象,且存入IOC容器;
id:对象的唯一标识;
class:对象的全限定类名;
利用反射通过标识找到对应的类;
具体代码:
public class Client {
/**
* 使用 main 方法获取容器测试执行
*/
public static void main(String[] args) {
//1.使用 ApplicationContext 接口,就是在获取 spring 容器
ApplicationContext ac = new ClassPathXmlApplicationContext("sringbean.xml");
//2.根据 bean 的 id 获取对象
IAccountService accountService = (IAccountService) ac.getBean("accountService");
System.out.println(aService);
IAccountDao accountDao = (IAccountDao) ac.getBean("accountDao");
System.out.println(accountDao );
}
}
ApplicationContext 和 BeanFactory:
BeanFactory是spring容器的顶层接口,ApplicationContext是它的子接口;
两者区别:
ApplicationContext:只要一读取配置文件,默认情况下就会创建对象;
ApplicationContext有常用三个实现类:
如何设置spring中对象的作用范围:
bean标签中配置scope指定,如<bean id='xx' class='xxx,xxx,xxx' scope=prototype>
scope指定范围属性:
* singleton :默认值,单例的.
* prototype :多例的.
* request :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中.
* session :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中.
* global session :WEB 项目中,应用在 Portlet(门户) 环境.如果没有 Portlet 环境那么
globalSession 相当于 session.
生命周期:
单例:scope='singleton',spring中默认
作用范围:整个应用;
生命周期:
出生:应用加载,创建容器时,对象就被创建;
活着:伴随容器,容器销毁对象死亡,容器存在,对象一直存活;
死亡:应用卸载,容器销毁,对象销毁;
多例: scope='prototype'
作用范围:每次访问对象,都会重新创建对象实例;
生命周期:
出生:使用对象时,创建新对象实例;
活着:对象在使用中,就一直活着;
死亡:对象长时间不用,会被Java垃圾回收机制回收;
实例化bean的三种方式:
1,无参构造,即我们上述写法,也是spring中默认使用方式;
2,静态工厂:StaticFactory
DI:Dependency Injection依赖注入
基于xml配置,这里指给指定对象属性注入实际值,多见于xml配置中,实际情况最常见的是使用注解;
构造函数注入:
<bean id="accountService" class="com.xxx.service.impl.AccountServiceImpl">
<constructor-arg name="name" value="张三"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>
<bean id="now" class="java.util.Date"></bean>
注意标签:
set方法注入,
<bean id="accountService" class="com.xxx.service.impl.AccountServiceImpl">
<property name="name" value="test"></property>
<property name="age" value="21"></property>
<property name="birthday" ref="now"></property>
</bean>
<bean id="now" class="java.util.Date"></bean>
注意标签:
注解形式:
创建对象的相关注解:
1,@Component
对应bean实例化标签:<bean id='xx' class=''>
2,@Controller @Service @Repository
@Compent衍生注解,分工明确:也是我们开发中最常见的。
@Controller:用于表现层;直接对接页面传输;
@Service:用于服务层,对接表现层和数据访问层,位于两者中间;
@Repository:用于数据访问层,上层是服务层,下面直接对接数据库;
其它相关注解(DI相关):
set方法注入标签:<property name='' ref=''>
@Autowired
按照类型自动注入,如果类型相同则使用下一个注解;
@Qualifier
在按照类型注入的基础上,再按照对象标识注入,即对象名称,跟@Autowired一起使用;
@Resource
直接按照bean的id注入,不过它不是属于spring的标签,属于Javax库;
@Value
注入基本数据类型和String类型的数据,常见于将配置文件中实际数据注入到bean对应的属性中;
@Scope
用于改变作用范围的,属性单例singleton,多例prototype,其他同xml;
@Configuration
用于指定当前类是一个配置类(spring中)
指定创建容器时要扫描的包,经常和@Configuration搭配使用;
@Bean
作用在方法上的注解,指使用此方法创建对象,交给spring;
AOP:Aspect Oriented Programming,面向切面编程:
概念:
将程序重复代码抽离,再需要执行时使用动态代理技术在不改变源码的基础,对我们已有方法进行增强;
动态代理技术:
允许在运行时创建一个代理对象,该代理对象可以代替原始对象进行方法调用。
通常用于在不修改原始对象的情况下增强其行为或实现横切关注点(如日志记录、性能监测等)。
在Java中,动态代理是通过Java反射机制来实现的。Java提供了两种方式来创建动态代理:
基于接口的动态代理和基于类的动态代理。
基于接口的动态代理
是通过实现InvocationHandler接口来创建代理对象,该接口定义了一个invoke方法,当代理对象的方法被调用时,invoke方法会被触发,从而可以在该方法中实现自定义的逻辑。
基于类的动态代理
是通过继承Proxy类来创建代理对象,代理类可以通过继承InvocationHandler接口或者通过传入InvocationHandler对象来实现代理逻辑。
除了上述Java提供还有第三方CGLib提供Enhancer创建代理对象,它是基于子类的动态代理;
动态代理技术在很多领域都有应用,例如AOP(面向切面编程)框架和远程方法调用(RMI)等。它可以减少代码的重复性,提高代码的复用性和可维护性。
特点:
字节码随用随创建,随用随加载;
xml形式:
通知:被增强方法再增强之前或者之后要做的事情;
连接点:被增强的方法;
切面:切入点和通知的结合;
切入点:被增强方法要增强做的定义;
注解形式:
@Aspect:
作用在类上,指明当前类是切面类,即增强方法所在类;
@Before
作用在方法上,前置通知
@AfterReturning
作用在方法上,后置通知
@After
作用在方法上,最终通知
@Around
作用在方法上,环绕通知
@Pintcut
作用在方法上,指定当前方法为切入点;
Spring中的事务
概念:
事务是一组操作的逻辑单元,要么全部操作成功提交,只要一处操作异常即全部失败,则回滚回未操作前的状态;
四大特性:(关系型数据库中)
原子性:
指整个事务是不可以分割的工作单元,即要么所有操作成功提交确认,要么都回滚被撤销,不能只执行其中一部分操作,必须是全部或全不执行;
一致性:
事务执行后,必须使数据处于一致的状态,即满足事务所定义的业务规则,不会因为某个部分的失败而破坏数据的完整性和一致性。例如典型的转账示例,一要保证两人账户总额不变(数据完整性),二要保证两人账户操作完成后金额和预期一致(一致性)。
隔离性:
同一时间内,多个事务并发执行,且互不影响。(实际情况是如果多个事务同时对同一数据进行操作,则会出现多种问题,因此设置了隔离级别;)
持久性:
事务完成后,必须保证所有数据更新持久化到磁盘上,即便发生故障,也不会导致数据丢失;
隔离级别:
存在的意义:
在实际开发中DML(增删改)是最常见的操作,伴随着事务必不可少,数据量上来之后,会出现多个事务同时操作同一批数据的情况,这时就会出现下述几种问题:
事务操作问题:
脏读:一个事务读取到另一个事务没有提交的数据;
例子:典型转账示例,前提隔离级别是读未提交,开启事务,张三转账李四50w,张三这边事务没提交commit,然后让李四查询账户,李四查询账户时是读取到的张三未提交事务的操作状态即临时数据,李四账户多了50W,这时李四确认收款且打了借条,然后张三收到李四的确认之后,执行了回滚rollback操作,结果是李四的再去查余额时和之前的一样,借的50W莫名消失了,还打了欠条。
脏读的产生伴随是不可重复读,因为李四去查账户这个操作是同一个事务中,但两次读取的数据是不一样的;
不可重复读(虚读):同一个事务中,两次读取的数据不一致;
幻读:一个事务操作表中数据(DML操作),另一个事务添加一条数据,则前一个事务查询不到自己的修改;
为了解决上述问题,设置了隔离级别,有以下几种:
isolation_default:
默认级别,归属其它级别;
isolation_read_uncommitted:
可以读取未提交的数据,即读未提交;
isolation_read_committed:
只能读取已提交的数据,解决脏读问题(Orcle默认级别)
isolation_repeatable_read:
是否读取其它事务提交修改后的数据,解决不可重复读问题(MySQL默认级别)
(这里MySQL是自动提交事务的,如果是手动,则得开启事务再提交start transaction,commit;如果没有开启自动提交,则修改后的数据不会持久化到磁盘中,只是临时数据,下次再打开数据库,数据还是未修改前的状态);
isolation_serializable:串行化
是否读取其它事务提交添加后的数据,解决幻读问题;
本质是给事务操作的当前表加锁;当隔离级别设置成serializable之后,只有当前表所有的事务动作完成之后释放锁,其它事务才能操作此表,所以在所有隔离级别当中,此隔离级别的安全系数是最高的;
注意:
在现实开发中不是隔离级别越高越好,相对应的隔离级别越高,效率越低,加锁等操作是重量级的;只有根据当前业务情况进行合适的选择才是最优方案,一般情况不会改变数据库默认的隔离级别;
传播机制:
事务的传播机制或者传播行为是spring框架特有的事务特性,基于SpringAOP实现的,数据库事务本身不具备这些特性,目的和本身事务的目的是一致的,为了保证数据库数据的一致性;
概念:
事务传播就是解决在spring框架中带事务的方法之间互相调用事务该怎么处理的机制;
七种传播机制:
相关注解:
失效场景:
- 访问权限问题(只有 public 方法会生效)。
- 方法用 final 修饰,不会生效。
- 方法没有交给spring框架管理。
- 数据库存储引擎不支持事务。
- 错误的异常处理方式,如try.catch中catch中的简单处理,不抛异常,则事务不会回滚。
等等,具体可查看此位博主分享: JAVA 事务不生效的常见场景和修改方案_java事务失效场景-CSDN博客
Spring中循环依赖问题:
是什么?
在spring中两个或两个以上的bean相互依赖,最终形成闭环;
相关代码:
public class A {
private B b;
}
public class B {
private A a;
}
带来的危害:
程序创建bean陷入死循环,导致内存溢出,程序崩溃;
解决办法:
使用代理类延迟依赖注入,解决过程中,如果检测到(源码中会进行判断是否创建相关实例)循环依赖,那么会将半成品对象(没有进行初始化只是申请了内存空间)暂时赋值给对应对象中的属性,并在稍后赋值给完全实例化的对象;
详细解释:
概念:
spring中bean是单例模式,创建上述bean(AB)时,只能一个一个去创建,创建过程如下:先创建A,分两步走,第一步,实例化A对象,即在堆内存申请空间,生成地址值(即半成品);第二步,初始化A对象,即给A对象中的属性赋值,发现A对象中属性是B对象类型,spring框架会先在spring的IOC容器中找是否有B对象存在,发现没有后,会去创建B对象;此时来到创建过程中创建第二个对象的流程,创建B对象和A的过程一样,分两步走:第一步,实例化B对象,即在堆内存中申请空间,生成地址值(半成品);第二步,初始化B对象,即给B对象中的属性值赋值,这时发现B对象中的属性值是A对象时,那么就会先在spring中容器中找,此时发现spring的容器中没有一个完整的A对象(上述A对象实例化只是开辟了空间只有地址值而已,空的,不能拿来使用,因为没有赋值),所以spring又会去创建完整A对象,此时就形成了循环依赖;
针对这种情况,解决办法就是将临时的半成品赋值给对应属性,例如将A对象的半成品对象临时赋值给B对象的属性,因此在A对象完成实例化操作得到A对象的半成品后得将此半成品临时存储到spring的容器中,为此容器中提供了半成品临时存储的三级缓存:
详细梳理图解:
涉及的关键方法:
三级缓存:
//一级:用来存放成品bean
private final Map<String,Object> singletonObjects = new ConcurrentHashMap<256>;
//三级:存放Bean工厂对象,来生成半成品的bean,并将其放入到二级缓存中,用来解决循环依赖
private final Map<String,objectFactory<?>> singletonFactories = new HashMap<16>;
//二级:存放半成品的bean,只是实例化申请了空间生成了地址值,没有赋值
private final Map<String,Object> earlySingletonObjects = new HashMap<16>;
objectFactory<?>:底层是一个函数式接口,有且仅有一个getObject方法执行具体逻辑,参数传lambda表达式或匿名内部类;
ClassPathXmlApplicationContext():
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
super(parent);
this.setConfigLocations(configLocations);
if (refresh) {
this.refresh();
}
}
refresh()方法:
包含13种方法,是spring的处理核心逻辑;
refresh()方法中找见:
finishBeanFactoryInitialization(beanFactory);
finishBeanFactoryInitialization(beanFactory)中找见:
beanFactory.preInstantiateSingletons();
点进preInstantiateSingletons()
@Override
public void preInstantiateSingletons() throws BeansException {
。。。。。。。。。
else {
getBean(beanName);
}
。。。。。。。。。
}
getBean(beanName);
public <T> T getBean(String name, @Nullable Class<T> requiredType, @Nullable Object... args)
throws BeansException {
return doGetBean(name, requiredType, args, false);
}
doGetBean();
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
...........
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
...........
}
getSingleton(beanName)对应代码:优先查询一级缓存
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
getSingleton(beanName,lambda表达式)对应代码:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
//从一级缓存中获取
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
//从三级缓存中获取:getObject();实则就是createBean方法
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
// Has the singleton object implicitly appeared in the meantime ->
// if yes, proceed with it since the exception indicates that state.
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw ex;
}
}
catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
createBean()方法:
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
。。。。。。。。。。
try {
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isDebugEnabled()) {
logger.debug("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
catch (BeanCreationException ex) {
// A previously detected exception with proper bean creation context already...
throw ex;
}
。。。。。。。。。。
}
doCreateBean()方法:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
。。。。。。。。。。
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
。。。。。。。。。。
}
createBeanInstance()方法:实例化Bean
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
........
// No special handling: simply use no-arg constructor.
return instantiateBean(beanName, mbd);
........
}
instantiateBean():实例化Bean
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
。。。。。。。。。。
else {
beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
}
。。。。。。。。。。
}
instantiate():实例化Bean
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
.................
return BeanUtils.instantiateClass(constructorToUse);
.................
}
instantiateClass():实例化Bean
public static <T> T instantiateClass(Constructor<T> ctor, Object... args)
throws IllegalAccessException, InvocationTargetException, InstantiationException {
.............
if (kotlinConstructor == null) {
return ctor.newInstance(args);
}
..............
}
三级缓存添加数据:addSingletonFactory(beanName,()->getEarlyBeanReference(beanName,mbd,bean));
第一个参数:beanName,指哪个对象的属性中填充,即按照上述流程中的A对象;
第二个参数:lambda表达式;此参数只有在调用getObject方法时才会执行;
此方法底层封装了:
// org.springframework.beans.factory.support.DefaultSingletonBeanRegistry
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
// 加入三级缓存
this.singletonFactories.put(beanName, singletonFactory);
// 删除二级缓存
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
populateBean():属性填充方法
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
..............
applyPropertyValues(beanName, mbd, bw, pvs);
..............
}
applyPropertyValues(beanName, mbd, bw, pvs);属性填充方法
找见下面三行代码:
此时:originalValue 是RuntimeBeanReference对象
String propertyName = pv.getName();
Object originalValue = pv.getValue();
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
resolveValueIfNecessary():属性填充方法
@Nullable
public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
..............
if (value instanceof RuntimeBeanReference) {
RuntimeBeanReference ref = (RuntimeBeanReference) value;
return resolveReference(argName, ref);
}
..............
}
resolveReference():属性填充方法
@Nullable
private Object resolveReference(Object argName, RuntimeBeanReference ref) {
。。。。。。。。。。。。
else {
bean = this.beanFactory.getBean(refName);
this.beanFactory.registerDependentBean(refName, this.beanName);
}
。。。。。。。。。。。。
}
B对象完成实例化进入初始化,给B对象中的a属性赋值:
添加数据到二级缓存删除三级缓存数据:getSingleton(beanName)
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
注意:
此时是getSingleton方法和创建实例A时的getSingleton()方法不同,参数不同,里面执行逻辑也不同;
B实例化和初始化完成且返回到属性赋值方法完成A对象b属性的赋值,则往一级缓存添加数据,目的是解决最开始循环方法循环B对象时直接能从一级缓存中获取到B对象,结束循环解决循环依赖;
addSingleton():往一级缓存添加数据且删除二三级缓存数据:
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
和图解流程一致,结合阅读;
关键点:
创建bean时实例化和初始化分开,提前暴露对象(即完成实例化没有完成初始化(赋值)的对象);在spring中set方式注入就是这种处理方式;
注意事项:
spring中只能解决set方式注入且是单例(spring中单例是默认模式)的循环依赖,不能解决构造函数注入的循环依赖;
原因:
创建bean时分两步走,即实例化bean和初始化bean,实例化bean就是申请内存空间,还没有将bean的属性赋值;初始化bean就是将bean的属性赋值;
bean属性赋值在spring中最常见的方式就是上述提到的set和构造函数注入,但是由于使用构造函数注入属性值时,并没有将创建bean和属性赋值分开,注入时因为构造函数对象和相关属性绑定在一起,所以没有办法解决循环依赖问题;
小结:
使用三级缓存解决循环依赖的核心:
bean对象的创建将实例化和初始化分开;使用三级循环提前暴露对象;
为什么使用缓存?
为了结束循环依赖,作用是充当容器,存放临时半成品对象结束循环创建实例;
三级缓存分别有什么作用?
一,存储作用:
三级缓存:保存lambda表达式,lambda表达式用来创建实例(createBean);对应上述流程:key:a,value:()->{} ; key:b,value:()->{}
二级缓存:保存半成品对象:对应上述流程:key:a,value:A@123 ;(此时三级中key为a移除,B实例化半成品还在应用中)
一级缓存:保存成品对象:key:a,value:A@123 key:b,value:B@123
二,核心作用:
除了上述存储数据作用之外,三级缓存的作用主要是生成代理对象(AOP),提前暴露对象,且保证单例模式,通过方法getEarlyBeanReference方法实现,这也是解决循环依赖的核心;如果所有方法不调用此方法,则使用二级缓存也可,但是只有一级缓存是不能解决循环依赖的,因为要区分对象的成品和半成品的存储;
getEarlyBeanReference方法是怎么保证bean的唯一性的?
使用匿名内部类的方式,在调用的时候使用代理对象将普通对象覆盖;
什么时候往缓存中添加数据?
往三级循环中添加:实例化bean时没有对象的bean往三级缓存中添加id值(a,b)及对应的lambda表达式;
往二级循环中添加:当实例化出bean地址值(半成品时);对应上述流程中的实例化出A半成品时,在步骤B填充属性a过程中(之前的实例化A和实例B流程并没结束还在创建过程中)
往一级循环中添加:当所有bean都是完全体时填入到一级缓存中;
也可此位大神博主分享:详解Spring循环依赖-CSDN博客