当前位置:

首页 > 编程开发 > SpringBoot源码之Bean的生命周期是什么

SpringBoot源码之Bean的生命周期是什么

入口方法为SpringApplication#run()1.SpringApplication#run()/***RuntheSpringapplication,creatingandrefreshinganew*{@linkApplicationContext}.*@paramargstheapplicationarguments(usuallypassedfromaJavamainmethod)*@returnarunning{@linkApplicationContext}*/publicConfi

入口方法为SpringApplication#run()

1.SpringApplication#run()

/**
	 * 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()

@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()

@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 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();
			}
		}
	}

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

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()

@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()

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 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属性

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的初始化后置方法

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;  
}
本文内容来源于互联网,如有侵权请联系删除。
作者最新文章
编程开发
相关文章 更多
using namespace 使用中遇到的问题怎么解决
using namespace 使用中遇到的问题怎么解决

命名空间的基本概念与常见引入问题在C++等编程语言中,命名空间(namespace)是一种将代码标识符(如变量、函数、类名)封装在特定名称下的机制,其主要目的是避免命名冲突,尤其是在大型项目或使用多个第三方库时。使用“using namespace”指令可以将指定命名空间中的所有名称引入当前作用域,

c语言函数递归 实操经验总结:这些技巧很实用
c语言函数递归 实操经验总结:这些技巧很实用

理解递归的基本原理在C语言中,递归是一种函数调用自身的编程技术。要掌握它,首先需要理解其核心思想:将一个复杂的大问题,分解为一个或几个与原问题相似但规模更小的子问题,直到子问题足够简单,可以直接求解。这个过程通常包含两个关键部分:递归出口和递归体。递归出口定义了问题何时不再继续分解,即最简单、可直接

c语言函数递归 怎么选?常见方案对比分析
c语言函数递归 怎么选?常见方案对比分析

递归函数的基本概念与适用场景在C语言编程中,递归是一种函数调用自身的编程技巧。它并非适用于所有问题,但在处理某些具有自相似结构的问题时,能提供极其清晰和优雅的解决方案。递归的核心思想是将一个大规模问题分解为一个或多个同类型但规模更小的子问题,直到子问题简单到可以直接求解。典型的适用场景包括树形结构的

Objective-C 内存管理入门:从 alloc 到 dealloc 的生命周期详解
Objective-C 内存管理入门:从 alloc 到 dealloc 的生命周期详解

理解内存管理的基石在Objective-C的编程世界中,内存管理是开发者必须掌握的核心技能之一。它直接关系到应用的性能、稳定性与资源利用效率。与一些采用自动垃圾回收机制的语言不同,Objective-C在很长一段时间里,依赖一套基于引用计数的、需要开发者部分介入的管理规则。这套规则的核心思想是明确的

如何正确使用 dealloc 以避免 iOS 应用中的内存泄漏
如何正确使用 dealloc 以避免 iOS 应用中的内存泄漏

理解 dealloc 的角色与时机在 iOS 应用开发中,内存管理是保障应用性能与稳定性的基石。dealloc 方法是 Objective-C 中对象生命周期结束时的关键回调,它标志着对象即将被系统回收内存。正确理解其触发时机至关重要:当一个对象的引用计数降为零时,运行时系统会自动调用该对象的 de

深入理解 Objective-C 中的 dealloc 方法:内存管理核心机制
深入理解 Objective-C 中的 dealloc 方法:内存管理核心机制

内存管理的基石在Objective-C的世界里,内存管理是开发者必须掌握的核心技能之一。作为一门在手动引用计数(MRC)时代诞生的语言,Objective-C要求程序员对对象的生命周期有清晰的认识。dealloc方法正是这一生命周期中至关重要的终点站。它是一个实例方法,当对象的引用计数降为零时,系统

理解 native2ascii:Java 国际化开发中的字符编码工具
理解 native2ascii:Java 国际化开发中的字符编码工具

native2ascii 工具的基本定位在Ja va应用程序的国际化与本地化开发过程中,处理非拉丁字符集是一个常见且关键的环节。Ja va内部使用Unicode字符集来统一表示全球各种语言的文字,但其属性文件(.properties)在历史上要求使用ASCII编码,或者更准确地说,要求非ASCII字

如何使用 native2ascii 转换中文字符为 Unicode 转义序列
如何使用 native2ascii 转换中文字符为 Unicode 转义序列

理解 native2ascii 工具的基本用途在软件开发,特别是涉及国际化处理的场景中,开发者常常需要处理不同编码的文本资源。native2ascii 是 Ja va 开发工具包(JDK)中提供的一个命令行实用程序,其主要功能是将包含本地字符编码(非ASCII字符)的文件,转换为包含 Unicode

Java native2ascii 命令详解:解决属性文件乱码问题
Java native2ascii 命令详解:解决属性文件乱码问题

native2ascii 命令的由来与作用在Ja va开发中,处理国际化资源文件是一个常见需求。资源文件通常以.properties格式存储,用于支持多语言界面。然而,Ja va属性文件默认采用ISO-8859-1字符集编码,这导致了一个直接的问题:当文件中包含非拉丁字符(如中文、日文、韩文等)时,

一个 memwatch 实战案例:定位野指针问题
一个 memwatch 实战案例:定位野指针问题

内存监控工具的价值与挑战在软件开发,尤其是使用C/C++这类手动管理内存的语言时,内存错误是程序员最常遭遇的难题之一。其中,野指针问题因其隐蔽性和破坏性,往往成为最难定位的“幽灵”缺陷。它可能潜伏在代码中,在特定条件下才被触发,导致程序崩溃、数据损坏或难以预测的行为。传统的调试手段,如打印日志或使用

查看更多
精品专题 更多
装机必备
装机必备

正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

Windows
Windows

正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

macOS软件
macOS软件

正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

Mac软件 更多
灵活计算器
灵活计算器
macOS/iOS/Android

灵活计算器是一款笔记式算数应用,支持实时计算、动态关联和云端同步功能。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

赤友清理大师
赤友清理大师
macOS

赤友清理大师是一款为 Mac 设计的智能清理优化工具,可精准扫描垃圾、大文件、重复文件等,释放磁盘空间。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

极度公式
极度公式
Windows/macOS/Linux

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

WINDOWS 更多
Windows 10
Windows 10
Windows

Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。

极度公式
极度公式
Windows/macOS/Linux

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

密码键盘
密码键盘
Windows/macOS/iOS/Android

密码键盘是一款兼具安全性与便捷性的高效密码管理器。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。