面试必问(框架+架构)

一,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:只要一读取配置文件,默认情况下就会创建对象;

BeanFactory :什么使用什么时候创建对象。
ApplicationContext有常用三个实现类:
ClassPathXmlApplicationContext:从类的根路径下加载配置文件 一般使用这种;
FileSystemXmlApplicationContext: 从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置;
AnnotationConfigApplicationContext: 使用注解时使用,此类创建spring容器,读取注解
如何设置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

< bean id = "accountService" class = "com.xxx.factory.StaticFactory" factory-method = "createAccountService" ></ bean >
id和class同上述, factory-method 属性:指定生产对象的静态方法。
3,实例工厂: InstanceFactory
< bean id = "instancFactory" class = "com.xxx.factory.InstanceFactory" ></ bean >
< bean id = "accountService"
factory-bean = "instancFactory"
factory-method = "createAccountService" ></ bean >
factory-bean 属性:用于指定实例工厂 bean id
factory-method 属性:用于指定实例工厂中创建对象的方法。
注意:后两种基本少见,源码级别会见到,实际开发中第一种最常见;

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>

注意标签:

constructor-arg: 构造函数注入标签

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>

注意标签:

property: 使用 set 方法的方式;这个标签也是我们实现开发中最常见的;

注解形式:

创建对象的相关注解:

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中)

@ComponentScan

指定创建容器时要扫描的包,经常和@Configuration搭配使用;

@Bean

作用在方法上的注解,指使用此方法创建对象,交给spring;

@Import
指在当前类中引入其它配置类,常见与源码中,如springboot自动装配底层;

AOP:Aspect Oriented Programming,面向切面编程:

概念:

将程序重复代码抽离,再需要执行时使用动态代理技术在不改变源码的基础,对我们已有方法进行增强;

动态代理技术:

允许在运行时创建一个代理对象,该代理对象可以代替原始对象进行方法调用。

通常用于在不修改原始对象的情况下增强其行为或实现横切关注点(如日志记录、性能监测等)。

在Java中,动态代理是通过Java反射机制来实现的。Java提供了两种方式来创建动态代理:
基于接口的动态代理和基于类的动态代理。

基于接口的动态代理
是通过实现InvocationHandler接口来创建代理对象,该接口定义了一个invoke方法,当代理对象的方法被调用时,invoke方法会被触发,从而可以在该方法中实现自定义的逻辑。

基于类的动态代理
是通过继承Proxy类来创建代理对象,代理类可以通过继承InvocationHandler接口或者通过传入InvocationHandler对象来实现代理逻辑。

除了上述Java提供还有第三方CGLib提供Enhancer创建代理对象,它是基于子类的动态代理;

动态代理技术在很多领域都有应用,例如AOP(面向切面编程)框架和远程方法调用(RMI)等。它可以减少代码的重复性,提高代码的复用性和可维护性。

特点:

字节码随用随创建,随用随加载;

xml形式:

< aop:config >:声明aop配置;
< aop:aspect id = "xx" ref = "xx" >:配置切面
id:切面标识,ref:引用通知类

通知:被增强方法再增强之前或者之后要做的事情;

连接点:被增强的方法;

切面:切入点和通知的结合;

切入点:被增强方法要增强做的定义;

< aop:pointcut expression =" execution ( ) id='xxx'>配置切入点
expression:定义切入点表达式
通知相关的xml配置:
aop:before:前置通知,指增强的方法在切入点之前执行;
aop:after-returning:后置通知,指增强的方法在切入点之后执行;
aop:after-throwing:异常通知,切入点方法异常之后执行;
aop:after:最终通知,无论是否有异常,都会在最后执行;
aop:around:环绕通知,包围切入点执行;

注解形式:

@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框架中带事务的方法之间互相调用事务该怎么处理的机制;

七种传播机制:
REQUIRED: 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。(spring中默认配置,默认值)。
SUPPORTS: 支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
MANDATORY :使用当前的事务,如果当前没有事务,就抛出异常
REQUERS_NEW: 新建事务,如果当前在事务中,把当前事务挂起。
NOT_SUPPORTED: 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
NEVER: 以非事务方式运行,如果当前存在事务,抛出异常
NESTED: 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行 REQUIRED 类似的操作。
相关注解:
@Transactional( readOnly= true,rollbackFor = Exception.class, propagation = Propagation.REQUIRED) 
readOnly:是否只读;
rollbackFor:回滚异常;
propagation:机制设置;
此注解可加位置:
接口:表示该接口的所有的实现类都有事务支持;
类:类中所有方法都有事务支持;
方法:此方法有事务支持;
最常使用位置方法上。
事务传播机制使用时机:spring中带事务方法互相调用时使用;
具体代码及编程式事务和声明式事务可以参考此位博主分享: Java 事务的传播性(Transactional)-CSDN博客

失效场景:
  • 访问权限问题(只有 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博客

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值