最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

帶有@Transactional和@Async的循環(huán)依賴問題的解決

 更新時間:2020年04月30日 10:42:01   作者:黃山技術(shù)猿  
這篇文章主要介紹了帶有@Transactional和@Async的循環(huán)依賴問題的解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

今天我們來探討一個有意思的spring源碼問題,也是一個學(xué)生告訴了我現(xiàn)象我從源碼里面找到了這個有意思的問題。
首先我們看service層的代碼案例,如下:

@Service("transationServiceImpl")
public class TransationServiceImpl implements TransationService {

  @Autowired
  TransationService transationService;

  @Transactional
  @Async
  @Override
  public void transation() {
  }
}

在transation方法上面加上了@Transactional和@Async兩個注解,然后在TransationServiceImpl 類中自己把自己的實例注入到transationService屬性中,存在循環(huán)依賴,理論上單例的循環(huán)依賴是允許的。但是我們啟動容器會報錯,測試代碼如下:

public class MyTest {
  @Test
  public void test1() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ComponentScanBean.class);
  }
}

@Component
@ComponentScan(basePackages = {"com.xiangxue"})
public class ComponentScanBean {
}

然后右鍵運(yùn)行test1單元測試加載spring容器就會報錯,報錯信息如下:
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name ‘transationServiceImpl': Bean with name ‘transationServiceImpl' has been injected into other beans [transationServiceImpl] 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 ‘a(chǎn)llowEagerInit' flag turned off, for example.
從報錯的字面意思來看,是存在了多版本的循環(huán)依賴,如果要解決這個問題,我們必須追溯到源碼中。

首先我們從TransationServiceImpl 實例化開始講起。
實例化從getBean方法看起,前面代碼我就不貼了,這篇文章是給讀過spring源碼的人看的,沒讀過也看不懂,哈哈 。

1、首先第一次創(chuàng)建TransationServiceImpl實例的時候會從緩存中獲取實例 ,如果緩存里面有實例則直接返回,第一次創(chuàng)建的時候緩存中是沒有實例的,所以會走到else代碼塊中。


這里是從三個緩存中獲取實例化的詳細(xì)代碼。后面會分析

	@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		//根據(jù)beanName從緩存中拿實例
		//先從一級緩存拿
		Object singletonObject = this.singletonObjects.get(beanName);
		//如果bean還正在創(chuàng)建,還沒創(chuàng)建完成,其實就是堆內(nèi)存有了,屬性還沒有DI依賴注入
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				//從二級緩存中拿
				singletonObject = this.earlySingletonObjects.get(beanName);

				//如果還拿不到,并且允許bean提前暴露
				if (singletonObject == null && allowEarlyReference) {
					//從三級緩存中拿到對象工廠
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
						//從工廠中拿到對象
						singletonObject = singletonFactory.getObject();
						//升級到二級緩存
						System.out.println("======get instance from 3 level cache->beanName->" + beanName + "->value->" + singletonObject );
						this.earlySingletonObjects.put(beanName, singletonObject);
						//刪除三級緩存
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}

2、第一次進(jìn)來緩存中沒有則創(chuàng)建TransationServiceImpl的實例


最終會走到doCreateBean方法中進(jìn)行實例化,部分代碼如下

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

		............非關(guān)鍵代碼不貼了

		// Eagerly cache singletons to be able to resolve circular references
		// even when triggered by lifecycle interfaces like BeanFactoryAware.
		//是否	單例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");
			}
			//這里著重理解,對理解循環(huán)依賴幫助非常大,重要程度 5  添加三級緩存
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
			//ioc di,依賴注入的核心方法,該方法必須看,重要程度:5
			populateBean(beanName, mbd, instanceWrapper);

			//bean 實例化+ioc依賴注入完以后的調(diào)用,非常重要,重要程度:5
			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);
			}
		}

		if (earlySingletonExposure) {
			Object earlySingletonReference = getSingleton(beanName, false);
			if (earlySingletonReference != null) {
				if (exposedObject == bean) {
					exposedObject = earlySingletonReference;
				}
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
					for (String dependentBean : dependentBeans) {
						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
							actualDependentBeans.add(dependentBean);
						}
					}
					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.");
					}
				}
			}
		}

		............非關(guān)鍵代碼不貼了

		return exposedObject;
	}

由于業(yè)務(wù)類有循環(huán)依賴


所以在第一次實例化業(yè)務(wù)類的時候,在populateBean(beanName, mbd, instanceWrapper);進(jìn)行依賴注入時會觸發(fā)TransationServiceImpl業(yè)務(wù)類的getBean操作,也就是會調(diào)用TransationServiceImpl業(yè)務(wù)類的getBean方法,第二次會走到TransationServiceImpl實例化的邏輯中。這里明白的刷朵鮮花敲個1,哈哈。

