亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Spring源碼剖析之Spring處理循環(huán)依賴的問題

 更新時間:2021年06月29日 15:09:12   作者:墨家巨子@俏如來  
大家都知道循環(huán)依賴依賴指的是Bean與Bean之間的依賴關(guān)系,循環(huán)依賴指的是兩個或者多個Bean相互依賴,本文通過代碼示例給大家講解Spring處理循環(huán)依賴的問題,感興趣的朋友一起看看吧

前言

你是不是被這個騷氣的標題吸引進來的,_ 喜歡我的文章的話就給個好評吧,你的肯定是我堅持寫作最大的動力,來吧兄弟們,給我一點動力

Spring如何處理循環(huán)依賴?這是最近較為頻繁被問到的一個面試題,在前面Bean實例化流程中,對屬性注入一文多多少少對循環(huán)依賴有過介紹,這篇文章詳細講一下Spring中的循環(huán)依賴的處理方案。

什么是循環(huán)依賴

依賴指的是Bean與Bean之間的依賴關(guān)系,循環(huán)依賴指的是兩個或者多個Bean相互依賴,如:

在這里插入圖片描述

構(gòu)造器循環(huán)依賴

代碼示例:

public class BeanA {

    private BeanB beanB;

    public BeanA(BeanB beanB){
        this.beanB = beanB;
    }
}

public class BeanB {

    private BeanA beanA;

    public BeanB(BeanA beanA){
        this.beanA = beanA;
    }
}

配置文件

<bean id="beanA" class="cn.itsource._01_di.BeanA" >
        <constructor-arg type="cn.itsource._01_di.BeanB" ref="beanB"  />
 </bean>


 <bean id="beanB" class="cn.itsource._01_di.BeanB"  >
         <constructor-arg type="cn.itsource._01_di.BeanA" ref="beanA" />
 </bean>

Setter循環(huán)依賴

代碼示例

public class BeanA {

    private BeanB beanB;

    public void setBeanB(BeanB beanB){
        this.beanB = beanB;
    }
}

@Data
public class BeanB {

    private BeanA beanA;

    public void setBeanA(BeanA beanA){
        this.beanA = beanA;
    }
}

配置文件

<bean id="beanA" class="cn.itsource._01_di.BeanA" >
    <property name="beanB" ref="beanB" />
</bean>

<bean id="beanB" class="cn.itsource._01_di.BeanB">
    <property name="beanA" ref="beanA" />
</bean>

循環(huán)依賴包括: 構(gòu)造器注入循環(huán)依賴 set , 注入循環(huán)依賴 和 prototype模式Bean的循環(huán)依賴。Spring只解決了單利Bean的 setter 注入循環(huán)依賴,對于構(gòu)造器循環(huán)依賴,和 prototype模式的循環(huán)依賴是無法解決的,在創(chuàng)建Bean的時候就會拋出異常 :“BeanCurrentlyInCreationException” ,

循環(huán)依賴控制開關(guān)在 AbstractRefreshableApplicationContext 容器工廠類中有定義:

