一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - Java教程 - springboot bean循環(huán)依賴實現(xiàn)以及源碼分析

springboot bean循環(huán)依賴實現(xiàn)以及源碼分析

2021-09-16 12:11wang03 Java教程

最近在使用Springboot做項目的時候,遇到了一個循環(huán)依賴的 問題,所以下面這篇文章主要給大家介紹了關(guān)于springboot bean循環(huán)依賴實現(xiàn)以及源碼分析的相關(guān)資料,需要的朋友可以參考下

前言

本文基于springboot版本2.5.1

?
1
2
3
4
5
6
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.1</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

本文主要聚焦在循環(huán)依賴部分,主要用單例bean來進行講解,其他bean實現(xiàn)的流程不會過多涉及。

1、什么叫循環(huán)依賴呢

簡單來說就是springboot容器中的多個bean,如A、B兩個bean,A有屬性B需要注入,B有屬性A需要注入,形成相互依賴的情況。

看下代碼,就是類似下面這種情況

?
1
2
3
4
5
6
7
8
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
@Component
public class ServiceA {
    @Autowired
    private ServiceB serviceB;
}
?
1
2
3
4
5
6
7
8
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
@Component
public class ServiceB {
    @Autowired
    private ServiceA serviceA;
}

上面有兩個bean,分別是ServiceA,ServiceB。ServiceA中需要注入ServiceB的實例,ServiceB中需要注入ServiceA的實例,這就是一種典型的循環(huán)依賴,其他還有方法參數(shù)循環(huán)依賴的場景等等,但是它們的內(nèi)部實現(xiàn)基本是一樣的。

2、具體出現(xiàn)循環(huán)依賴的代碼邏輯

獲取bean的方法

在springboot中默認的beanFactory是DefaultListableBeanFactory,在我們獲取bean對象的時候,如果bean對象存在就直接返回,如果不存在,就先創(chuàng)建bean對象再返回。

我們先看下我們獲取bean的常用方法都有哪些

?
1
2
3
4
5
public <T> T getBean(Class<T> requiredType) throws BeansException
public Object getBean(String name) throws BeansException
public <T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException
public Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType)
public void preInstantiateSingletons() throws BeansException

常用的獲取bean的方法主要有上面幾個和它們的重載版本,對于第3行、第4行、第5行最終都會調(diào)用到第2行的方法來獲取bean。而它也會通過調(diào)用doGetBean(在AbstractBeanFactory這個類中)來獲取bean

?
1
2
3
public Object getBean(String name) throws BeansException {
 return doGetBean(name, null, null, false);
}

第1行的方法也會調(diào)用doGetBean來獲取bean

?
1
2
3
4
5
public <T> T getBean(String name, @Nullable Class<T> requiredType, @Nullable Object... args)
  throws BeansException {
 
 return doGetBean(name, requiredType, args, false);
}

所有最終獲取bean的方法都是

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

這個方法,這個方法是protected的,是不對外提供的。所以我們不能直接調(diào)用它,只能通過上面提供的5個方法來獲取bean對象。

