博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring BeanFactory、Bean和Context
阅读量:2207 次
发布时间:2019-05-04

本文共 23591 字,大约阅读时间需要 78 分钟。

版本说明:spring-5.2.8(然后,引用的官方文档基本是5.2.9的)

下面会结合Spring的源码,分析Bean的加载

(源码篇幅不小,光是网页上看的话体验不好,建议最好另外用文本编辑器查看)

1.BeanFactory、Bean和Context之间的关系

在了解加载过程之前,我觉得有必明确context和bean的关系,先看一个简单的getBean

ApplicationContext context =new ClassPathXmlApplicationContext("applicationContext.xml");		Hello hello=context.getBean("helloImpl",Hello.class);

我这里的例子就讲最简单的ClassPathXmlApplicationContext

This is a simple, one-stop shop convenience ApplicationContext. Consider using the GenericApplicationContext class in combination with an XmlBeanDefinitionReader for more flexible context setup.

关于context是什么可以参考

看似我们是从Context中取的bean

其实getBean()方法是context借助了BeanFactory

然后,得一步一步了解了。。。(这也是Java 继承被吐槽的原因了)

首先是ClassPathXmlApplicationContext的声明

public class ClassPathXmlApplicationContext

extends AbstractXmlApplicationContext

继承于AbstractXmlApplicationContext

public abstract class AbstractXmlApplicationContext

extends AbstractRefreshableConfigApplicationContext

然后,大家。。。自己看吧。。乱七八糟的继承关系

public abstract class AbstractRefreshableConfigApplicationContext

extends AbstractRefreshableApplicationContext
implements BeanNameAware, InitializingBean

public abstract class AbstractRefreshableApplicationContext

extends AbstractApplicationContext

public abstract class AbstractApplicationContext

extends DefaultResourceLoader
implements ConfigurableApplicationContext

public interface ConfigurableApplicationContext

extends ApplicationContext, Lifecycle, Closeable

public interface ApplicationContext

extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver

public interface ListableBeanFactory

extends BeanFactory

概括一下,就是ClassPathXmlApplicationContext实现了Spring的ApplicationContext接口

然后ApplicationContext接口又继承有BeanFactory,包括了前面提到的getBean()方法

那么,我们就可以不通过BeanFactory

而是用context去获取Bean了~

(getBeab()具体的实现,就在那乱七八糟的某个类中)

而且官方也是推荐这么做(Spring 5.2.x的文档)

Because an ApplicationContext includes all the functionality of a BeanFactory, it is generally recommended over a plain BeanFactory, except for scenarios where full control over bean processing is needed.


2.BeanFactory的具体实现

我这里只是一最简单的ClassPathXmlApplicationContext为例

先看看ClassPathXmlApplicationContext的源码

public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {
configLocation}, true, null); }public ClassPathXmlApplicationContext(String... configLocations) throws BeansException {
this(configLocations, true, null); }public ClassPathXmlApplicationContext(String[] configLocations, @Nullable ApplicationContext parent) throws BeansException {
this(configLocations, true, parent); }public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException {
this(configLocations, refresh, null); }public ClassPathXmlApplicationContext( String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
super(parent); setConfigLocations(configLocations); if (refresh) {
refresh(); } }

这是对于传入参数只有一个关于xml的文件路径的构造方法

主要的功能代码是

setConfigLocations(configLocations);refresh();

setConfigLocations()的实现是在这个类里

public abstract class AbstractRefreshableConfigApplicationContext extends AbstractRefreshableApplicationContext		implements BeanNameAware, InitializingBean

它做的事情就是确定配置文件的位置,具体的我不展开了

然后是refresh()

r e f r e s h ( ) 方 法 看 着 简 单 , 其 实 特 别 重 要 ! ! \color{red}{refresh()方法看着简单,其实特别重要!!} refresh()

Load or refresh the persistent representation of the configuration, which might be from Java-based configuration, an XML file, a properties file, a relational database schema, or some other format.

refresh()实现是在这个类

public abstract class AbstractApplicationContext extends DefaultResourceLoader		implements ConfigurableApplicationContext

然后是refresh()的实现代码:

public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 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); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // 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(); } } }

这里我要展开的是obtainFreshBeanFactory();方法,因为涉及到BeanFactory的创建

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory(); return getBeanFactory(); }

这里refreshBeanFactory()的实现在这个类(又是嫌弃继承的一天)

public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans(); closeBeanFactory(); } try {
DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); loadBeanDefinitions(beanFactory); this.beanFactory = beanFactory; } catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }

追溯到这里,足够了