public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {

	@Nullable
	private Boolean allowBeanDefinitionOverriding;
	//是否允許循環(huán)依賴
	@Nullable
	private Boolean allowCircularReferences;
	
	//設(shè)置循環(huán)依賴
	public void setAllowCircularReferences(boolean allowCircularReferences) {
		this.allowCircularReferences = allowCircularReferences;
	}

默認情況下是允許Bean之間的循環(huán)依賴的,在依賴注入時Spring會嘗試處理循環(huán)依賴。如果將該屬性配置為“false”則關(guān)閉循環(huán)依賴,當在Bean依賴注入的時遇到循環(huán)依賴時拋出異常??梢酝ㄟ^如下方式關(guān)閉,但是一般都不這么做

ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
//禁用循環(huán)依賴
applicationContext.setAllowCircularReferences(false);
//刷新容器
applicationContext.refresh();
...

構(gòu)造器循環(huán)依賴處理

構(gòu)造器是不允許循環(huán)依賴的,動動你的小腦瓜想一想,比如:A 依賴 B ,B依賴C,C依賴A,在實例化A的時候,構(gòu)造器需要注入B,然后Spirng會實例化B,此時的A屬于“正在創(chuàng)建”的狀態(tài)。當實例化B的時候,發(fā)現(xiàn)構(gòu)造器需要注入C,然后去實例化C,然而實例化C的時候又需要注入A的實例,這樣就造成了一個死循環(huán),永遠無法先實例化出某一個Bean,所以Spring遇到這里構(gòu)造器循環(huán)依賴會直接拋出異常。

那么Spring到底是如何做的呢?

  1. 首先Spring會走Bean的實例化流程嘗試創(chuàng)建 A 的實例 ,在創(chuàng)建實例之間先從 “正在創(chuàng)建Bean池” (一個緩存Map而已)中去查找A 是否正在創(chuàng)建,如果沒找到,則將 A 放入 “正在創(chuàng)建Bean池”中,然后準備實例化構(gòu)造器參數(shù) B。
  2. Spring會走Bean的實例化流程嘗試創(chuàng)建 B 的實例 ,在創(chuàng)建實例之間先從 “正在創(chuàng)建Bean池” (一個緩存Map而已)中去查找B 是否正在創(chuàng)建,如果沒找到,則將 B 放入 “正在創(chuàng)建Bean池”中,然后準備實例化構(gòu)造器參數(shù) A。
  3. Spring會走Bean的實例化流程嘗試創(chuàng)建 A 的實例 ,在創(chuàng)建實例之間先從 “正在創(chuàng)建Bean池” (一個緩存Map而已)中去查找A 是否正在創(chuàng)建。
  4. 此時:Spring發(fā)現(xiàn) A 正處于“正在創(chuàng)建Bean池”,表示出現(xiàn)構(gòu)造器循環(huán)依賴,拋出異常:“BeanCurrentlyInCreationException”

DefaultSingletonBeanRegistry#getSingleton

下面我們以 BeanA 構(gòu)造參數(shù)依賴BeanB, BeanB 構(gòu)造參數(shù)依賴BeanA 為例來分析。

當Spring的IOC容器啟動,嘗試對單利的BeanA進行初始化,根據(jù)之前的分析我們知道,單利Bean的創(chuàng)建入口是 AbstractBeanFactory#doGetBean 在該方法中會先從單利Bean緩存中獲取,如果沒有代碼會走到:DefaultSingletonBeanRegistry#getSingleton(jString beanName, ObjectFactory<?> singletonFactory) 方法中 ,在該方法中會先對把創(chuàng)建的Bean加入 一個名字為 singletonsCurrentlyInCreation 的 ConcurrentHashMap中,意思是該Bean正在創(chuàng)建中,然后調(diào)用 ObjectFactory.getObject() 實例化Bean , 假設(shè) BeanA 進入了該方法進行實例化:

//正在創(chuàng)建中的Bean
private final Set<String> singletonsCurrentlyInCreation =
			Collections.newSetFromMap(new ConcurrentHashMap<>(16));

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		...省略...
		//把該Bean的名字加入 singletonsCurrentlyInCreation 正在創(chuàng)建池 中
		beforeSingletonCreation(beanName);
		boolean newSingleton = false;
		boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
		if (recordSuppressedExceptions) {
			this.suppressedExceptions = new LinkedHashSet<>();
		}
		try {
			//調(diào)用ObjectFactory創(chuàng)建Bean的實例
			singletonObject = singletonFactory.getObject();
			newSingleton = true;
		}
...省略...


//如果singletonsCurrentlyInCreation中沒該Bean,就把該Bean存儲到singletonsCurrentlyInCreation中,
//如果 singletonsCurrentlyInCreation 中有 該Bean,就報錯循環(huán)依賴異常BeanCurrentlyInCreationException
//也就意味著同一個beanName進入該方法2次就會拋異常
protected void beforeSingletonCreation(String beanName) {
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
			throw new BeanCurrentlyInCreationException(beanName);
		}
	}

beforeSingletonCreation 方法非常關(guān)鍵 ,它會把beanName加入 singletonsCurrentlyInCreation,一個代表“正在創(chuàng)建中的Bean”的ConcurrentHashMap中。

  • 如果singletonsCurrentlyInCreation中沒該beanName,就把該Bean存儲到singletonsCurrentlyInCreation中,
  • 如果 singletonsCurrentlyInCreation 中有 該Bean,就報錯循環(huán)依賴異常BeanCurrentlyInCreationException