下面我們從doGetBean這里來看下serviceA創(chuàng)建的過程

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
protected <T> T doGetBean(
  String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
  throws BeansException {
  //如果bean之前存在,這里返回的shareInstance就是非空,就會從后面的if分支中返回,如果bean之前不存在,就會執(zhí)行后面的bean創(chuàng)建及注入屬性的過程
  Object sharedInstance = getSingleton(beanName);
  if (sharedInstance != null && args == null) {
  ......
  //如果當前不只是檢查,而且是創(chuàng)建bean,這個參數(shù)就是false,在這里就會做個bean創(chuàng)建的標記,把beanName 加到alreadyCreated里面去
  if (!typeCheckOnly) {
   markBeanAsCreated(beanName);
  }
   //我們當前要創(chuàng)建的bean是單例的,就會走到這里去,下面我們走到里面的調(diào)用去看看
   // 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;
     }
    });
    beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
   }
 
 }
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
 Assert.notNull(beanName, "Bean name must not be null");
 synchronized (this.singletonObjects) {
               ......
               //這里會把當前bean的名字加入到當前正在創(chuàng)建的單例對象集合singletonsCurrentlyInCreation中
   beforeSingletonCreation(beanName);
   ......
   try {
                   //這里就是調(diào)用上面的return createBean(beanName, mbd, args);這個方法,我們進這里面去看看
    singletonObject = singletonFactory.getObject();
    newSingleton = true;
   }
   ......
  }
  return singletonObject;
 }
}
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
  throws BeanCreationException {
 ......
 // Make sure bean class is actually resolved at this point, and
 // clone the bean definition in case of a dynamically resolved Class
 // which cannot be stored in the shared merged bean definition.
       //在這里獲取要創(chuàng)建的bean的class對象
 Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
 ......
 try {
           //調(diào)用這里來創(chuàng)建,我們再走到這里面去看看
           //3個參數(shù)分別為
           //1、beanName  bean對象的名字
           //2、mbdToUseRootBeanDefinition對象,可以認為就是bean的元數(shù)據(jù)信息,包含bean的類對象,bean的類上注解,bean實際位置路徑等等
           //3、args  bean對象的構(gòu)造方法的實參,這里一般是空的
  Object beanInstance = doCreateBean(beanName, mbdToUse, args);
  if (logger.isTraceEnabled()) {
   logger.trace("Finished creating instance of bean '" + beanName + "'");
  }
  return beanInstance;
 }
 ......
}
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
      throws BeanCreationException {
 
   ......
   //真正創(chuàng)建bean對象是在這里,這里返回的instanceWrapper是bean對象的類實例的包裝對象BeanWrapper
   if (instanceWrapper == null) {
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }
   //這里的bean就是實際創(chuàng)建的bean對象的類實例
   Object bean = instanceWrapper.getWrappedInstance();
   Class<?> beanType = instanceWrapper.getWrappedClass();
   if (beanType != NullBean.class) {
      mbd.resolvedTargetType = beanType;
   }
 ......
   // Eagerly cache singletons to be able to resolve circular references
   // even when triggered by lifecycle interfaces like BeanFactoryAware.
   //看上面的注釋大概也能明白, 大概意思就是早期的單例緩存,為了解決由 BeanFactoryAware等等觸發(fā)的循環(huán)依賴
   //mbd.isSingleton()  表示bean是單例的(這個是bean對應(yīng)的類上的,默認就是單例),
   //this.allowCircularReferences 允許循環(huán)引用,這個是beanFactory的成員屬性,默認也是true
   //isSingletonCurrentlyInCreation(beanName) 表示是否在當前正在創(chuàng)建的bean集合中。beforeSingletonCreation(beanName);我們在前面執(zhí)行過這句就加到正在創(chuàng)建的bean集合中了
   //這里earlySingletonExposure 就是true了,會進到if分支中
   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");
      }
      //這句主要是將將() -> getEarlyBeanReference(beanName, mbd, bean) 這個lambda表達式存儲到this.singletonFactories集合中
      addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
   }
 
   // Initialize the bean instance.
   Object exposedObject = bean;
   try {
      //在這里就會進行屬性填充,完成成員注入等等,也就是在這里serviceA這個bean會注入serviceB這個成員屬性,我們走進這個方法去看看
      populateBean(beanName, mbd, instanceWrapper);
      ......
   }
  ......
 
   return exposedObject;
}
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
 ......
 if (hasInstAwareBpps) {
  if (pvs == null) {
   pvs = mbd.getPropertyValues();
  }
  //真正的屬性注入是在這里完成的,aop也是在這里來完成的。這里是獲取beanFactory中的InstantiationAwareBeanPostProcessor對bean對象進行增強
  //如果屬性注入用的是@Resource,就會用CommonAnnotationBeanPostProcessor來完成
  //如果屬性注入用的是@Autowired,就會用AutowiredAnnotationBeanPostProcessor來完成
  //如果是AOP 就會使用InfrastructureAdvisorAutoProxyCreator來生成對應(yīng)的代理對象
  //我們這里使用的是@Autowired,所以會用AutowiredAnnotationBeanPostProcessor來完成注入。我們走到它的postProcessProperties的去看看
  for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
   PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
   ......
}
?
1
2
3
4
5
6
7
8
9
10
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
       //這里主要是獲取bean的類屬性和方法上的org.springframework.beans.factory.annotation.Autowired,org.springframework.beans.factory.annotation.Value注解來進行注入
 InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
 try {
           //繼續(xù)進去看看
  metadata.inject(bean, beanName, pvs);
 }
 ......
}
?
1
2
3
4
5
6
7
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
   ......
   //對每一個屬性分別進行注入,繼續(xù)進去
         element.inject(target, beanName, pvs);
      }
   }
}
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
 Field field = (Field) this.member;
 Object value;
 //如果之前緩存過就從緩存取,我們是第一次注入,所以之前沒有緩存,不會走這個分支
 if (this.cached) {
  try {
   value = resolvedCachedArgument(beanName, this.cachedFieldValue);
  }
  catch (NoSuchBeanDefinitionException ex) {
   // Unexpected removal of target bean for cached argument -> re-resolve
   value = resolveFieldValue(field, bean, beanName);
  }
 }
 else {
  //會走這里來解析字段的值,再進去
  value = resolveFieldValue(field, bean, beanName);
 }
 if (value != null) {
  ReflectionUtils.makeAccessible(field);
  field.set(bean, value);
 }
}
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Nullable
 private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
  //創(chuàng)建字段的包裝類DependencyDescriptor
  DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
 
  try {
   //調(diào)用這里完成對應(yīng)字段值的查找,再進去
   value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
  }
  catch (BeansException ex) {
   throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
  }
  synchronized (this) {
   //獲取到值之后,進行緩存
   if (!this.cached) {
     ......
    }
    this.cachedFieldValue = cachedFieldValue;
    this.cached = true;
   }
  }
  return value;
 }
}
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
  @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
 
 descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
 if (Optional.class == descriptor.getDependencyType()) {
  return createOptionalDependency(descriptor, requestingBeanName);
 }
 else if (ObjectFactory.class == descriptor.getDependencyType() ||
   ObjectProvider.class == descriptor.getDependencyType()) {
  return new DependencyObjectProvider(descriptor, requestingBeanName);
 }
 else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
  return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
 }
 else {
  //當前的類是一個普通的class,會走到這里面,由于我們的bean沒有Lazy注解,所以這里返回時null,走到下面的if分支
  Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
    descriptor, requestingBeanName);
  if (result == null) {
   //在這里我們看下這里的入?yún)ⅰ?/code>
   //descriptor是包含了需要注入的字段的信息。
   //requestingBeanName是當前正在創(chuàng)建的bean的名字serviceA,
   //autowiredBeanNames是當前需要注入的字段的對應(yīng)的bean的名字的集合,這里只有serviceB
   //typeConverter這個是進行注入時做類型轉(zhuǎn)換的,這里我們可以不用關(guān)注這個
   result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
  }
  return result;
 }
}
?
1
2
3
4
5
6
7
8
9
10
11
@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
  @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
  ......
  if (instanceCandidate instanceof Class) {
   //又會調(diào)用到這里,我們再進入到DependencyDescriptor的resolveCandidate去看看
               //注意:這里的autowiredBeanName是我們需要注入的屬性名這里是serviceB
   instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
  }
  ......
}
?
1
2
3
4
5
6
public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
  throws BeansException {
 //看到?jīng)],到這里就出現(xiàn)循環(huán)調(diào)用了,到這里又會重新調(diào)用beanFactory.getBean("serviceB")去創(chuàng)建serviceB的bean對象,完成后注入到serivceA對應(yīng)的Bean上的屬性上來,這時代碼又會從本節(jié)開頭的位置開始執(zhí)行,先創(chuàng)建serviceB對象實例,再去注入serviceB對象的serviceA屬性。
       //最終會執(zhí)行到beanFactory.getBean("serviceA")這里
 return beanFactory.getBean(beanName);
}