若已有BeanFactory存在,则会销毁原来的Bean,然后重新new一个

我个人的理解是,BeanFactory不允许迭代,你的配置文件如何,生成出来的BeanFactory就是如何

其中loadBeanDefinitions() 是加载Bean的配置

实现在PropertiesBeanDefinitionReader、XmlBeanDefinitionReader

PropertiesBeanDefinitionReader

Bean definition reader for a simple properties format.

(从properties文件中获取bean的配置信息)

public class PropertiesBeanDefinitionReaderextends AbstractBeanDefinitionReader

XmlBeanDefinitionReader

Bean definition reader for XML bean definitions.

public class XmlBeanDefinitionReaderextends AbstractBeanDefinitionReader

好啦!

得到新的BeanFactory和加载好配置信息后,就继续refresh()的一系列操作

调用的各个函数都有注释说明,我不继续展开了


3.Bean 的加载

而关于getbean()的具体实现,又是牵扯到乱七八糟的继承,做好心理准备(跪了,小声bb)

根据前面的代码

DefaultListableBeanFactory beanFactory = createBeanFactory()

我 在 D e f a u l t L i s t a b l e B e a n F a c t o r y 中 找 到 了 g e t B e a n ( ) 的 实 现 \color{red}{我在DefaultListableBeanFactory中找到了getBean()的实现} DefaultListableBeanFactorygetBean()

但这其实很不是很直观,因为这看起来很简单

但实际上其中调用的方法还另外牵扯到很多

后面讲到Bean的加载的时候会再细讲

public class DefaultListableBeanFactoryextends AbstractAutowireCapableBeanFactoryimplements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable
public 
T getBean(Class
requiredType) throws BeansException {
return getBean(requiredType, (Object[]) null); } public
T getBean(Class
requiredType, @Nullable Object... args) throws BeansException {
Assert.notNull(requiredType, "Required type must not be null"); Object resolved = resolveBean(ResolvableType.forRawClass(requiredType), args, false); if (resolved == null) {
throw new NoSuchBeanDefinitionException(requiredType); } return (T) resolved; }

其中的resolveBean方法,也在DefaultListableBeanFactory中

这个方法是为Bean实例化做准备

private 
T resolveBean(ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) {
NamedBeanHolder
namedBean = resolveNamedBean(requiredType, args, nonUniqueAsNull); if (namedBean != null) {
return namedBean.getBeanInstance(); } BeanFactory parent = getParentBeanFactory(); if (parent instanceof DefaultListableBeanFactory) {
return ((DefaultListableBeanFactory) parent).resolveBean(requiredType, args, nonUniqueAsNull); } else if (parent != null) {
ObjectProvider
parentProvider = parent.getBeanProvider(requiredType); if (args != null) {
return parentProvider.getObject(args); } else {
return (nonUniqueAsNull ? parentProvider.getIfUnique() : parentProvider.getIfAvailable()); } } return null; }

其中的resolveNamedBean,也在DefaultListableBeanFactory中

public 
NamedBeanHolder
resolveNamedBean(Class
requiredType) throws BeansException {
Assert.notNull(requiredType, "Required type must not be null"); NamedBeanHolder
namedBean = resolveNamedBean(ResolvableType.forRawClass(requiredType), null, false); if (namedBean != null) {
return namedBean; } BeanFactory parent = getParentBeanFactory(); if (parent instanceof AutowireCapableBeanFactory) {
return ((AutowireCapableBeanFactory) parent).resolveNamedBean(requiredType); } throw new NoSuchBeanDefinitionException(requiredType); }private
NamedBeanHolder
resolveNamedBean( ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) throws BeansException { Assert.notNull(requiredType, "Required type must not be null"); String[] candidateNames = getBeanNamesForType(requiredType); if (candidateNames.length > 1) { List
autowireCandidates = new ArrayList<>(candidateNames.length); for (String beanName : candidateNames) { if (!containsBeanDefinition(beanName) || getBeanDefinition(beanName).isAutowireCandidate()) { autowireCandidates.add(beanName); } } if (!autowireCandidates.isEmpty()) { candidateNames = StringUtils.toStringArray(autowireCandidates); } } if (candidateNames.length == 1) { String beanName = candidateNames[0]; return new NamedBeanHolder<>(beanName, (T) getBean(beanName, requiredType.toClass(), args)); } else if (candidateNames.length > 1) { Map
candidates = new LinkedHashMap<>(candidateNames.length); for (String beanName : candidateNames) { if (containsSingleton(beanName) && args == null) { Object beanInstance = getBean(beanName); candidates.put(beanName, (beanInstance instanceof NullBean ? null : beanInstance)); } else { candidates.put(beanName, getType(beanName)); } } String candidateName = determinePrimaryCandidate(candidates, requiredType.toClass()); if (candidateName == null) { candidateName = determineHighestPriorityCandidate(candidates, requiredType.toClass()); } if (candidateName != null) { Object beanInstance = candidates.get(candidateName); if (beanInstance == null || beanInstance instanceof Class) { beanInstance = getBean(candidateName, requiredType.toClass(), args); } return new NamedBeanHolder<>(candidateName, (T) beanInstance); } if (!nonUniqueAsNull) { throw new NoUniqueBeanDefinitionException(requiredType, candidates.keySet()); } } return null; }