【注意】也就意味著同一個beanName進入該方法2次就會拋異常 , 現(xiàn)在BeanA已經(jīng)加入了singletonsCurrentlyInCreation

AbstractAutowireCapableBeanFactory#autowireConstructor

我們前面分析過 ObjectFactory.getObject實例化Bean的詳細流程,這里我只是大概在復(fù)盤一下就行了。因為我們的BeanA的構(gòu)造器注入了一個BeanB,所以 代碼最終會走到AbstractAutowireCapableBeanFactory#autowireConstructor ,通過構(gòu)造器來實例化BeanA(在屬性注入那一章有講到 ) 。

在autowireConstructor 方法中會通過 ConstructorResolver#resolveConstructorArguments 來解析構(gòu)造參數(shù),調(diào)用 BeanDefinitionValueResolver 去把 ref="beanB" 這種字符串的引用變成一個實實在在的Bean,即BeanB,所以在 BeanDefinitionValueResolver 屬性值解析器中又會去實例化BeanB,同樣會走到 DefaultSingletonBeanRegistry#getSingleton 中把BeanB加入 singletonsCurrentlyInCreation “正在創(chuàng)建Bean池”中,然后調(diào)用ObjectFactory.getObject實例化BeanB。

低于BeanB而已同樣需要通過構(gòu)造器創(chuàng)建,BeanB構(gòu)造器參數(shù)依賴了BeanA,也就意味著又會調(diào)用 BeanDefinitionValueResolver 去把 ref=“beanA” 這種字符串引用變成容器中的BeanA的Bean實例,然后代碼又會走到 DefaultSingletonBeanRegistry#getSingleton。然后再一次的嘗試把BeanA加入singletonsCurrentlyInCreation “正在創(chuàng)建Bean池”。

此時問題就來了,在最開始創(chuàng)建BeanA的時候它已經(jīng)加入過一次“正在創(chuàng)建Bean” 池,這會兒實例化BeanB的時候,由于構(gòu)造器參數(shù)依賴了BeanA,導致BeanA又想進入“正在創(chuàng)建Bean” 池 ,此時 Spring拋出循環(huán)依賴異常:

Error creating bean with name ‘beanA': Requested bean is currently in creation: Is there an unresolvable circular reference?

到這,Spring處理構(gòu)造器循環(huán)依賴的源碼分析完畢。

setter循環(huán)依賴處理

setter循環(huán)依賴是可以允許的。Spring是通過提前暴露未實例化完成的Bean的 ObjectFactory 來實現(xiàn)循環(huán)依賴的,這樣做的目的是其他的Bean可以通過 ObjectFactory 引用到該Bean。

實現(xiàn)流程如下:

  1. Spring創(chuàng)建BeanA,通過無參構(gòu)造實例化,并暴露一個ObjectFactory,用來獲取創(chuàng)建中的BeanA,然后把BeanA添加到“正在創(chuàng)建Bean池”中,然后通過setter注入BeanB
  2. Spring創(chuàng)建BeanB,通過無參構(gòu)造實例化,并暴露一個ObjectFactory,用來獲取創(chuàng)建中的BeanB,然后把BeanB添加到“正在創(chuàng)建Bean池”中,然后通過setter注入BeanA
  3. 在BeanB通過setter注入BeanA時,由于BeanA 提前暴露了ObjectFactory ,通過它返回一個提前暴露一個創(chuàng)建中的BeanA。
  4. 然后完成BeanB的依賴注入

AbstractAutowireCapableBeanFactory#doCreateBean