但是在觸發(fā)第二次業(yè)務(wù)類的getBean操作之前,還有一個非常重要的步驟,就是業(yè)務(wù)類的提前暴露,也就是三級緩存的建立。這塊會建立業(yè)務(wù)類和ObjectFactory的映射關(guān)系這個建立映射關(guān)系是在依賴注入之前?。。。?br />


3、循環(huán)依賴注入觸發(fā)TransationServiceImpl類的第二次getBean獲取實例化的邏輯
第二次進(jìn)來的時候,由于第一次實例化的時候在三級緩存中建立了映射關(guān)系,所以第二次會從緩存中獲取實例


ObjectFactory對象的getObject方法就會調(diào)用到。getEarlyBeanReference方法,這個方法是會從BeanPostProcessor中獲取實例,這里可能就會返回代理實例


三級緩存的getObject方法會調(diào)用到getEarlyBeanReference中,斷點(diǎn)一下,看看。


從斷點(diǎn)看,
3:是獲取事務(wù)代理的BeanPostProcessor類型是SmartInstantiationAwareBeanPostProcessor類型的,所以事務(wù)代理的BeanPostProcessor會進(jìn)來,然后生成代理
4:是獲取@Async異步代理的BeanPostProcessor,但是不是SmartInstantiationAwareBeanPostProcessor類型的,所以這里if就不會進(jìn)來,所以最后這里從三級緩存中拿到的是事務(wù)切面的代碼對象,注意這里是類中的依賴注入的實例是事務(wù)切面的代理實例,如圖:


可以看到,這里的advisors切面容器明顯是一個事務(wù)切面,所以業(yè)務(wù)類中依賴注入的是一個事務(wù)切面的代理實例。
但是在這里我還是要說一下,在生成事務(wù)代理的時候其實是有做緩存的,如下代碼:


這里的cacheKey就是TransationServiceImpl業(yè)務(wù)類的bean的名稱的字符串,然后會把這個字符串加入到一個earlyProxyReferences的Set容器中

在這里已經(jīng)在TransationServiceImpl的第二次getBean的時候從三級緩存中獲取到了代理對象了,那么第二次的實例化已經(jīng)完成了,并且已經(jīng)依賴注入到了TransationServiceImpl的屬性中了,這時候依賴注入已經(jīng)完成了,好,我們還是接著第一次TransationServiceImpl的實例來講,貼代碼:

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

		............非關(guān)鍵代碼不貼了

		// Eagerly cache singletons to be able to resolve circular references
		// even when triggered by lifecycle interfaces like BeanFactoryAware.
		//是否	單例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");
			}
			//這里著重理解,對理解循環(huán)依賴幫助非常大,重要程度 5  添加三級緩存
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
			//ioc di,依賴注入的核心方法,該方法必須看,重要程度:5
			populateBean(beanName, mbd, instanceWrapper);

			//bean 實例化+ioc依賴注入完以后的調(diào)用,非常重要,重要程度:5
			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);
			}
		}

		if (earlySingletonExposure) {
			Object earlySingletonReference = getSingleton(beanName, false);
			if (earlySingletonReference != null) {
				if (exposedObject == bean) {
					exposedObject = earlySingletonReference;
				}
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
					for (String dependentBean : dependentBeans) {
						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
							actualDependentBeans.add(dependentBean);
						}
					}
					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.");
					}
				}
			}
		}

		............非關(guān)鍵代碼不貼了

		return exposedObject;
	}

也就是populateBean(beanName, mbd, instanceWrapper);依賴注入已經(jīng)完成了,代碼接著往下走。
代理會執(zhí)行到:

//bean 實例化+ioc依賴注入完以后的調(diào)用,非常重要,重要程度:5
exposedObject = initializeBean(beanName, exposedObject, mbd);

在這里,業(yè)務(wù)類會在這個方法里面再次生成代理,這里就有意思了。代碼如下

	protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
		if (System.getSecurityManager() != null) {
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				invokeAwareMethods(beanName, bean);
				return null;
			}, getAccessControlContext());
		}
		else {
			//調(diào)用Aware方法
			invokeAwareMethods(beanName, bean);
		}

		Object wrappedBean = bean;
		if (mbd == null || !mbd.isSynthetic()) {
			//對類中某些特殊方法的調(diào)用,比如@PostConstruct,Aware接口,非常重要 重要程度 :5
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}

		try {
			//InitializingBean接口,afterPropertiesSet,init-method屬性調(diào)用,非常重要,重要程度:5
			invokeInitMethods(beanName, wrappedBean, mbd);
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					(mbd != null ? mbd.getResourceDescription() : null),
					beanName, "Invocation of init method failed", ex);
		}
		if (mbd == null || !mbd.isSynthetic()) {
			//這個地方可能生出代理實例,是aop的入口
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}

		return wrappedBean;
	}

wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
在這個方法里面可能會生成業(yè)務(wù)類的代理,我們看看這個方法:

@Override
	public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
			throws BeansException {

		Object result = existingBean;
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
			Object current = processor.postProcessAfterInitialization(result, beanName);
			if (current == null) {
				return result;
			}
			result = current;
		}
		return result;
	}

我們斷點(diǎn)看看情況


**效果跟我們預(yù)期的一樣,第一次實例化的時候,在屬性依賴注入的時候會在三級緩存中獲取事務(wù)的代理對象,從斷點(diǎn)看,里面的屬性確實是一個事務(wù)的代理對象,自己本身是沒生成代理的。

由于方法上面有 @Transactional @Async在,3,4兩個AOP入口的BeanPostProcessor中會生成相應(yīng)的代理對象,這里為什么會生成代理對象,就不贅述了,核心思想是獲取所有advisors,然后挨個判斷advisors的pointCut是否matches這兩個注解,matches的思路是看方法上面是否有@Transactional 或@Async注解,如果有則返回true就匹配了,如果能找到匹配的切面則生成bean的代理,但是這里要注意的是,事務(wù)切面在這里就不會生成代理了,為什么呢???**看代碼


這里會判斷earlyProxyReferences的Set容器中是否有這個cacheKey,這個cacheKey就是類的名稱,而這個容器在提前暴露的三級緩存獲取實例的時候就已經(jīng)設(shè)置進(jìn)去了,所以Set容器中是有這個類的
所以3的AOP入口這里會原樣返回Bean,如圖:


OK,有意思的來了,這時候就輪到4這個BeanPostProcessor的異步切面的AOP入口執(zhí)行了。如圖:


在這里就返回了bean的異步切面代理,實例如圖:


我解釋一下這個截圖內(nèi)容,
exposedObject是異步代理對象,在targetSource是代理對象的目標(biāo)對象,目標(biāo)對象中有一個transationService屬性,這個屬性是一個事務(wù)的代理對象,OK,從這里我們發(fā)現(xiàn),我去,一個同樣的類,居然生成了兩個不同的代理對象,一個是異步的代理對象,一個是事務(wù)的代理對象,代理對象居然不一致了。為什么會這樣,前面我已經(jīng)分享得很清楚了

然后在spring中,這種情況默認(rèn)是不被允許的,代碼如下:

if (earlySingletonExposure) {
			Object earlySingletonReference = getSingleton(beanName, false);
			if (earlySingletonReference != null) {
				if (exposedObject == bean) {
					exposedObject = earlySingletonReference;
				}
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
					for (String dependentBean : dependentBeans) {
						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
							actualDependentBeans.add(dependentBean);
						}
					}
					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.");
					}
				}
			}
		}

Object earlySingletonReference = getSingleton(beanName, false);
這里我們前面分析過,這里會從三級緩存中獲取到事務(wù)代理對象

if (exposedObject == bean) {
	exposedObject = earlySingletonReference;
}

然后這里有個if判斷,bean是第一次實例化的bean,是沒被initializeBean代理之前的bean


而exposedObject對象是一個異步切面的代理對象


這里兩者是不相等的,而這個變量默認(rèn)是allowRawInjectionDespiteWrapping=false的


所有這里就會拋異常,就是文章前面的那個異常,所有我們找到了為什么會有這么一個異常的出現(xiàn)了。
其實要解決這個異常也比較簡單,只要把a(bǔ)llowRawInjectionDespiteWrapping這個屬性變成true就行了。
如何變了,代碼如下:


這是這個變量就為true了 ,就不會拋異常了


但是就會存在一個現(xiàn)象,單元測試中獲取到的bean對象和類中依賴注入的對象不是同一個了
這個bean對象是異步代理對象


類中屬性的對象是事務(wù)切面的代理對象


有意思吧,哈哈 。

如果在類里面沒有@Async異步注解,其實就不會有問題,默認(rèn)是允許單例循環(huán)依賴的,為什么沒問題

@Service("transationServiceImpl")
public class TransationServiceImpl implements TransationService {

  @Autowired
  TransationService transationService;

  @Transactional
  @Override
  public void transation() {
    System.out.println(transationService.hashCode());
    System.out.println("s");
  }
}

因為

if (earlySingletonExposure) {
			Object earlySingletonReference = getSingleton(beanName, false);
			if (earlySingletonReference != null) {
				if (exposedObject == bean) {
					exposedObject = earlySingletonReference;
				}
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
					for (String dependentBean : dependentBeans) {
						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
							actualDependentBeans.add(dependentBean);
						}
					}
					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.");
					}
				}
			}
		}

