范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

深度解析Spring是如何解决循环依赖的

  问题:什么是循环依赖呢?
  就是一个或者多个 bean 之间互相引用对方,这种依赖关系最终形成一个闭环就是循环依赖。循环依赖有这以下几种情况:自己依赖自己
  两个对象互相依赖
  多个对象间接依赖
  问题:循环依赖有几种场景呢?构造器注入public class A {  	private B b; 	//通过构造方法注入 B 	public A(B b) { 		System.out.println("b" + b); 	} 	public void test(){ 		System.out.println("属性B = " + b); 	} }  public class B {  	private A a;     //通过构造方法注入 A 	public B(A a) { 		System.out.println("a" + a); 	}  	public void test(){ 		System.out.println("属性A = " + a); 	} }  xml配置  	   	   复制代码启动类public static void main(String[] args) { 	ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("circularDependencies.xml"); 	A a = (A)context.getBean("a"); 	a.test(); } 结果:  抛出 BeanCreationException 异常 复制代码单例的 setter 注入public class A {  	private B b;  	public B getBb() { 		return b; 	}  	public void setBb(B b) { 		this.b = b; 	}  	public void test(){ 		System.out.println("属性B = " + b); 	} }   public class B { 	private A a;      	public A getAa() { 		return a; 	}  	public void setAa(A a) { 		this.a = a; 	}  	public void test(){ 		System.out.println("属性A = " + a); 	} }  xml配置:      		 	 	 		 	  结果: 属性B = com.gongj.circularDependencies.dto.B@19d37183 复制代码depends-on指定依赖	 		 	 	 		 	              结果: 抛出 BeanCreationException 异常 复制代码原型的 setter 注入 		 	 	 		 	          结果: 抛出 BeanCreationException 异常 复制代码
  从上面的几个例子可以得知:
  对于构造器的循环依赖,Spring 是无法自动帮我们解决的,会抛出 BeanCurrentlyInCreationException 异常,表示循环依赖。当然可以由我们程序员解决,下面会讲到的。
  Spring帮我们解决的循环依赖是 单例的 settet注入的循环依赖。对于作用域为 prototype 的循环依赖,Spring也是不会解决的,直接抛出 BeanCurrentlyInCreationException 异常。
  使用 depends-on属性指定依赖,造成循环依赖,Spring 也不会对此进行解决,直接抛出 BeanCurrentlyInCreationException 异常 。
  那么循环依赖是个问题吗?如果不考虑 Spring,循环依赖并不是问题,因为对象之间相互依赖是很正常的事情。那在 Spring 中为什么会有循环依赖这种问题产生呢?那是因为在 Spring 中,一个对象并不是简单 new 出来的,而是会经过一系列的 Bean的生命周期,就是因为 Bean 的生命周期所以才会出现循环依赖问题。当然,在 Spring 中,出现循环依赖的场景很多,有的场景 Spring 帮我们解决了,而有的场景则需要程序员来解决,下文详细来说Spring是如何解决循环依赖的,也就是为什么允许单例的 setter循环依赖注入。Bean的生命周期
  这里不会对 Bean 的生命周期进行详细的描述,只描述一下大概的过程。想详细了解可以看看笔者前面的源码阅读。如果想真正的理解循环依赖,那对 Bean的生命周期 肯定要有了解的。
  Bean 的生命周期指的就是:在 Spring 中,Bean 是如何生成的?
  Bean 的生成步骤如下:
  可以看到,对于 Spring 中的 Bean 的生成过程,步骤很多。
  但可以发现,在 Spring 中,创建一个Bean,是在 实例化 这步得到的。推断构造方法之后根据构造方法反射得到一个对象的,当然还有其他方式得到一个对象,比如说工厂方法、Supplier 回调,不管其他方式如何创建,反正创建对象出来的这一步叫 实例化,而创建出来的这个对象我们称之为原始对象!
  得到一个原始对象后,Spring 需要给对象中的属性进行依赖注入,那么这个注入过程是怎样的?
  比如上文说的 A 类,A 类中存在一个 B 类的 b 属性,所以,当 A类生成了一个原始对象之后,就会去给 b 属性去赋值,此时就会根据 b 属性的类型和属性名(beanName)去 BeanFactory 中去获取 B 类所对应的单例 bean。如果此时 BeanFactory 中存在 B 对应的 Bean,那么直接拿来赋值给 b 属性;如果此时 BeanFactory 中不存在 B 对应的 Bean,则需要生成一个 B 对应的 Bean,然后赋值给 b 属性。
  问题就出现在第二种情况,如果此时 B 类在 BeanFactory 中还没有生成对应的 Bean,那么就需要去生成,就会经过 B 的 Bean 的生命周期。
  那么在创建 B 类 Bean 的过程中, B 类中存在一个 A 类的 a 属性,那么在创建 B 类 Bean 的过程中就需要 A 类对应的 Bean,B类就会去创建 A,所以这里就出现了循环依赖:
  A Bean 创建--> 依赖了B属性 --> 触发B Bean创建 --->B依赖了A属性--->需要A Bean(但A Bean还在创建过程中)
  从而导致 A Bean 创建不出来,B Bean 也创建不出来。
  简化一下生命周期,将重要的部分列出来:A的生命周期     1.实例化 A 对象(new A()),称之为 原始对象     2.填充 b 属性(走getBean流程) --> 从单例池中获取对应的bean -->找不到 --> 创建 B 对应的 Bean(走B的生命周期)     3.填充其他属性     4.初始化后     5.添加单例池      B的生命周期     1.实例化 B 对象(new B()),称之为 原始对象     2.填充 a 属性(走getBean流程) --> 从单例池中获取对应的bean -->找不到 --> 创建 A 对应的 Bean(走A的生命周期)     3.填充其他属性     4.初始化后     5.添加单例池     复制代码
  这就是循环依赖,但是上文说了, Spring 通过某些机制帮开发者解决了单例setter注入循环依赖的问题,这个机制就是 三级缓存。问题:什么是三级缓存?
  Spring 使用三级缓存解决循环依赖!singletonObjects:一级缓存,用于保存经历了完整生命周期的bean对象,也就是成品对象,最终给程序员使用的对象。earlySingletonObjects:二级缓存,比 singletonObjects 多了一个 early,early 早期,用于保存实例化完成的 bean 实例,Bean的生命周期还没走完就把这个 Bean 实例放入了earlySingletonObjects中,属于半成品对象。singletonFactories:三级缓存,缓存的是 ObjectFactory,表示对象工厂,用来创建某个对象的。主要是解决 Spring AOP。问题:一定要三级缓存吗?
  就上述出现的循环依赖,一定要用三级缓存才能解决吗?再增加一个 Map 不就解决了吗?一个缓存用于存放成品对象,另外一个缓存用于存放半成品对象。没有 AOP 的循环依赖
  A的生命周期     1.实例化 A 对象(new A()),称之为原始对象 --> 原始对象放入 gongJMap 中进行缓存     2.填充 b 属性(走getBean流程) --> 从单例池中获取 b 对应的bean -->找不到 --> 创建 B 对应的 Bean(走B的生命周期)     3.填充其他属性     4.初始化后     5.添加单例池      B的生命周期     1.实例化 B 对象(new B()),称之为 原始对象 --> 原始对象放入 gongJMap 中进行缓存     2.填充 a 属性(走getBean流程) --> 从单例池中获取 a 对应的bean -->找不到 --> 去 gongJMap 获取 --> 得到A原始对象     3.填充其他属性     4.初始化后     5.添加单例池    复制代码
  可以看到 二级缓存 就能解决上述的循环依赖(不进行AOP时)。那 Spring 为什么要使用三级缓存呢?其实主要是 4.初始化后这步会可能进行 AOP 操作。AOP 操作会根据你的 原始对象 产生一个 代理对象 。生命周期再次发生变化,需要进行 AOPA的生命周期     1.实例化 A 对象(new A()),称之为原始对象 --> 原始对象放入 gongJMap 中进行缓存     2.填充 b 属性(走getBean流程) --> 从单例池中获取 b 对应的bean -->找不到 --> 创建 B 对应的 Bean(走B的生命周期)     3.填充其他属性     4.初始化后(AOP)  -->产生代理对象     5.添加单例池      B的生命周期     1.实例化 B 对象(new B()),称之为 原始对象 --> 原始对象放入 gongJMap 中进行缓存     2.填充 a 属性(走getBean流程) --> 从单例池中获取 a 对应的bean -->找不到 --> 去 gongJMap 获取 --> 得到A原始对象     3.填充其他属性     4.初始化后(AOP) -->产生代理对象     5.添加单例池    复制代码
  各位可以思考一下上面这个步骤有没有问题?1.实例化 A 对象(new A()),称之为原始对象 --> 原始对象放入 gongJMap 中进行缓存 2.填充 b 属性(走getBean流程) --> 从单例池中获取 b 对应的bean -->找不到 --> 创建 B 对应的 Bean(走B的生命周期)      3.实例化 B 对象(new B()),称之为 原始对象 --> 原始对象放入 gongJMap 中进行缓存 4.填充 a 属性(走getBean流程) --> 从单例池中获取 a 对应的bean -->找不到 --> 去 gongJMap 获取 --> 得到A原始对象 5.填充其他属性 6.初始化后(AOP) -->产生代理对象 7.添加单例池(将B代理对象放到单例池) B生命周期结束     // 走 A 的生命周期  A 的 b 属性放的是 B 的代理对象 8.填充其他属性 9.初始化后(AOP)  -->产生代理对象 10.添加单例池 (将A代理对象放到单例池) 复制代码
  在第 4 步 进行填充 a 属性拿到的是 A 的原始对象。而在第 2 步 填充 b 属性,A 里的 b 属性放的是 B 的代理对象。造成两者不一致了,正常情况需如下:
  A 的 b 属性填充的是 B 的代理对象,B 的 a 属性填充的是 A 的代理对象。这才是正常的。那怎么解决呢!终于需要用到三级缓存了吗?不不不,还用不到,在这里还是可以使用二级解决的。办法:提前进行AOP处理,然后在初始化后那步进行判断,如果进行过 AOP 操作就不再进行AOP了。A的生命周期     1.实例化 A 对象(new A()),称之为原始对象 --> 提前进行AOP,产生代理对象--> 放入gongJMap 中进行缓存     2.填充 b 属性(走getBean流程) --> 从单例池中获取 b 对应的bean -->找不到 --> 创建 B 对应的 Bean(走B的生命周期)     3.填充其他属性     4.初始化后(AOP),判断是否进行过AOP了     4.5 从gongJMap获取 A 的代理对象     5.添加单例池(A代理对象)      B的生命周期     1.实例化 B 对象(new B()),称之为 原始对象 --> 提前进行AOP,产生代理对象 --> 放入gongJMap 中进行缓存     2.填充 a 属性(走getBean流程) --> 从单例池中获取 a 对应的bean --> 找不到 --> 去 gongJMap 获取 --> 得到A代理对象     3.填充其他属性     4.初始化后(AOP),判断是否进行过AOP了     4.5 从gongJMap获取 B 的代理对象     5.添加单例池(B代理对象) 复制代码
  这种方式解决了A、B对象属性不一致的问题。那这里就会出现一个新的问题,什么情况下才需要提前进行 AOP 呢?答案是 产生循环依赖的时候才需要提前进行AOP。 因为如果没有出现循环依赖,A、B两个Bean各自走自己的生命周期那是不会有影响的。A的生命周期     1.实例化 A 对象(new A()),称之为原始对象 --> 原始对象放入 gongJMap 中进行缓存     2.填充属性     3.初始化后(AOP)  --> 产生A代理对象     4.添加单例池(A代理对象)      B的生命周期     1.实例化 B 对象(new B()),称之为 原始对象 --> 原始对象放入 gongJMap 中进行缓存     2.填充属性     3.初始化后(AOP) -->产生B代理对象     4.添加单例池 (B代理对象)   复制代码
  那又会有一个问题,我要才能怎么知道你产生了循环依赖呢?还是上述例子,A 依赖 B,B 依赖 A。在创建 A 的时候能知道有循环依赖吗?那肯定不能!填充 b 属性的时候?不能!实例化B?不能。只有在 B 实例化之后进行 a 属性填充时,才能知道 A、B发生了循环依赖。
  解决:增加一个 Set 集合,用来存取正在创建的 beanName。生命周期再次发生变化,如下:A的生命周期     0.将 a 放入 Set 中     1.实例化 A 对象(new A()),称之为原始对象     2.填充 b 属性(走getBean流程) --> 从单例池中获取 b 对应的bean -->找不到 --> 创建 B 对应的 Bean(走B的生命周期)     3.填充其他属性     4.初始化后(AOP)     4.5 从gongJMap获取 A 的代理对象     5.添加单例池(A代理对象)      B的生命周期     0.将 b 放入 Set 中     1.实例化 B 对象(new B()),称之为 原始对象     2.填充 a 属性(走getBean流程) --> 从单例池中获取 a 对应的bean --> 找不到 --> 判断 a 是否正在创建中 -->正在创建中,代表出现了循环依赖 --> gongJMap寻找--> 找不到 --> 进行AOP,产生代理对象 --> 得到A代理对象 --> gongJMap 中进行缓存     3.填充其他属性     4.初始化后(AOP)     4.5 从gongJMap获取 B 的代理对象     5.添加单例池(B代理对象) 复制代码
  看到这,不知道各位有没有疑问,原始对象 我们没有将它缓存起来了。但前面又讲过,代理对象需要根据原始对象去创建。所以这时候才需要用到第三级缓存了。A的生命周期     0.将 a 放入 Set 中,标记为当前单例bean正在创建中     1.实例化 A 对象(new A()),称之为原始对象 -->放入第三级缓存     2.填充 b 属性(走getBean流程) --> 从单例池中获取 b 对应的bean --> 找不到 --> 判断 b 是否正在创建中(B没有在创建中) --> 创建 B 对应的 Bean(走B的生命周期)     3.填充其他属性     4.初始化后(AOP)     4.5 从gongJMap获取 A 的代理对象     5.添加单例池(A代理对象)      B的生命周期     0.将 b 放入 Set 中,标记为当前单例bean正在创建中     1.实例化 B 对象(new B()),称之为 原始对象 -->放入第三级缓存     2.填充 a 属性(走getBean流程) --> 从单例池中获取 a 对应的bean --> 找不到 --> 判断 a 是否正在创建中 --> 正在创建中,代表出现了循环依赖 --> 从 gongJMap 寻找--> 找不到 -->  从第三级缓存寻找 --> 得到A原始对象 --> 进行AOP,产生代理对象 --> 得到A代理对象 --> gongJMap 中进行缓存     3.填充其他属性     4.初始化后(AOP)     4.5 从gongJMap获取 B 的代理对象     5.添加单例池(B代理对象) 复制代码
  到此,Spring 为什么要使用三级缓存去解决循环依赖的思路就到这了。接下来我们就开始阅读源码,看看 Spring 设计的是否与我们推断是否一致。源码阅读
  源码阅读呢会涉及到几个方法。我们根据上述所说的Bean的生命周期来分析。第零步
  该方法位于 DefaultSingletonBeanRegistry#getSingleton(String beanName, ObjectFactory<?> singletonFactory)。
  创建 A 对象,首先从单例池拿,有直接返回。没有的话首先将 A 添加到正在创建Bean的集合中,然后进行创建 A 逻辑。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);  			// 如果不存在实例,则创建单例bean实例 			if (singletonObject == null) { 				// 忽略… 				// 把当前正在创建的beanName添加到singletonsCurrentlyInCreation中, 				// singletonsCurrentlyInCreation是一个Set 				//表示这些bean正常创建中,在没创建完时不能重复创建 				beforeSingletonCreation(beanName); 				boolean newSingleton = false; 				boolean recordSuppressedExceptions = (this.suppressedExceptions == null); 				if (recordSuppressedExceptions) { 					this.suppressedExceptions = new LinkedHashSet<>(); 				} 				try { 					// singletonFactory是外面传进来的lambda表达式,执行lambda表达式 					//就是调用 createBean() 					singletonObject = singletonFactory.getObject(); 					newSingleton = true; 				}                  				// 忽略…                  				//将创建好的单例bean添加到单例池singletonObjects中 				//和单例注册表registeredSingletons中 				//并清除二级、三级缓存 				if (newSingleton) { 					addSingleton(beanName, singletonObject); 				} 			} 			return singletonObject; 		} 	}  protected void beforeSingletonCreation(String beanName) {     // inCreationCheckExclusions中的beanName,表示如果是这些bean正在创建中,重复创建也没关系     // singletonsCurrentlyInCreation中的beanName,表示这些bean正常创建中,在没创建完时不能重复创建     if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {         throw new BeanCurrentlyInCreationException(beanName);     } } 复制代码第一步1.实例化 A 对象(new A()),称之为原始对象  ---> 放入第三级缓存 复制代码
  关注一下 2、实例化,这里会产生一个原始对象。该方法位于AbstractAutowireCapableBeanFactory。protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) 			throws BeanCreationException { 		//省略代码…  		// 2、实例化 		if (instanceWrapper == null) { 			// 创建bean实例 1、工厂方法 2、构造函数自动注入 3、简单初始化 			instanceWrapper = createBeanInstance(beanName, mbd, args); 		}  		// 包装的实例对象,也就是原始对象 		final Object bean = instanceWrapper.getWrappedInstance(); 		 		//省略代码…  		//4、 		// 如果当前bean是单例并且支持循环依赖,且当前bean正在创建, 		//就通过往singletonFactories(三级缓存)添加一个objectFactory, 		//这样后期如果有其他bean依赖该bean 可以从singletonFactories获取到bean, 解决循环依赖 		// getEarlyBeanReference()可以对返回的bean进行修改,这边目前除了可能会返回 代理对象 其他的都是直接返回bean 		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && 				isSingletonCurrentlyInCreation(beanName)); 		if (earlySingletonExposure) { 			if (logger.isTraceEnabled()) { 				logger.trace("Eagerly caching bean "" + beanName + 						"" to allow for resolving potential circular references"); 			} 			// 构造一个 ObjectFactory 添加到singletonFactories中 			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); 		}  		// Initialize the bean instance. 		Object exposedObject = bean; 		try {  			// 5、 			// 填充属性 @Autowired,将各个属性注入 			// 对 bean 进行填充 将各个属性注入 			populateBean(beanName, mbd, instanceWrapper);  			// 6、 			// 执行初始化方法 			exposedObject = initializeBean(beanName, exposedObject, mbd); 		} 		catch (Throwable ex) { 			if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) { 				throw (BeanCreationException) ex; 			} 			else { 				throw new BeanCreationException( 						mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); 			} 		} 		// 7、 		if (earlySingletonExposure) { 			// earlySingletonReference 只有在检测到有循环依赖的情况下才会不为空 			Object earlySingletonReference = getSingleton(beanName, false); 			if (earlySingletonReference != null) { 				// 如果提前暴露的对象(bean)和经过了完整的生命周期后的对象相等(exposedObject) 				// 则把缓存中的earlySingletonReference赋值给exposedObject 				// 最终会添加到singletonObjects中去 				// (初始化之后的bean等于原始的bean,说明不是proxy), 				// 				if (exposedObject == bean) { 					exposedObject = earlySingletonReference; 			} 				//检测该bean的dependon的bean是否都已经初始化好了 				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { 					String[] dependentBeans = getDependentBeans(beanName); 					Set actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); 					for (String dependentBean : dependentBeans) { 						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { 							actualDependentBeans.add(dependentBean); 						} 					}  		return exposedObject; 	} 复制代码
  我们再关注一下第四点,该代码所处的位置 Bean进行实例化之后,属性填充之前。按照我们的理解是要放入到三级缓存的。		//4、 		// 如果当前bean是单例并且支持循环依赖,且当前bean正在创建, 		//就通过往singletonFactories(三级缓存)添加一个objectFactory, 		//这样后期如果有其他bean依赖该bean 可以从singletonFactories获取到bean, 解决循环依赖 		// getEarlyBeanReference()可以对返回的bean进行修改,这边目前除了可能会返回动态代理对象 其他的都是直接返回bean 		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && 				isSingletonCurrentlyInCreation(beanName));  		if (earlySingletonExposure) { 			if (logger.isTraceEnabled()) { 				logger.trace("Eagerly caching bean "" + beanName + 						"" to allow for resolving potential circular references"); 			} 			// 构造一个 ObjectFactory 添加到singletonFactories中 			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); 		} 复制代码
  要不要往三级缓存添加对象是由 earlySingletonExposure这个变量控制的。三个值都为 true 时,earlySingletonExposure才为 truembd.isSingleton():当前 bean 是单例allowCircularReferences:是否支持循环依赖,默认是 trueisSingletonCurrentlyInCreation(beanName):当前bean是否正在创建中,创建中则返回 true,至于在哪里被放入到 Set 集合中的,可以看前面的源码阅读。具体代码在DefaultSingletonBeanRegistry类的getSingleton方法。
  ``earlySingletonExposure这个变量的值,正常情况下都是为true的。 所以addSingletonFactory`方法都会被执行,那一起来看看它干了什么?
  addSingletonFactory方法两个入参,一个是 beanName,一个是类型为 ObjectFactory的函数式接口。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); 			} 		} 	} 复制代码
  如果 一级缓存 singletonObjects,也就是单例池中不存在当前beanName的记录,则进入if代码块。首先往 三级缓存 singletonFactories中添加一个 key 为 beanName,value 为 ObjectFactory 的数据。然后从 二级缓存 earlySingletonObjects 删除 key 为beanName的数据。再然后将 beanName 放入 registeredSingletons的Set集合中。这段代码重点就是往 第三级缓存里添加了一个 key为 beanName,value 为 ObjectFactory的函数式接口,也就是一个 Lambda 表达式。我们自己推断的只放一个原始对象。那我们来看看具体的 Lambda 表达式的实现!注意,这里并没有执行 Lambda 表达式的具体实现。可以看到这段代码比较符合我们所推测的,只不过Spring是往三级缓存里存的是 类型为 ObjectFactory 的 Lambda 表达式而不是纯粹原始对象。第二步
  2、然后进行 b 属性填充 --> 从单例池中获取 b 对应的bean --> 找不到 --> 判断 b 是否正在创建中(B没有在创建中,还没开始创建呢) --> 创建 B 对应的 Bean。
  最终会调用到 getBean(beanName)方法。然后进入到 doGetBean方法。protected  T doGetBean(final String name, @Nullable final Class requiredType, 			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {  		// 对name进行转换,找到真正的beanName 		// 如果传入的name是"&gongjFactoryBean",那么beanName就是"gongjFactoryBean" 		final String beanName = transformedBeanName(name); 		Object bean;  		//根据beanName去单例池中获取Bean 		Object sharedInstance = getSingleton(beanName);  // Map<> 		if (sharedInstance != null && args == null) { 			if (logger.isTraceEnabled()) { 				if (isSingletonCurrentlyInCreation(beanName)) { 					logger.trace("Returning eagerly cached instance of singleton bean "" + beanName + 							"" that is not fully initialized yet - a consequence of a circular reference"); 				} 				else { 					logger.trace("Returning cached instance of singleton bean "" + beanName + """); 				} 			} 			// 判断sharedInstance是不是FactoryBean, 			// 如果是FactoryBean,那么真正需要拿到的是getObject方法所返回的对象 			//beanName 是spinrg进行解析后获取到的BeanName  name 我们手动传入的beanName 			//sharedInstance 根据beanName获取到的单例Bean对象 			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); 		} 		//单例池没有获取到Bean 则走创建Bean的流程 		else {} 复制代码
  上述代码在本篇中只需要关注getSingleton方法。@Override @Nullable public Object getSingleton(String beanName) {     return getSingleton(beanName, true); } 复制代码
  注意getSingleton(beanName, true);的第二个参数值为 true。@Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) {     // 从单例池中获取实例     Object singletonObject = this.singletonObjects.get(beanName);      // 如果从单例池中没有获取到实例 并且 指定的单例bean正在创建中     if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {         //锁定全局变量并进行处理         synchronized (this.singletonObjects) {             //earlySingletonObjects 二级缓存             singletonObject = this.earlySingletonObjects.get(beanName);             // 没有从二级缓存中获取到 并且 allowEarlyReference 为 true             if (singletonObject == null && allowEarlyReference) {                 // 实例化之后调用 addSingletonFactory 方法将                 // ObjectFactory 初始化存储在 singletonFactories Map中                 // singletonFactories 三级缓存                 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);                 if (singletonFactory != null) {                     // 执行lambda AOp                     singletonObject = singletonFactory.getObject();                     //记录在缓存中 earlySingletonObjects与singletonFactories互斥                     this.earlySingletonObjects.put(beanName, singletonObject);                     this.singletonFactories.remove(beanName);                 }             }         }     }     return singletonObject; } 复制代码
  从单例池获取 b Bean ,那是肯定获取不到的,所以 singletonObject值为 null。至于 b 是否正在创建中,那肯定也是 false,b 还没开始创建!所以 if代码块都不会进入,该方法直接返回 null。 返回 null 也就代表着 B 需要进行创建。进入到 B 的生命周期。第三步
  开始创建 B Bean。流程如下:0.将 b 放入 Set 中,标记为当前单例bean正在创建中 1.实例化 B 对象(new B()),称之为 原始对象 -->放入第三级缓存 2.填充 a 属性(走getBean流程) --> 从单例池中获取 a 对应的bean --> 找不到 --> 判断 a 是否正在创建中 --> 正在创建中,代表出现了循环依赖 --> 从 gongJMap 寻找--> 找不到 -->  从第三级缓存寻找 --> 得到A原始对象 --> 进行AOP,产生代理对象 --> 得到A代理对象 --> gongJMap 中进行缓存 复制代码//单例池没有获取到Bean 则走创建Bean的流程 else {     // 省略     // 根据Scope去创建bean     //创建单例模式Bean的实例对象     if (mbd.isSingleton()) {         //使用一个匿名内部类,创建Bean实例对象,并且注册给所依赖的对象         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.                 //显式地从容器销毁给定的bean                 destroySingleton(beanName);                 throw ex;             }         });         // sharedInstance可能是一个FactoryBean,如果是FactoryBean         // 那么真正需要拿到的是getObject方法所返回的对象         bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);     } } 复制代码
  关注一下 getSingleton(String beanName, ObjectFactory<?> singletonFactory)方法,该方法与 第零步 一模一样。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);  			// 如果不存在实例,则创建单例bean实例 			if (singletonObject == null) { 				// 忽略… 				// 把当前正在创建的beanName添加到singletonsCurrentlyInCreation中, 				// singletonsCurrentlyInCreation是一个Set 				//表示这些bean正常创建中,在没创建完时不能重复创建 				beforeSingletonCreation(beanName); 				boolean newSingleton = false; 				boolean recordSuppressedExceptions = (this.suppressedExceptions == null); 				if (recordSuppressedExceptions) { 					this.suppressedExceptions = new LinkedHashSet<>(); 				} 				try { 					// singletonFactory是外面传进来的lambda表达式,执行lambda表达式 					//就是调用 createBean() 					singletonObject = singletonFactory.getObject(); 					newSingleton = true; 				}                  				// 忽略…                  				//将创建好的单例bean添加到单例池singletonObjects中 				//和单例注册表registeredSingletons中 				//并清除二级、三级缓存 				if (newSingleton) { 					addSingleton(beanName, singletonObject); 				} 			} 			return singletonObject; 		} 	} 复制代码
  这里就会走 B 的生命周期了。进行实例化。// singletonFactory是外面传进来的lambda表达式,执行lambda表达式 //就是调用 createBean() singletonObject = singletonFactory.getObject(); 复制代码
  进行完 B 的 实例化之后,然后进行填充属性。发现需要填充 A 属性,再走 A 的 getBean 方法。
  既然是走 getBean 的方法,那就会进入到 第二步 所分析的 getSingleton方法。@Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) {     // 从单例池中获取实例     Object singletonObject = this.singletonObjects.get(beanName);      // 如果从单例池中没有获取到实例 并且 指定的单例bean正在创建中     if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {         //锁定全局变量并进行处理         synchronized (this.singletonObjects) {             //earlySingletonObjects 二级缓存             singletonObject = this.earlySingletonObjects.get(beanName);             // 没有从二级缓存中获取到 并且 allowEarlyReference 为 true             if (singletonObject == null && allowEarlyReference) {                 // 实例化之后调用 addSingletonFactory 方法将                 // ObjectFactory 初始化存储在 singletonFactories Map中                 // singletonFactories 三级缓存                 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);                 if (singletonFactory != null) {                     // 执行lambda AOp                     singletonObject = singletonFactory.getObject();                     //记录在缓存中 earlySingletonObjects与singletonFactories互斥                     this.earlySingletonObjects.put(beanName, singletonObject);                     this.singletonFactories.remove(beanName);                 }             }         }     }     return singletonObject; } 复制代码
  从单例池获取 a Bean 那是也是获取不到的,所以 singletonObject值为 null。至于 a 是否正在创建中,a 现在是在创建中了,所以进入if代码块,从二级缓存中获取实例,肯定获取不到,所以为 null,至于 allowEarlyReference的值,由上层传入 true。然后从三级缓存根据 beanName(也就是 a) 获取到 ObjectFactory类型的一个函数式接口。这时候是有值的,我们在 A 实例化之后,就将 key 为 a 的 ObjectFactory接口放入到了三级缓存中了。然后调用 singletonFactory的getObject(),也就是执行 lambda 体。我们现在可以知道 lambda 体是在产生循环依赖的时候才会被执行。然后将获得的对象保存在二级缓存中,将三级缓存的记录进行删除。
  lambda 体执行完毕之后,会返回一个对象。那该对象一定是 代理对象 吗?
  执行 lambda 体,也技术执行getEarlyBeanReference方法。 进入AbstractAutowireCapableBeanFactory类getEarlyBeanReference方法,	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { 		Object exposedObject = bean; 		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { 			for (BeanPostProcessor bp : getBeanPostProcessors()) {  // AOP 				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) { 					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp; 					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName); 				} 			} 		} 		return exposedObject; 	} 复制代码
  先将原始对象赋予给另外一个变量。获得所有的BeanPostProcessor,判断是否是SmartInstantiationAwareBeanPostProcessor,如果是则执行 getEarlyBeanReference方法。将原始对象和 bean名称作为入参传入。
  调试进入 SmartInstantiationAwareBeanPostProcessor类,可以发现getEarlyBeanReference方法下有两个子类实现。一个是InstantiationAwareBeanPostProcessorAdapter,另外一个是 AbstractAutoProxyCreator。这里我们看 AbstractAutoProxyCreator就好了,因为 InstantiationAwareBeanPostProcessorAdapter没有进行具体的实现。
  进入AbstractAutoProxyCreator类,其实这时候我们可以发现该类所处的包路径为
  org.springframework.aop.framework.autoproxy;,可以看到一个眼熟的词 aop。// 获取提前暴露的bean的引用,用来支持单例对象的循环引用,解决循环依赖问题	 @Override public Object getEarlyBeanReference(Object bean, String beanName) { 	Object cacheKey = getCacheKey(bean.getClass(), beanName);  	 	this.earlyProxyReferences.put(cacheKey, bean);  	return wrapIfNecessary(bean, beanName, cacheKey);  //可能会产生代理对象 } 复制代码
  该方法里有三行代码,我们一行一行的看。getCacheKey	protected Object getCacheKey(Class<?> beanClass, @Nullable String beanName) { 		if (StringUtils.hasLength(beanName)) { 			// isAssignableFrom:判断当前的 Class 所表示的类, 			// 是不是参数中传递的 Class 所表示的类的父类,超接口,或者是相同的类型 			// 是则返回true,否则返回false 			// 使用方式:父类.class.isAssignableFrom(子类.class) 			return (FactoryBean.class.isAssignableFrom(beanClass) ? 					BeanFactory.FACTORY_BEAN_PREFIX + beanName : beanName); 		} 		else { 			return beanClass; 		} 	} 复制代码
  首先判断当前beanName不等于 null 并且不等于空字符串,然后使用三元表达式计算当前 beanClass是否是FactoryBean子类或者子实现,如果是则将 beanName拼接上 &。
  第二行往 earlyProxyReferencesMap容器里添加了一条记录,key为cacheKey的值,value为 原始对象。在生命周期初始化后那步就会根据``earlyProxyReferencesMap进行判断,是否进行过 AOP。
  接下来我们看最重要的一个方法:wrapIfNecessary,方法名翻译之后大意是 如果需要包装,也就是说,如果要包装则产生代理对象,否则返回原对象。是否要产生代理对象是有条件的。wrapIfNecessaryprotected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {   // 在当前targetSourcedBeans中存在的bean,表示在实例化之前就产生了代理对象   // 直接返回,那就不要再次产生代理对象了   if(StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(       beanName)) {     return bean;   }   // advisedBeans 是一个 map, 存储的是 class:是否应该被代理,为true则需要被代理   // 当前这个bean不用被代理   if(Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {     return bean;   }   // 如果是普通bean,或者继承 Advisor、Pointcut、Advice   //AopInfrastructureBean 的类不需要被代理   if(isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(),       beanName)) {     // 将当前 cacheKey 标记为不用被代理     this.advisedBeans.put(cacheKey, Boolean.FALSE);     return bean;   }   // 获取当前 beanClass 所匹配的 advisors   Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(),     beanName, null);   // 如果匹配的advisors不等于null,那么则进行代理,并返回代理对象   if(specificInterceptors != DO_NOT_PROXY) {     // 将当前 cacheKey 标记为 需要被代理     this.advisedBeans.put(cacheKey, Boolean.TRUE);     // 基于bean对象和Advisor创建代理对象     Object proxy = createProxy(bean.getClass(), beanName,       specificInterceptors, new SingletonTargetSource(bean));     // 存一个代理对象的类型     this.proxyTypes.put(cacheKey, proxy.getClass());     return proxy;   }   this.advisedBeans.put(cacheKey, Boolean.FALSE);   return bean; } 复制代码
  上面 AOP 的逻辑走完了之后,就会将 A 对象进行返回,然后赋值给 B。B 的属性填充也就此完成,那就会继续往下走。if (earlySingletonExposure) { 			// earlySingletonReference 只有在检测到有循环依赖的情况下才会不为空 			Object earlySingletonReference = getSingleton(beanName, false); 			if (earlySingletonReference != null) { 				// 如果提前暴露的对象(bean)和经过了完整的生命周期后的对象相等(exposedObject) 				// 则把缓存中的earlySingletonReference赋值给exposedObject 				// 最终会添加到singletonObjects中去 				// (初始化之后的bean等于原始的bean,说明不是proxy), 				// 				if (exposedObject == bean) { 					exposedObject = earlySingletonReference; 			} 				//检测该bean的dependon的bean是否都已经初始化好了 				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { 					String[] dependentBeans = getDependentBeans(beanName); 					Set actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); 					for (String dependentBean : dependentBeans) { 						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { 							actualDependentBeans.add(dependentBean); 						} 					} 					/* 					* 因为 bean 创建后;其所依赖的 bean 一定是创建了的。 					* actualDependentBeans 不为空表示当 bean 创建后依赖的 bean 没有 					* 全部创建完,也就是说存在循环依赖 					*/ 					if (!actualDependentBeans.isEmpty()) { 						throw new BeanCurrentlyInCreationException(beanName, 								"Bean with name "" + beanName + "" has been injected into other beans [" + 								StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + 								"] in its raw version as part of a circular reference, but has eventually been " + 								"wrapped. This means that said other beans do not use the final version of the " + 								"bean. This is often the result of over-eager type matching - consider using " + 								""getBeanNamesOfType" with the "allowEagerInit" flag turned off, for example."); 					} 				} 			} 		} try { 			// 8、注册DisposableBean 			registerDisposableBeanIfNecessary(beanName, bean, mbd); 		} 		catch (BeanDefinitionValidationException ex) { 			throw new BeanCreationException( 					mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); 		}  		return exposedObject; 复制代码getSingleton:注意 allowEarlyReference的值为 false。@Nullable 	protected Object getSingleton(String beanName, boolean allowEarlyReference) { 		// 从单例池中获取实例 		Object singletonObject = this.singletonObjects.get(beanName);  		// 如果从单例池中没有获取到实例 并且 指定的单例bean正在创建中 		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { 			//锁定全局变量并进行处理 			synchronized (this.singletonObjects) { 				//earlySingletonObjects 二级缓存 				singletonObject = this.earlySingletonObjects.get(beanName); 				// 没有从二级缓存中获取到 并且 allowEarlyReference 为 true 				if (singletonObject == null && allowEarlyReference) { 					//实例化之后调用 addSingletonFactory 方法将 					//ObjectFactory 初始化存储在 singletonFactories Map中 					// singletonFactories 三级缓存 					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); 					if (singletonFactory != null) { 						// 执行lambda AOp 						singletonObject = singletonFactory.getObject(); 						//记录在缓存中 earlySingletonObjects与singletonFactories互斥 						this.earlySingletonObjects.put(beanName, singletonObject); 						this.singletonFactories.remove(beanName); 					} 				} 			} 		} 		return singletonObject; 	} 复制代码
  又是这个方法,带大家再来看看这个方法。这个时候 beanName 还是为 b,B 的生命周期还没走完呢。从单例池获取 -》 没有,b 还在创建中,所以进入 if,然后从二级里面获取 -》没有,allowEarlyReference由上层传入false,结束方法,返回 null。返回 null,就不会符合earlySingletonReference != null的判断,直接调用registerDisposableBeanIfNecessary方法,进行 Bean 销毁逻辑。protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) { 		AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null); 		if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) { 			if (mbd.isSingleton()) { 				// Register a DisposableBean implementation that performs all destruction 				// work for the given bean: DestructionAwareBeanPostProcessors, 				// DisposableBean interface, custom destroy method. 				registerDisposableBean(beanName, 						new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc)); 			} 			else { 				// A bean with a custom scope... 				Scope scope = this.scopes.get(mbd.getScope()); 				if (scope == null) { 					throw new IllegalStateException("No Scope registered for scope name "" + mbd.getScope() + """); 				} 				scope.registerDestructionCallback(beanName, 						new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc)); 			} 		} 	} 复制代码
  只要看 requiresDestruction方法就好了。protected boolean requiresDestruction(Object bean, RootBeanDefinition mbd) { 		// 判断某个Bean是否拥有销毁方法 		// 1. 实现了DisposableBean接口或AutoCloseable接口 		// 2. BeanDefinition中定义了destroyMethodName 		// 3. 类中是否存在@PreDestroy注解的方法 		return (bean.getClass() != NullBean.class && 				(DisposableBeanAdapter.hasDestroyMethod(bean, mbd) || (hasDestructionAwareBeanPostProcessors() && 						DisposableBeanAdapter.hasApplicableProcessors(bean, getBeanPostProcessors())))); 	}

爸爸去哪儿奶爸现状4位离婚,1位分居,其中3位已找到新欢爸爸去哪儿开播于2013年,一晃眼已经开播8年,历经了五季,节目中明星爸爸和萌娃们的各种感动和搞笑瞬间,给网友们带来了很多欢乐。而8年过去了,当年的萌娃们长大了,爸爸却不是当年的爸19年后再看征服,剧中6大恶人现状如何,有人70岁当网红19年前,一部名为征服的电视剧,引发收视狂潮。这部根据真实案件改编的影视作品,筹拍时遇到了资金上的困难,差点半途夭折。导演高群书搬出自己的全部家底,到处求人借钱,最终凑齐了400万横店群演现状惨淡20万人潮退去,懒汉丛生光棍遍地,遇戏疯抢横店是很多有明星梦的年轻人向往的地方。最初,横店是普通的村落,90年代末,在得到影视公司注资和地方的政策支持后,现在我们熟悉的横店影城1个个园区建立起来,成为内地最大的电影电视剧拍吉利旗下国内首款电动皮卡,破百才6秒多!雷达RD6太惊喜了聊到纯电皮卡,可能大家想到的是迟迟不来的特斯拉Cybertruck,供不应求的福特F150Lighting,复活了的悍马HUMMEREV皮卡,甚至是更科幻的RIVIANR1T。而我还没开始就结束了?游戏版号拿到了,可公司破产了在游戏版号停发8个多月后,终于恢复了。即便第一批版号数量仅45个,不过有总比没有强。是不是?而在如今6月份的版号较4月份增加了15个。然而细心的网友们却发现了一个有些糟心的现实那便吸毒艺人拿影帝,是今年最大笑话匪夷所思。最近,昔日的某个吸毒明星在台北电影节获奖了。图源网络一边惊讶于台湾的宽容度,一边又对微博下面的评论感到离谱抛开吸毒不说,他还是那么帅。什么时候在大陆可以复出。图源微博8年三大航空公司2500亿团购292架飞机,一文读懂国内史上最大订单钛度图闻钛度图闻第五十期策划制作钛媒体视觉中心,编辑刘美兰,作图初彦墨7月1日,国内三大航司南航国航以及东航宣布,将购买总价近2500亿元人民币(约372。57亿美元)的292架空客A32娱乐圈又又又塌房了创造2021,也算最后的选秀节目不知道大家还记得吗?在里面有位当时的宝藏爱豆。张欣尧,当时他的两条相关热搜张欣尧从一贫如洗到富甲一方以及张欣尧送给甘往星一张2万元银行卡获得了很高的易烊千玺事件背后其父公司经营异常法人曾称可将刑责变行政警告凤凰网风暴眼出品文凤凰网财经琢絮核心提示易烊千玺考编事件令疑似其父亲易上捷的资本版图被曝光。天眼查显示,易上捷目前共在13家企业中任职,其中3家企业任法人,9家企业任高管,公司涉及赵长鹏被喷看不懂智能合约!CZ解释误报UniswapV3漏洞乌龙全球最大交易所币安(Binance)创办人赵长鹏(CZ)昨(12)日清晨六点半左右,紧急发文表示他们在以太坊链上检测到去中心化交易所UniswapV3的潜在漏洞。事后发生不久,随即博瑞医药获世界银行集团国际金融公司5000万美元长期贷款支持2022年6月27日,博瑞生物医药(苏州)股份有限公司与世界银行集团国际金融公司(IFC)正式签署贷款协议。IFC将向博瑞医药提供5000万美元贷款支持,该笔贷款的获批充分显示了国
2022年幽默风趣富有哲理的金句(七一)1。为什么叫三八,三七是中药,三九是西药,只有三八是迷魂药。2。为什么要提倡百业孝为先,缘于娘肚子里有儿,儿肚子里没娘。3。贵人多忘事,贵人之所以为贵人,是因为他知道如何取舍,懂得摘抄小句子1。我们有奔赴远方的理想,有着用不退却的热情,我们就像一株野草,永远在肆意野蛮地生长。2。读了很多的道理,看过很多的励志书,但还是抵不过自己经历一遍来得深刻。3。不知所措的年纪,什智能一拖二自动切换,南卡Litepro2蓝牙耳机体验什么样的耳机能够获得德国红点设计大奖?其实在我们国内,有很多耳机品牌都已经在国外获奖无数,而其中获得德国红点设计大奖的也有不少,我这次则是入手了南卡Litepro2蓝牙耳机,这是一液晶电视和OLED电视如何选?听听业内人如何讲?现在市面上电视大体可以分为两种,一种是LCD电视,就是我们常说的液晶电视,另外一种则是OLED电视,它们价格相差也是非常大的!比如配置相同的液晶电视和OLED电视,以索尼电视为例,OPPO良心5G手机,天玑1200三星屏60W闪充,8GB128GB降至1599元OPPO这个品牌相信很多朋友们都十分清楚,毕竟在大街小巷上随处可见OPPO手机店。OPPO和VIVO一样,诞生的时候也是主打线下市场,不同于主打线上市场的小米手机,所以在性价比方面OPPOFindX5和小米11Pro价格差不多,哪一款更值?OPPOFindX5发布不足一个月,而小米11Pro已经发布整整一年的时间了!论硬件配置,小米11Pro依然不过时,不少地方甚至要比OPPOFindX5更优秀,而且价格上小米11P手机相机都能驾驭,智云云鹤M2S稳定器到底有多强?在用户的印象当中,能驾驭全画幅微单这类专业相机的稳定器,一般在体积和重量上都会相当的笨重,但给人的感觉就是一个专业的产品而只能驾驭手机这类的稳定器产品,虽然在体积和重量上会更加地追摩托罗拉亮剑5800mAh新机,161TB首次上阵,4299成焊门员现在的摩托罗拉应该很多人都知道了,毕竟在联想之下,我们看到摩托罗拉新机不断,并且都是性价比很高的旗舰机。进入2022年,我们看到摩托罗拉对于新机布局很积极,在小米OV一个都没有发布一步到位,4K144Hz高刷爽快玩,泰坦军团P2SA2V旗舰电竞显示器前言7080和9000的消费观念差距非常大,可能因为节约的习惯,相比于买买买,7080更喜欢一步到位,我们有一个专业名词毕业装。对于喜欢玩游戏,尤其是电竞游戏的朋友来说,游戏主机当vivo心动价1349,跑分57万五千毫安120Hz高刷,还有LPDDR5内存自从华为遭遇芯片问题之后,手机销量大幅度下滑,原本在国内市场份额排名第一的华为,销量甚至已经不及个别大厂商的子品牌了。那么目前国内市场中,排名第一的品牌又是谁呢?不少用户会以为是主三星开始向中端智能机发送3月更新,A系列即将大面积推送大家好,欢迎关注Galaxy数码菌。2022年3月的月度安全更新尚未覆盖所有高端Galaxy设备,但三星已开始将其推广到一些中端智能手机。据数码菌了解到,包括GalaxyA41在内