就是下面圖的樣子

springboot bean循環(huán)依賴實現(xiàn)以及源碼分析

3、解決循環(huán)依賴的代碼實現(xiàn)

接著上面的beanFactory.getBean("serviceA")這行代碼我們繼續(xù)往下看

這次又會走到這里

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
protected <T> T doGetBean(
  String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
  throws BeansException {
 //我們第二部分就是從這里開始的,又走回來了,但這次又會有所不同
 String beanName = transformedBeanName(name);
 Object beanInstance;
 
 // Eagerly check singleton cache for manually registered singletons.
 //這次我們這里返回的就不是空了,sharedInstance對象的值就是對應(yīng)serviceA的bean對象了,這次就會從if分支中返回,而之前我們不會進這里的if分支而是進入else分支導(dǎo)致后面出現(xiàn)了循環(huán)依賴的問題,這次我們進到這個方法看看
 Object sharedInstance = getSingleton(beanName);
 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 + "'");
   }
  }
  beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
 }
?
1
2
3
4
5
@Nullable
public Object getSingleton(String beanName) {
 //再點進去
 return getSingleton(beanName, true);
}
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
 // Quick check for existing instance without full singleton lock
 Object singletonObject = this.singletonObjects.get(beanName);
        //這里由于當前的serviceA bean還沒完成創(chuàng)建,所以這里singletonObject返回的是空,
       //再看看 isSingletonCurrentlyInCreation(beanName)這里,由于我們在創(chuàng)建serviceA過程中有這么一句beforeSingletonCreation(beanName)(不清楚這句的搜索下本文,上面就有講到),所有這個條件是true。這時我們就會進入if分支中
 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
  singletonObject = this.earlySingletonObjects.get(beanName);
          //由于我們是第一次進入這里,所以this.earlySingletonObjects.get(beanName)返回的也是null
          //我們的入?yún)?allowEarlyReference是true,會繼續(xù)進到這個if分支中
  if (singletonObject == null && allowEarlyReference) {
   synchronized (this.singletonObjects) {
    // Consistent creation of early reference within full singleton lock
    singletonObject = this.singletonObjects.get(beanName);
                   //這里的singletonObject還是null,繼續(xù)進到if分支
    if (singletonObject == null) {
     singletonObject = this.earlySingletonObjects.get(beanName);
     if (singletonObject == null) {
                           //最終會走到這里,在創(chuàng)建serviceA對象之后,屬性注入之前,執(zhí)行了這句 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))(不清楚的搜索下本文,上面有說到),所以這里返回的singletonFactory是個lamdba表達式,getEarlyBeanReference(beanName, mbd, bean))附帶了3個參數(shù),第一個beanName是serivceA,mdb是對應(yīng)serviceA的附帶serviceA元數(shù)據(jù)信息的RootBeanDefinition對象,bean就是創(chuàng)建出來的serviceA對象
      ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
      if (singletonFactory != null) {
                               //這里就會調(diào)用getEarlyBeanReference(beanName, mbd, bean)對serviceA對象進行一個getEarlyBeanReference增強后返回,返回后放置到earlySingletonObjects中,并從singletonFactories中刪除
       singletonObject = singletonFactory.getObject();
       this.earlySingletonObjects中,并從.put(beanName, singletonObject);
       this.singletonFactories.remove(beanName);
      }
     }
    }
   }
  }
 }
 return singletonObject;
}