我們以BeanA 通過settter依賴BeanB,BeanB通過setter 依賴BeanA為例來分析一下源碼,在之前的Bean實例化流程分析過程中我們了解到,Bean的實例化會走AbstractBeanFactory#doGetBean,然后查找單利緩存中是否有該Bean ,如果沒有就調(diào)用 DefaultSingletonBeanRegistry#getSingleton,方法會把BeanA加入 singletonsCurrentlyInCreation “創(chuàng)建中的Bean池”,然后調(diào)用ObjectFactory.getObject創(chuàng)建Bean.

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean 源碼:

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

		final String beanName = transformedBeanName(name);
		Object bean;

		// Eagerly check singleton cache for manually registered singletons.
		//緩存中獲取Bean,解決了循環(huán)依賴問題
		Object sharedInstance = getSingleton(beanName);
     	...緩存中沒有走下面...
		if (mbd.isSingleton()) {
					//走 DefaultSingletonBeanRegistry#getSingleton ,方法會把bean加入“正在創(chuàng)建bean池”
					//然后調(diào)用ObjectFactory實例化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.
							destroySingleton(beanName);
							throw ex;
						}
					});
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

第一次進來,緩存中是沒有BeanA的,所有會走 getSingleton 方法,然后代碼最終會走到AbstractAutowireCapableBeanFactory#doCreateBean 方法中 。

AbstractAutowireCapableBeanFactory#doCreateBean源碼:

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {

		// Instantiate the bean.
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
		//實例化Bean
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		...省略...
		//如果是單利 ,如果是允許循環(huán)依賴,如果 beanName 出于創(chuàng)建中,已經(jīng)被添加到“創(chuàng)建中的bean池”
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			if (logger.isDebugEnabled()) {
				logger.debug("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			//把ObjectFactory 添加到 singletonFactories 中。
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}
		
	try {
		//走依賴注入流程
			populateBean(beanName, mbd, instanceWrapper);
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}


//緩存單利Bean的創(chuàng)建工廠,用于解決循環(huán)依賴
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(singletonFactory, "Singleton factory must not be null");
		synchronized (this.singletonObjects) {
			//singletonObjects單利緩存中是否包含Bean
			if (!this.singletonObjects.containsKey(beanName)) {
				//提前暴露ObjectFactory,把ObjectFactory放到singletonFactories中,
				//后面解決循環(huán)依賴,獲取Bean實例的時候會用到
				this.singletonFactories.put(beanName, singletonFactory);
				//早期單利bean緩存中移除Bean
				this.earlySingletonObjects.remove(beanName);
				//把注冊的Bean加入registeredSingletons中
				this.registeredSingletons.add(beanName);
			}
		}
	}

該方法中把BeanA實例化好之后,會把ObjectFactory存儲到一個 singletonFactories (HashMap)中來提前暴露Bean的創(chuàng)建工廠,用于解決循環(huán)依賴【重要】,然后調(diào)用 populateBean 走屬性注入流程。

屬性注入會通過BeanDefinition得到bean的依賴屬性,然后調(diào)用 AbstractAutowireCapableBeanFactory#applyPropertyValues ,把屬性應(yīng)用到對象上。在applyPropertyValues 方法中最終調(diào)用 BeanDefinitionValueResolver#resolveValueIfNecessary 解析屬性值,比如:ref=“beanB” 這種字符串引用變成 對象實例的引用。

在BeanDefinitionValueResolver解析依賴的屬性值即:BeanB的時候,同樣會觸發(fā)BeanB的實例化,代碼會走到AbstractBeanFactory#doGetBean ,然后走方法 DefaultSingletonBeanRegistry#getSingleton 中把BeanB加入 singletonsCurrentlyInCreation “創(chuàng)建中的Bean池”,然后代碼會走到AbstractAutowireCapableBeanFactory#doCreateBean 方法中創(chuàng)建BeanB,

該方法中會先實例化BeanB,接著會把BeanB的ObjectFactory存儲到 singletonFactories (HashMap)中來提前暴露Bean的創(chuàng)建工廠,用于解決循環(huán)依賴,然后調(diào)用 populateBean 走屬性注入流程。

同樣因為BeanB通過Setter 注入了 A,所以在 populateBean 屬性注入流程中會解析 ref=“beanA” 為容器中的 BeanA 的實例。

然后會走到 AbstractBeanFactory#doGetBean 中獲取BeanA的實例。這個時候流程就不一樣了,我們先看一下 AbstractBeanFactory#doGetBean 中的代碼

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

		final String beanName = transformedBeanName(name);
		Object bean;

		// Eagerly check singleton cache for manually registered singletons.
		//從緩存中獲取Bean
		Object sharedInstance = getSingleton(beanName);

		...省略...

		//如果緩存中沒有Bean,就創(chuàng)建Bean
		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);
				}

