服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - SpringBoot源码之Bean的生命周期

SpringBoot源码之Bean的生命周期

2023-04-14 16:25LUK流 Java教程

spring的bean的生命周期主要是创建bean的过程,一个bean的生命周期主要是4个步骤,实例化,属性注入,初始化,销毁,本文详细介绍了bean的生命周期,感兴趣的小伙伴可以参考阅读

入口方法为SpringApplication#run()

1.SpringApplication#run()

?
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
45
46
47
48
49
50
51
/**
     * Run the Spring application, creating and refreshing a new
     * {@link ApplicationContext}.
     * @param args the application arguments (usually passed from a Java main method)
     * @return a running {@link ApplicationContext}
     */
    public ConfigurableApplicationContext run(String... args) {
        long startTime = System.nanoTime();
        DefaultBootstrapContext bootstrapContext = createBootstrapContext();
        ConfigurableApplicationContext context = null;
        configureHeadlessProperty();
        SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting(bootstrapContext, this.mainApplicationClass);
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
            Banner printedBanner = printBanner(environment);
            context = createApplicationContext();
            context.setApplicationStartup(this.applicationStartup);
            prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
            refreshContext(context);
            afterRefresh(context, applicationArguments);
            Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
            }
            listeners.started(context, timeTakenToStartup);
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
            if (ex instanceof AbandonedRunException) {
                throw ex;
            }
            handleRunFailure(context, ex, listeners);
            throw new IllegalStateException(ex);
        }
        try {
            if (context.isRunning()) {
                Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
                listeners.ready(context, timeTakenToReady);
            }
        }
        catch (Throwable ex) {
            if (ex instanceof AbandonedRunException) {
                throw ex;
            }
            handleRunFailure(context, ex, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }

2.SpringApplication#run()=> SpringApplication#refreshContext(context)=> SpringApplication#refresh(context)=>ConfigurableApplicationContext#refresh()=>AbstractApplicationContext#refresh()

?
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
@Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
 
            // Prepare this context for refreshing.
            prepareRefresh();
 
            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
 
            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);
 
            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);
 
                StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);
 
                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);
                beanPostProcess.end();
 
                // Initialize message source for this context.
                initMessageSource();
 
                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();
 
                // Initialize other special beans in specific context subclasses.
                onRefresh();
 
                // Check for listener beans and register them.
                registerListeners();
 
                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);
 
                // Last step: publish corresponding event.
                finishRefresh();
            }
 
            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }
 
                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();
 
                // Reset 'active' flag.
                cancelRefresh(ex);
 
                // Propagate exception to caller.
                throw ex;
            }
 
            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
                contextRefresh.end();
            }
        }
    }

3.SpringApplication#run()=> SpringApplication#refreshContext(context)=> SpringApplication#refresh(context)=>ConfigurableApplicationContext#refresh()=>AbstractApplicationContext#refresh()=>AbstractApplicationContext#finishBeanFactoryInitialization()=>ConfigurableListableBeanFactory#preInstantiateSingletons()=>DefaultListableBeanFactory#preInstantiateSingletons()

?
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
@Override
    public void preInstantiateSingletons() throws BeansException {
        if (logger.isTraceEnabled()) {
            logger.trace("Pre-instantiating singletons in " + this);
        }
 
        // Iterate over a copy to allow for init methods which in turn register new bean definitions.
        // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
        List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
 
        // Trigger initialization of all non-lazy singleton beans...
        for (String beanName : beanNames) {
            RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
            if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
                if (isFactoryBean(beanName)) {
                    Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
                    if (bean instanceof SmartFactoryBean<?> smartFactoryBean && smartFactoryBean.isEagerInit()) {
                        getBean(beanName);
                    }
                }
                else {
                    // 此处就是初始化bean的方法
                    getBean(beanName);
                }
            }
        }
 
        // Trigger post-initialization callback for all applicable beans...
        for (String beanName : beanNames) {
            // 此处就是解决循环依赖的代码
            Object singletonInstance = getSingleton(beanName);
            if (singletonInstance instanceof SmartInitializingSingleton smartSingleton) {
                StartupStep smartInitialize = this.getApplicationStartup().start("spring.beans.smart-initialize")
                        .tag("beanName", beanName);
                smartSingleton.afterSingletonsInstantiated();
                smartInitialize.end();
            }
        }
    }