最終在serviceA 這個bean創(chuàng)建完成后,就會從singletonsCurrentlyInCreation移除掉

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
   ......
   finally {
    //在這里從singletonsCurrentlyInCreation中移除掉
    afterSingletonCreation(beanName);
   }
   if (newSingleton) {
    //將serviceA bean對象添加到singletonObjects,registeredSingletons中
    //從singletonFactories,earlySingletonObjects中移除掉
    addSingleton(beanName, singletonObject);
   }
  }
  return singletonObject;
 }
}

所以整個獲取serviceA的流程就是這樣了,

1、首先去創(chuàng)建serviceA這個bean,

  • 由于它有個屬性serviceB,在創(chuàng)建完serviceA對象后,就會去進行serviceB的屬性注入,
  • 這時由于serviceB之前沒有生成,這時又會去創(chuàng)建serviceB這個bean,
  • 先創(chuàng)建serviceB對象,然后再進行serviceA這個屬性的注入,
  • 繼續(xù)去獲取serviceA這個bean,第二次進入獲取serviceA的流程,這時從之前緩存的lambda表達式中獲取到之前創(chuàng)建的serviceA的引用返回。

2、總結(jié)下關(guān)鍵的代碼點

  • 創(chuàng)建bean對象之前調(diào)用beforeSingletonCreation(beanName)將bean對象名字添加到singletonsCurrentlyInCreation集合中
  • 創(chuàng)建bean對象對應(yīng)的類實例后調(diào)用addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));添加到singletonFactories中
  • 在循環(huán)依賴中第二次調(diào)用到創(chuàng)建bean對象時,調(diào)用getSingleton(beanName, true)時,從singletonFactories中返回對應(yīng)的早期bean對象的引用,并添加到earlySingletonObjects中