在獲取單利Bean的實例的時候是會先去單利Bean的緩存中去查看Bean是否已經(jīng)存在,如果不存在,才會走DefaultSingletonBeanRegistry#getSingleton方法創(chuàng)建Bean。
問題是:此刻單利Bean緩存中已經(jīng)有BeanA了,因為在最開始BeanA已經(jīng)出于“正在創(chuàng)建Bean池”中了。我們先來看一下是如何從緩存獲取Bean的。

DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)源碼如下:

//allowEarlyReference :是否創(chuàng)建早期應(yīng)用,主要用來解決循環(huán)依賴
	@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// Quick check for existing instance without full singleton lock
		//從Map中 singletonObjects = new ConcurrentHashMap<>(256); 獲取單利Bean

		//【一級緩存】singletonObject緩存中是否有Bean , 它存儲的是已經(jīng)實例化好的Bean
		Object singletonObject = this.singletonObjects.get(beanName);

		//如果singletonObjects中沒有Bean,但是Bean出于正在創(chuàng)建池中,即: Set<String> singletonsCurrentlyInCreation中有Bean,
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			
			//【二級緩存】從早期單例對象的緩存 earlySingletonObjects 中獲取
			singletonObject = this.earlySingletonObjects.get(beanName);
			
			//早期單利對象緩存中也沒有,但是允許循環(huán)依賴
			if (singletonObject == null && allowEarlyReference) {
				
				synchronized (this.singletonObjects) {
					// Consistent creation of early reference within full singleton lock
					singletonObject = this.singletonObjects.get(beanName);
					if (singletonObject == null) {
						singletonObject = this.earlySingletonObjects.get(beanName);
						if (singletonObject == null) {
							
							//【三級緩存】獲取ObjectFactory , 對象創(chuàng)建工廠,得到Bean創(chuàng)建過程中提前暴露的工廠。
							ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
							if (singletonFactory != null) {
								//通過工廠ObjectFactory 獲取對象實例
								singletonObject = singletonFactory.getObject();
								//把對象存儲到早期緩存中
								this.earlySingletonObjects.put(beanName, singletonObject);
								//把ObjectFactory移除
								this.singletonFactories.remove(beanName);
							}
						}
					}
				}
			}
		}
		return singletonObject;
	}

這里就是經(jīng)典的三級緩存解決Spring循環(huán)依賴。你看到了,這里會先從 singletonObjects 單利Bean緩存集合中獲取Bean(該緩存是實例化完成了的Bean),如果沒有,就從earlySingletonObjects早期對象緩存中獲取Bean(該緩存中存放的是還未實例化完成的早期Bean),如果還是沒有,就從singletonFactories中得到暴露的ObjectFactory來獲取依賴的Bean。然后放入早期緩存中。并把ObjectFactory從singletonFactories中移除。最后返回Bean的實例。

由于在實例化BeanA的時候已經(jīng)把BeanA的ObjectFactory添加到了 singletonFactories 緩存中,那么這里就會走到 singletonFactory.getObject(); 方法得到BeanA的實例,并且會把BeanA存儲到 earlySingletonObjects早期單利Bean緩存中。

BeanA的實例成功返回,那么BeanB的 setter注入成功,代表BeanB實例化完成,那么BeanA的setter方法注入成功,BeanA實例化完成。

prototype模式的循環(huán)依賴

對于prototype模式下的Bean不允許循環(huán)依賴,因為 這種模式下Bean是不做緩存的,所以就沒法暴露ObjectFactory,也就沒辦法實現(xiàn)循環(huán)依賴。

總結(jié)

不知道你有沒有看暈,反正我但是在源碼時的過程是比較辛苦的~~~~(>_<)~~~~ ,這里需要你對前面Bean的實例化流程和屬性注入流程比較熟悉,否則就會暈菜。