要注意,上面resolveNamedBean中

getBean(beanName, requiredType.toClass(), args))

调用的并不是最开始getBean

因为参数类型不对!beanName是String类型的

(我看得时候在这卡了很久,这怎么可以构成一个回路?)

这 里 调 的 g e t B e a n 的 实 现 其 实 在 下 面 这 个 类 中 \color{red}{这里调的getBean的实现其实在下面这个类中} getBean

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory
@Override	public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false); } @Override public
T getBean(String name, Class
requiredType) throws BeansException {
return doGetBean(name, requiredType, null, false); } @Override public Object getBean(String name, Object... args) throws BeansException {
return doGetBean(name, null, args, false); } public
T getBean(String name, @Nullable Class
requiredType, @Nullable Object... args) throws BeansException {
return doGetBean(name, requiredType, args, false); } @SuppressWarnings("unchecked") protected
T doGetBean( String name, @Nullable Class
requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { String beanName = transformedBeanName(name); Object bean; // Eagerly check singleton cache for manually registered singletons. 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 + "'"); } } bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { // Fail if we're already creating this bean instance: // We're assumably within a circular reference. if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } // Check if bean definition exists in this factory. BeanFactory parentBeanFactory = getParentBeanFactory(); if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { // Not found -> check parent. String nameToLookup = originalBeanName(name); if (parentBeanFactory instanceof AbstractBeanFactory) { return ((AbstractBeanFactory) parentBeanFactory).doGetBean( nameToLookup, requiredType, args, typeCheckOnly); } else if (args != null) { // Delegation to parent with explicit args. return (T) parentBeanFactory.getBean(nameToLookup, args); } else if (requiredType != null) { // No args -> delegate to standard getBean method. return parentBeanFactory.getBean(nameToLookup, requiredType); } else { return (T) parentBeanFactory.getBean(nameToLookup); } } if (!typeCheckOnly) { markBeanAsCreated(beanName); } try { RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); checkMergedBeanDefinition(mbd, beanName, args); // Guarantee initialization of beans that the current bean depends on. String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dep : dependsOn) { if (isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } registerDependentBean(dep, beanName); try { getBean(dep); } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", ex); } } } // 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; } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } else if (mbd.isPrototype()) { // It's a prototype -> create a new instance. Object prototypeInstance = null; try { beforePrototypeCreation(beanName); prototypeInstance = createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } else { String scopeName = mbd.getScope(); if (!StringUtils.hasLength(scopeName)) { throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'"); } Scope scope = this.scopes.get(scopeName); if (scope == null) { throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'"); } try { Object scopedInstance = scope.get(beanName, () -> { beforePrototypeCreation(beanName); try { return createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } }); bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } catch (IllegalStateException ex) { throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current thread; consider " + "defining a scoped proxy for this bean if you intend to refer to it from a singleton", ex); } } } catch (BeansException ex) { cleanupAfterBeanCreationFailure(beanName); throw ex; } } // Check if required type matches the type of the actual bean instance. if (requiredType != null && !requiredType.isInstance(bean)) { try { T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType); if (convertedBean == null) { throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } return convertedBean; } catch (TypeMismatchException ex) { if (logger.isTraceEnabled()) { logger.trace("Failed to convert bean '" + name + "' to required type '" + ClassUtils.getQualifiedName(requiredType) + "'", ex); } throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } } return (T) bean; }

篇幅很长,简单的说就是

B e a n F a c t o r y 会 按 照 S c o p e 的 类 型 来 返 回 b e a n 对 象 \color{red}{BeanFactory会按照Scope的类型来返回bean对象} BeanFactoryScopebean

Singleton、Prototype等等

对于Singleton

BeanFactory会有一个singletonObjects用来存储

private final Map
singletonObjects

我们则根据beanname来取对应的对象


对于Prototype,则会根据BeanDefinition(即配置信息,在Spring是个接口)

新建一个Bean对象,然后返回

即Prototype 类型的Bean是在调用getBean()时才进行加载的

最后再讲讲Singleton 类型的Bean 的加载

上面说到Singleton 类型的Bean是在singletonObjects中取的

那其中的Bean对象又是什么时候加载的呢?

r e f r e s h ( ) \color{red}{refresh()} refresh()中的finishBeanFactoryInitialization()

// Instantiate all remaining (non-lazy-init) singletons.finishBeanFactoryInitialization(beanFactory);
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// Initialize conversion service for this context. if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) && beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService( beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)); } // Register a default embedded value resolver if no bean post-processor // (such as a PropertyPlaceholderConfigurer bean) registered any before: // at this point, primarily for resolution in annotation attribute values. if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal)); } // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early. String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false); for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName); } // Stop using the temporary ClassLoader for type matc hing. beanFactory.setTempClassLoader(null); // Allow for caching all bean definition metadata, not expecting further changes. beanFactory.freezeConfiguration(); // Instantiate all remaining (non-lazy-init) singletons. beanFactory.preInstantiateSingletons(); }

再次向继承跪下。。。

preInstantiateSingletons()的实现在之前出现过的

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
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 FactoryBean) {
FactoryBean
factory = (FactoryBean
) bean; boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged( (PrivilegedAction
) ((SmartFactoryBean
) factory)::isEagerInit, getAccessControlContext()); } else {
isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean
) factory).isEagerInit()); } if (isEagerInit) {
getBean(beanName); } } } else {
getBean(beanName); } } } // Trigger post-initialization callback for all applicable beans... for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName); if (singletonInstance instanceof SmartInitializingSingleton) {
SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance; if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction
) () -> {
smartSingleton.afterSingletonsInstantiated(); return null; }, getAccessControlContext()); } else {
smartSingleton.afterSingletonsInstantiated(); } } } }