總結(jié)

到此這篇關(guān)于springboot bean循環(huán)依賴實現(xiàn)以及源碼分析的文章就介紹到這了,更多相關(guān)springboot bean循環(huán)依賴內(nèi)容請搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!

原文鏈接:https://www.cnblogs.com/wbo112/p/14938678.html

延伸 · 閱讀

精彩推薦
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程Java實現(xiàn)搶紅包功能

    Java實現(xiàn)搶紅包功能

    這篇文章主要為大家詳細介紹了Java實現(xiàn)搶紅包功能,采用多線程模擬多人同時搶紅包,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙...

    littleschemer13532021-05-16
  • Java教程Java BufferWriter寫文件寫不進去或缺失數(shù)據(jù)的解決

    Java BufferWriter寫文件寫不進去或缺失數(shù)據(jù)的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進去或缺失數(shù)據(jù)的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望...

    spcoder14552021-10-18
  • Java教程升級IDEA后Lombok不能使用的解決方法

    升級IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級,尋思已經(jīng)有好久沒有升過級了。升級完畢重啟之后,突然發(fā)現(xiàn)好多錯誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關(guān)于小米推送Java代碼,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧...

    富貴穩(wěn)中求8032021-07-12
  • Java教程Java8中Stream使用的一個注意事項

    Java8中Stream使用的一個注意事項

    最近在工作中發(fā)現(xiàn)了對于集合操作轉(zhuǎn)換的神器,java8新特性 stream,但在使用中遇到了一個非常重要的注意點,所以這篇文章主要給大家介紹了關(guān)于Java8中S...

    阿杜7472021-02-04
  • Java教程20個非常實用的Java程序代碼片段

    20個非常實用的Java程序代碼片段

    這篇文章主要為大家分享了20個非常實用的Java程序片段,對java開發(fā)項目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程xml與Java對象的轉(zhuǎn)換詳解

    xml與Java對象的轉(zhuǎn)換詳解

    這篇文章主要介紹了xml與Java對象的轉(zhuǎn)換詳解的相關(guān)資料,需要的朋友可以參考下...

    Java教程網(wǎng)2942020-09-17
主站蜘蛛池模板: 日韩欧美国产一区 | 人生路不在线观看完整版 | а天堂中文最新版在线官网视频 | 四虎黄色影视库 | 亚洲热图 | 涩涩屋在线播放 | 男人狂躁女人下面狂叫图片 | 1986葫芦兄弟全集免费观看第十集 | free性泰国娇小videos | 韩剧在线观看 | 插入粉嫩| 日韩精品成人a在线观看 | 性色生活片在线观看 | 欧美日韩国产一区二区三区不卡 | 99久热只有精品视频免费观看17 | 香蕉久久夜色精品国产小优 | 草逼的视频| 国产成人手机在线好好热 | 国产深夜视频 | 亚洲欧美日韩另类在线一 | 国产二区精品视频 | 天天乐影院 | 毛片免费在线视频 | 男人猛进猛出女人下面视频 | 羞羞影院午夜男女爽爽影院网站 | 亚洲精品一区波多野结衣 | 波多野结在线观看 | 国产成+人+综合+亚洲不卡 | 脱了白丝校花的内裤猛烈进入 | 四虎影视在线看免费 720p | 欧美一区二区三区在线观看不卡 | 国产精品原创视频 | 久久国产伦子伦精品 | 999热这里只有精品 999久久久免费精品国产牛牛 | 精品久久久久久久久久香蕉 | 日韩欧美推理片免费看完整版 | 久草大 | 九色PORNY丨视频入口 | 精品久久成人免费第三区 | 女子监狱第二季未删减在线看 | 亚洲国产一区 |