這里總結(jié)一下:

  1. 構(gòu)造器循環(huán)依賴是不允許的,主要通過 singletonsCurrentlyInCreation “正在創(chuàng)建Bean池” 把創(chuàng)建中的Bean緩存起來,如果循環(huán)依賴,同一個Bean勢必會嘗試進入該緩存2次,拋出循環(huán)依賴異常。
  2. setter循環(huán)依賴是可以允許的。Spring是通過提前暴露未實例化完成的Bean的 ObjectFactory 來實現(xiàn)循環(huán)依賴的,這樣做的目的是其他的Bean可以通過 ObjectFactory 引用到該Bean 。

在獲取依賴的Bean的時候使用到了三級緩存。

下面的面試題你會答了嗎?

  1. Spirng支持那種模式下的循環(huán)依賴(構(gòu)造器?,setter?, prototype?)
  2. Spring是如何處理構(gòu)造器注入循環(huán)依賴的?
  3. Spring是如何處理Setter注入循環(huán)依賴的?

到此這篇關(guān)于Spring源碼剖析之Spring處理循環(huán)依賴的問題的文章就介紹到這了,更多相關(guān)Spring循環(huán)依賴內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • JAVA十大排序算法之快速排序詳解

    JAVA十大排序算法之快速排序詳解

    這篇文章主要介紹了java中的快速排序,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-08-08
  • Spring如何更簡單的讀取和存儲對象

    Spring如何更簡單的讀取和存儲對象

    這篇文章主要給大家介紹了關(guān)于Spring如何更簡單的讀取和存儲對象的相關(guān)資料,在Spring 中想要更簡單的存儲和讀取對象的核?是使?注解,文中通過圖文介紹的非常詳細,需要的朋友可以參考下
    2023-06-06
  • 詳解SpringBoot啟動代碼和自動裝配源碼分析

    詳解SpringBoot啟動代碼和自動裝配源碼分析

    這篇文章主要介紹了SpringBoot啟動代碼和自動裝配源碼分析,使用SpringBoot很簡單,在主類中添加一個@SpringBootApplication,以及調(diào)用SpringApplication.run()并傳入主類,本文通過示例代碼給大家介紹的非常詳細,需要的朋友可以參考下
    2022-07-07
  • Java binarysearch方法原理詳解

    Java binarysearch方法原理詳解

    這篇文章主要介紹了Java binarysearch方法原理詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-01-01
  • 詳解Java多線程和IO流的應(yīng)用

    詳解Java多線程和IO流的應(yīng)用

    這篇文章主要介紹了詳解Java多線程和IO流的應(yīng)用,無論是本地文件復(fù)制,還是網(wǎng)絡(luò)多線程下載,對于流的使用都是一樣的,需要的朋友可以參考下
    2023-04-04
  • java播放聲音類和一個簡單示例

    java播放聲音類和一個簡單示例

    這篇文章主要介紹了一個java播放聲音類和一個java播放聲音的應(yīng)用程序,應(yīng)用程序可以單次播放聲音、循環(huán)播放聲音,需要的朋友可以參考下
    2014-03-03
  • 關(guān)于 Java 的數(shù)據(jù)結(jié)構(gòu)鏈表

    關(guān)于 Java 的數(shù)據(jù)結(jié)構(gòu)鏈表

    這篇文章主要介紹了關(guān)于 Java 的數(shù)據(jù)結(jié)構(gòu)鏈表的相關(guān)資料,需要的朋友可以參考下面文章內(nèi)容
    2021-09-09
  • Spring事務(wù)處理原理步驟詳解

    Spring事務(wù)處理原理步驟詳解

    這篇文章主要介紹了Spring事務(wù)處理原理步驟詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-03-03
  • java學生信息管理系統(tǒng)源代碼

    java學生信息管理系統(tǒng)源代碼

    這篇文章主要為大家詳細介紹了java學生信息管理系統(tǒng)源代碼,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • 靈活控制任務(wù)執(zhí)行時間的Cron表達式范例

    靈活控制任務(wù)執(zhí)行時間的Cron表達式范例

    這篇文章主要為大家介紹了靈活控制任務(wù)執(zhí)行時間的Cron表達式范例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-10-10

最新評論