如果只要存在循環(huán)依賴,第一次業(yè)務(wù)類實例化的時候代理對象就是從這里獲取的


這個地方

//bean 實例化+ioc依賴注入完以后的調(diào)用,非常重要,重要程度:5
			exposedObject = initializeBean(beanName, exposedObject, mbd);

由于三級緩存中建立了緩存了


所以會直接返回對應(yīng)的bean,沒有生成代理。代理對象是從這個獲取的


是從提前暴露的三級緩存中獲取的代理對象賦值給了第一次實例化的bean對象,所以這個else if中可能出現(xiàn)異常的地方就不會走了,因為這兩個bean exposedObject 和 bean是相等的。

到此這篇關(guān)于帶有@Transactional和@Async的循環(huán)依賴問題的解決的文章就介紹到這了,更多相關(guān)@Transactional和@Async的循環(huán)依賴內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 關(guān)于Java跨域Json字符轉(zhuǎn)類對象的方法示例

    關(guān)于Java跨域Json字符轉(zhuǎn)類對象的方法示例

    這篇文章主要給大家介紹了關(guān)于Java跨域Json字符轉(zhuǎn)類對象的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-11-11
  • Java中抽象類和接口的區(qū)別_動力節(jié)點(diǎn)Java學(xué)院整理

    Java中抽象類和接口的區(qū)別_動力節(jié)點(diǎn)Java學(xué)院整理

    java抽象類和接口最本質(zhì)的區(qū)別是接口里不能實現(xiàn)方法--接口中的方法全是抽象方法。抽象類中可實現(xiàn)方法--抽象類中的方法可以不是抽象方法,下文給大家簡單介紹下,需要的的朋友參考下
    2017-04-04
  • Java File類的詳解及簡單實例

    Java File類的詳解及簡單實例

    這篇文章主要介紹了Java File類的詳解及簡單實例的相關(guān)資料,希望通過本文大家能夠掌握這部分內(nèi)容,需要的朋友可以參考下
    2017-09-09
  • Java各種比較對象的方式的對比總結(jié)

    Java各種比較對象的方式的對比總結(jié)

    比較對象是面向?qū)ο缶幊陶Z言的一個基本特征.在本教程中,我們將介紹Java語言的一些特性,這些特性允許我們比較對象.此外,我們還將研究外部庫中的這些特性,需要的朋友可以參考下
    2021-06-06
  • 五分鐘帶你學(xué)會用java解析json字符串

    五分鐘帶你學(xué)會用java解析json字符串

    這篇文章主要給大家介紹了關(guān)于用java解析json字符串的相關(guān)資料,JSON是一種輕量級的、基于文本的、與語言無關(guān)的數(shù)據(jù)交換格式,易于人和機(jī)器讀寫,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-07-07
  • Mybatis如何直接執(zhí)行SQL語句

    Mybatis如何直接執(zhí)行SQL語句

    這篇文章主要介紹了Mybatis如何直接執(zhí)行SQL語句,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • JDK動態(tài)代理原理:只能代理接口,不能代理類問題

    JDK動態(tài)代理原理:只能代理接口,不能代理類問題

    這篇文章主要介紹了JDK動態(tài)代理原理:只能代理接口,不能代理類問題。具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • java中Properties文件加載和使用方法

    java中Properties文件加載和使用方法

    這篇文章主要為大家詳細(xì)介紹了java中Properties文件加載和使用方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-03-03
  • java中用數(shù)組實現(xiàn)環(huán)形隊列的示例代碼

    java中用數(shù)組實現(xiàn)環(huán)形隊列的示例代碼

    這篇文章主要介紹了java中用數(shù)組實現(xiàn)環(huán)形隊列的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • Java中Date和LocalDate區(qū)別與介紹

    Java中Date和LocalDate區(qū)別與介紹

    本文主要介紹了Java中Date和LocalDate區(qū)別與介紹,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07

最新評論

四子王旗| 广灵县| 隆安县| 西宁市| 周宁县| 鄯善县| 淮滨县| 龙岩市| 礼泉县| 焉耆| 宜城市| 安图县| 临桂县| 广州市| 江津市| 卓资县| 平顺县| 佛学| 衡南县| 崇明县| 大姚县| 拉孜县| 京山县| 依兰县| 工布江达县| 高陵县| 襄城县| 永城市| 阜新| 梓潼县| 垫江县| 淳安县| 靖远县| 华阴市| 邵武市| 台南县| 鹤岗市| 枝江市| 乌什县| 拜泉县| 顺昌县|