解决循环依赖的代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 尝试从缓存中获取成品的目标对象,如果存在,则直接返回
  Object singletonObject = this.singletonObjects.get(beanName);
  // 如果缓存中不存在目标对象,则判断当前对象是否已经处于创建过程中,在前面的讲解中,第一次尝试获取A对象
  // 的实例之后,就会将A对象标记为正在创建中,因而最后再尝试获取A对象的时候,这里的if判断就会为true
  if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
    synchronized (this.singletonObjects) {
      singletonObject = this.earlySingletonObjects.get(beanName);
      if (singletonObject == null && allowEarlyReference) {
        // 这里的singletonFactories是一个Map,其key是bean的名称,而值是一个ObjectFactory类型的
        // 对象,这里对于A和B而言,调用图其getObject()方法返回的就是A和B对象的实例,无论是否是半成品
        ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
        if (singletonFactory != null) {
          // 获取目标对象的实例
          singletonObject = singletonFactory.getObject();
          this.earlySingletonObjects.put(beanName, singletonObject);
          this.singletonFactories.remove(beanName);
        }
      }
    }
  }
  return singletonObject;
}
  • 一级缓存,singletonObjects 单例缓存,存储已经实例化的单例bean。
  • 二级缓存,earlySingletonObjects 提前暴露的单例缓存,这里存储的bean是刚刚构造完成,但还会通过属性注入bean。
  • 三级缓存,singletonFactories 生产单例的工厂缓存,存储工厂。

解决原理如下:

  • 在第一层中,先去获取 A 的 Bean,发现没有就准备去创建一个,然后将 A 的代理工厂放入“三级缓存”(这个 A 其实是一个半成品,还没有对里面的属性进行注入),但是 A 依赖 B 的创建,就必须先去创建 B;
  • 在第二层中,准备创建 B,发现 B 又依赖 A,需要先去创建 A,去创建 A,因为第一层已经创建了 A 的代理工厂,直接从“三级缓存”中拿到 A 的代理工厂,获取 A 的代理对象,放入“二级缓存”,并清除“三级缓存”;
  • 有了 A 的代理对象,对 A 的依赖完美解决(这里的 A 仍然是个半成品),B 初始化成功。在 B 初始化成功,完成 A 对象的属性注入,然后再填充 A 的其它属性,以及 A 的其它步骤(包括 AOP),完成对 A 完整的初始化功能(这里的 A 才是完整的 Bean)。
  • 将 A 放入“一级缓存”。

 

4.SpringApplication#run()=> SpringApplication#refreshContext(context)=> SpringApplication#refresh(context)=>ConfigurableApplicationContext#refresh()=>AbstractApplicationContext#refresh()=>AbstractApplicationContext#finishBeanFactoryInitialization()=>ConfigurableListableBeanFactory#preInstantiateSingletons()=>DefaultListableBeanFactory#preInstantiateSingletons()=>AbstractBeanFactory#getBean() => AbstractBeanFactory#doGetBean()=>AbstractBeanFactory#createBean()=>AbstractAutowireCapableBeanFactory#createBean()=>AbstractAutowireCapableBeanFactory#doCreateBean()

bean的生命周期:

1.调用InstantiationAwareBeanPostProcessor# postProcessBeforeInstantiation
跟进doCreateBean()