其调用的一个很重要的getSingleton()的实现则是在

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
public Object getSingleton(String beanName) {
return getSingleton(beanName, true); } @Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) {
ObjectFactory
singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) {
singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; }

Ps:preInstantiateSingletons()调用getSingleton()传入的参数只有一个String

同时之前提及的singletonObjects就在DefaultSingletonBeanRegistry这个类中

好的,又是概括环节

最主要的就是getSingleton()这个方法

如果在singletonObjects中不存在我们想要的Singleton类型的Bean

(说明当前这个Singleton是初始化时加载的)

则给singletonObjects上锁,新建一个Bean对象,然后把Bean对象存入earlySingletonObjects

注 意 区 分 e a r l y S i n g l e t o n O b j e c t s 和 s i n g l e t o n O b j e c t s \color{red}{注意区分earlySingletonObjects和singletonObjects} earlySingletonObjectssingletonObjects

Singleton有一个Lazy initialization机制

就是像prototype一样,等到需要的时候才加载

那么这类lazy-init的Bean就会被放在singletonObjects

而默认下的Singleton是Eager initialization机制,即在应用启动的时加载

会被放在earlySingletonObjects中


好啦,以上~

总结:

Context拥有BeanFactory的全部功能,还有拓展,所以我们一般通过Context来获取Bean

BeanFactory 通过refresh() 完成建立

Bean的加载按照Scope的类型Singleton和Prototype有不同的加载机制


留意当前类是否为抽象类或接口

若方法在当前类中没有实现则到其父类中去找

若方法是抽象方法,则在子类中找实现

还是得吐槽一下Java的继承,太苦啦~

转载地址:http://nliyb.baihongyu.com/

你可能感兴趣的文章
【数据结构】有关二叉树的面试题
查看>>
【Linux】内核态和用户态
查看>>
【Linux】HTTP的理解
查看>>
【Linux】HTTPS的理解
查看>>
【操作系统】大小端问题
查看>>
Git上传代码时碰到的问题及解决方法
查看>>
【Linux】vim的简单配置
查看>>
【C++】智能指针
查看>>
【C++】const修饰的成员函数
查看>>
【C++】面向对象的三大特性
查看>>
【C++】智能指针(后续)
查看>>
【C】堆区和栈区的区别
查看>>
【linux】send和recv函数解析
查看>>
【Linux】线程安全的单例模式以及计算密集型线程和IO密集型线程
查看>>
一次完整的HTTP请求是怎样的??
查看>>
【C++】常见的内存泄漏及解决方法
查看>>
【C++】const 指针与指向const的指针
查看>>
【Linux】多线程和多进程 及其应用场景
查看>>
【C++】构造函数中必须通过初始化列表来进行初始化情况
查看>>
【算法】对于大数的操作
查看>>