?
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
45
46
47
48
49
50
51
52
53
54
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) 
      throws BeanCreationException { 
  
   if (logger.isTraceEnabled()) { 
      logger.trace("Creating instance of bean '" + beanName + "'"); 
   
   RootBeanDefinition mbdToUse = mbd; 
  
   // 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.   Class<?> resolvedClass = resolveBeanClass(mbd, beanName); 
   if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) { 
      mbdToUse = new RootBeanDefinition(mbd); 
      mbdToUse.setBeanClass(resolvedClass); 
   
  
   // Prepare method overrides. 
   try
      mbdToUse.prepareMethodOverrides(); 
   
   catch (BeanDefinitionValidationException ex) { 
      throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(), 
            beanName, "Validation of method overrides failed", ex); 
   
  
   try
      // 1.调用InstantiationAwareBeanPostProcessor# postProcessBeforeInstantiation
      Object bean = resolveBeforeInstantiation(beanName, mbdToUse); 
      if (bean != null) { 
         return bean; 
      
   
   catch (Throwable ex) { 
      throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, 
            "BeanPostProcessor before instantiation of bean failed", ex); 
   
  
   try
      // 跟进doCreateBean()
      Object beanInstance = doCreateBean(beanName, mbdToUse, args); 
      if (logger.isTraceEnabled()) { 
         logger.trace("Finished creating instance of bean '" + beanName + "'"); 
      
      return beanInstance; 
   
   catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { 
      // A previously detected exception with proper bean creation context already, 
      // or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.      throw ex; 
   
   catch (Throwable ex) { 
      throw new BeanCreationException( 
            mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex); 
   
}

2.创建bean实例

跟进populateBean()
跟进initializeBean()

?
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) 
      throws BeanCreationException { 
  
   // Instantiate the bean. 
   BeanWrapper instanceWrapper = null
   if (mbd.isSingleton()) { 
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); 
   
   if (instanceWrapper == null) {
      // 2.创建bean实例
      instanceWrapper = createBeanInstance(beanName, mbd, args); 
   
   Object bean = instanceWrapper.getWrappedInstance(); 
   Class<?> beanType = instanceWrapper.getWrappedClass(); 
   if (beanType != NullBean.class) { 
      mbd.resolvedTargetType = beanType; 
   
  
   // Allow post-processors to modify the merged bean definition. 
   synchronized (mbd.postProcessingLock) { 
      if (!mbd.postProcessed) { 
         try {
            applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); 
         
         catch (Throwable ex) { 
            throw new BeanCreationException(mbd.getResourceDescription(), beanName, 
                  "Post-processing of merged bean definition failed", ex); 
         
         mbd.markAsPostProcessed(); 
      
   
  
   // Eagerly cache singletons to be able to resolve circular references 
   // even when triggered by lifecycle interfaces like BeanFactoryAware.   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"); 
      
      addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); 
   
  
   // Initialize the bean instance. 
   Object exposedObject = bean; 
   try {
      // 跟进populateBean()
      populateBean(beanName, mbd, instanceWrapper);
      // 跟进initializeBean()
      exposedObject = initializeBean(beanName, exposedObject, mbd); 
   
   catch (Throwable ex) { 
      if (ex instanceof BeanCreationException bce && beanName.equals(bce.getBeanName())) { 
         throw bce; 
      
      else
         throw new BeanCreationException(mbd.getResourceDescription(), beanName, ex.getMessage(), 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 "
                     "'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example."); 
            
         
      
   
  
   // Register bean as disposable. 
   try
      registerDisposableBeanIfNecessary(beanName, bean, mbd); 
   
   catch (BeanDefinitionValidationException ex) { 
      throw new BeanCreationException( 
            mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); 
   
  
   return exposedObject; 
}

3.调用InstantiationAwareBeanPostProcessor# postProcessAfterInstantiation

4.注入bean属性

?
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { 
   if (bw == null) { 
      if (mbd.hasPropertyValues()) { 
         throw new BeanCreationException( 
               mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance"); 
      
      else
         // Skip property population phase for null instance. 
         return
      
   
  
   if (bw.getWrappedClass().isRecord()) { 
      if (mbd.hasPropertyValues()) { 
         throw new BeanCreationException( 
               mbd.getResourceDescription(), beanName, "Cannot apply property values to a record"); 
      
      else
         // Skip property population phase for records since they are immutable. 
         return
      
   
  
      // 3.调用InstantiationAwareBeanPostProcessor# postProcessAfterInstantiation
      for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { 
         if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { 
            return
         
      
   
  
   PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null); 
  
   int resolvedAutowireMode = mbd.getResolvedAutowireMode(); 
   if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) { 
      MutablePropertyValues newPvs = new MutablePropertyValues(pvs); 
      // Add property values based on autowire by name if applicable. 
      if (resolvedAutowireMode == AUTOWIRE_BY_NAME) { 
         autowireByName(beanName, mbd, bw, newPvs); 
      
      // Add property values based on autowire by type if applicable. 
      if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) { 
         autowireByType(beanName, mbd, bw, newPvs); 
      
      pvs = newPvs; 
   
   if (hasInstantiationAwareBeanPostProcessors()) { 
      if (pvs == null) {
         pvs = mbd.getPropertyValues(); 
      
      for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { 
         PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName); 
         if (pvsToUse == null) { 
            return
         
         pvs = pvsToUse; 
      
   
  
   boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); 
   if (needsDepCheck) { 
      PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); 
      checkDependencies(beanName, mbd, filteredPds, pvs); 
   
  
   if (pvs != null) {
      // 4.注入属性 
      applyPropertyValues(beanName, mbd, bw, pvs); 
   
}

5.设置Aware接口的属性

6.调用BeanPostProcessor的初始化前置方法

7.先((InitializingBean) bean).afterPropertiesSet(),后调用init-method方法,进行初始化操作

8.调用BeanPostProcessor的初始化后置方法

?
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
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    // 5.设置Aware接口的属性
   invokeAwareMethods(beanName, bean); 
  
   Object wrappedBean = bean; 
   if (mbd == null || !mbd.isSynthetic()) {
      // 5.调用BeanPostProcessor的初始化前置方法 
      wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); 
   
  
   try {
      // 6.调用init-method方法,进行初始化操作 
      invokeInitMethods(beanName, wrappedBean, mbd); 
   
   catch (Throwable ex) { 
      throw new BeanCreationException( 
            (mbd != null ? mbd.getResourceDescription() : null), beanName, ex.getMessage(), ex); 
   
   if (mbd == null || !mbd.isSynthetic()) {
      // 7. 调用BeanPostProcessor的初始化后置方法
      wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); 
   
  
   return wrappedBean; 
}

以上就是SpringBoot源码之Bean的生命周期的详细内容,更多关于SpringBoot Bean生命周期的资料请关注服务器之家其它相关文章!

原文链接:https://blog.csdn.net/qq_35688140/article/details/130117398

延伸 · 阅读

精彩推荐
  • Java教程JBoss5.x下配置Log4j方法介绍

    JBoss5.x下配置Log4j方法介绍

    这篇文章主要介绍了JBoss5.x下配置Log4j方法介绍,小编觉得挺不错的,这里分享给大家,供需要的朋友参考。...

    于亮12302021-01-22
  • Java教程浅谈Java锁机制

    浅谈Java锁机制

    在多线程环境下,程序往往会出现一些线程安全问题,为此,Java提供了一些线程的同步机制来解决安全问题,比如:synchronized锁和Lock锁都能解决线程安全...

    随身电源10812022-01-25
  • Java教程Java并发编程之对象的共享

    Java并发编程之对象的共享

    这篇文章主要介绍了Java并发编程之对象的共享,介绍如何共享和发布对象,使它们被安全地由多个进程访问。需要的小伙伴可以参考一下...

    万猫学社9042022-11-08
  • Java教程SpringMVC中MultipartFile上传获取图片的宽度和高度详解

    SpringMVC中MultipartFile上传获取图片的宽度和高度详解

    本篇文章主要介绍了SpringMVC中MultipartFile上传获取图片的宽度和高度,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    DT部落7422020-09-29
  • Java教程详解Java的MyBatis框架中的事务处理

    详解Java的MyBatis框架中的事务处理

    利用MyBatis框架的配置管理比直接使用JDBC API编写事务控制要来得更加轻松,这里我们就来详解Java的MyBatis框架中的事务处理,尤其是和Spring框架集成后更加ex...

    fhd00114172020-05-08
  • Java教程Java实例讲解多态数组的使用

    Java实例讲解多态数组的使用

    本文章向大家介绍Java多态数组,主要包括Java多态数组使用实例、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下...

    Demo龙6972022-12-16
  • Java教程java中volatile不能保证线程安全(实例讲解)

    java中volatile不能保证线程安全(实例讲解)

    下面小编就为大家带来一篇java中volatile不能保证线程安全(实例讲解)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看...

    Think-00710682020-12-26
  • Java教程JDK8环境中使用struts2的步骤详解

    JDK8环境中使用struts2的步骤详解

    这篇文章主要给大家介绍了关于在JDK8环境中使用struts2的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,...

    kongwenqiang10712021-06-14