AbstractBeanFactory.doGetBean

protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
//转换beanName,应为有可能是别名
final 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.isDebugEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
//如果是BeanFactory,那么需要通过getObject方法获取真正的对象
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
} else {
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
//如果是多例的,通过ThreadLocal判断是否循环引用,如果是直接报错
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
} // Check if bean definition exists in this factory.
BeanFactory parentBeanFactory = getParentBeanFactory();
//如果有父容器,并且当前容器不存在beanName,才会从父容器中寻找
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (args != null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}
//如果不是仅仅进行类型检查,那么将这个beanName存入alreadyCreated这个set集合,标记它已经创建或即将创建,并且从mergedBeanDefinitions map集合中移除
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
} try {
//合并beanDefinition,如果有父子级,那么 就会把父beanDefinition的属性合并到一起,最后变成RootBeanDefinition
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
//如果这个RootBeanDefinition是抽象的,那么就直接抛错
checkMergedBeanDefinition(mbd, beanName, args); // Guarantee initialization of beans that the current bean depends on.
//如果在xml中配置了当前bean依赖其他的bean,如果发现这种依赖被循环,直接抛错,比如a -> b c d 然后 c - a f y,当spring发现c依赖a f y之后,就去创建a,然后发现a已经c这个依赖了,那么就报错,这种通过xml配置的依赖,可能是注入依赖,也有可能是必须某个bean先创建的依赖(就像是ant里的某个任务依赖另一个任务,这种是不允许早期依赖的)
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dependsOnBean : dependsOn) {
if (isDependent(beanName, dependsOnBean)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dependsOnBean + "'");
}
//注册依赖bean,这里主要注册了两个集合,dependentBeanMap beanName -> 依赖的beanName集合, dependenciesForBeanMap 依赖的beanName -》 beanName集合
registerDependentBean(dependsOnBean, beanName);
//先创建依赖的bean,想象一下,如果循环依赖,就会一直递归调用下去,直至stackOverflow。
getBean(dependsOnBean);
}
} // Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
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,那么就使用工厂来创建这个bean
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 {
//使用其他的scope创建bean
String scopeName = mbd.getScope();
final 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, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
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. 转换成指定bean类型
if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
try {
return getTypeConverter().convertIfNecessary(bean, requiredType);
}
catch (TypeMismatchException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to convert bean '" + name + "' to required type [" +
ClassUtils.getQualifiedName(requiredType) + "]", ex);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}
return (T) bean;
} SimpleAliasRegistry.canonicalName public String canonicalName(String name) {
String canonicalName = name;
// Handle aliasing...
String resolvedName;
do {
//获取最终的beanName
resolvedName = this.aliasMap.get(canonicalName);
if (resolvedName != null) {
canonicalName = resolvedName;
}
}
while (resolvedName != null);
return canonicalName;
} DefaultSingletonBeanRegistry.getSingleton protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//从singletonObjects map中获取已经创建好的单例对象
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
//获取提前暴露的bean工厂,这个bean工厂获取到的bean是还没有初始化完成的bean
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//获取还没有进行设置属性,未初始化,未被后置处理器处理的原始bean
singletonObject = singletonFactory.getObject();
//将未被初始化完全的bean扔到容器中,用于循环依赖
this.earlySingletonObjects.put(beanName, singletonObject);
//未初始化提前暴露的工厂使用完后就移除它
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
} AbstractBeanFactory.getObjectForBeanInstance protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, RootBeanDefinition mbd) { // Don't let calling code try to dereference the factory if the bean isn't a factory.
//如果name是以&开头的,并且beanInstance又不是FactoryBean就抛出错误
if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
} // Now we have the bean instance, which may be a normal bean or a FactoryBean.
// If it's a FactoryBean, we use it to create a bean instance, unless the
// caller actually wants a reference to the factory.
//如果这个bean不是工厂或者这个name是以&开头的,那么直接返回bean
//不是工厂bean,那么就说明它已经是想要的bean了,如果是以&开头的,并且前面已经通过了这个bean是工厂bean的校验,那么就说明用户想要获取这个工厂bean
if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
return beanInstance;
} Object object = null;
//mbd在获取早期未初始化bean的时候为null,这个时候会从工厂bean缓存中尝试获取
if (mbd == null) { object = getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
// Return bean instance from factory.
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
// Caches object obtained from FactoryBean if it is a singleton.
//如果mbd为空,并且存在这样的beanDefinition,那么就获取到合并的BeanDefinition
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
//判断是否为应用程序生成的对象,这里主要为了后面判断是否进行后置处理做判断
boolean synthetic = (mbd != null && mbd.isSynthetic());
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
} FactoryBeanRegistrySupport.getObjectFromFactoryBean protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
//如果是单例的,并且已经创建好了对应的bean工厂对象
if (factory.isSingleton() && containsSingleton(beanName)) {
synchronized (getSingletonMutex()) {
Object object = this.factoryBeanObjectCache.get(beanName);
if (object == null) {
//调用工厂bean的getObject方法获取对象
object = doGetObjectFromFactoryBean(factory, beanName);
// Only post-process and store if not put there already during getObject() call above
// (e.g. because of circular reference processing triggered by custom getBean calls) //再次检查这个bean是否已经被缓存,因为在创建这个bean的时候,有可能因为循环引用调用了getBean,或者用户调用getBean的时候已经前一步创建完成,并且put进这个缓存中
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
if (alreadyThere != null) {
object = alreadyThere;
}
else {
if (object != null && shouldPostProcess) {
try {
//如果不是应用生成的bean,那么就进行初始化后置处理
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName,
"Post-processing of FactoryBean's singleton object failed", ex);
}
}
//加入缓存
this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT));
}
}
return (object != NULL_OBJECT ? object : null);
}
}
else {
//调用bean的工厂方法获取bean
Object object = doGetObjectFromFactoryBean(factory, beanName);
if (object != null && shouldPostProcess) {
try {
//后置处理
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
}
}
return object;
}
} DefaultSingletonBeanRegistry.getSingleton public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "'beanName' must not be null");
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while the singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
//在创建bean之前记录这个beanName到singletonsCurrentlyInCreation集合中
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<Exception>();
}
try {
//调用AbstractAutowireCapableBeanFactory. createBean
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
// Has the singleton object implicitly appeared in the meantime ->
// if yes, proceed with it since the exception indicates that state.
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw ex;
}
}
catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
//创建成功后从singletonsCurrentlyInCreation移除beanName
afterSingletonCreation(beanName);
}
if (newSingleton) {
/** this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
* this.singletonFactories.remove(beanName);
* this.earlySingletonObjects.remove(beanName);
* this.registeredSingletons.add(beanName);
*/
addSingleton(beanName, singletonObject);
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
} AbstractAutowireCapableBeanFactory. createBean protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
if (logger.isDebugEnabled()) {
logger.debug("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.
//解析这个BeanDefinition代表的Class类型,因为再配置的时候,class属性可能是一个表达式,需要动态运算出来
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
//如果原始的BeanDefinition没有BeanClass,那么就是动态创建的,那么为了不改变原始的BeanDefinition,克隆一个新的BeanDefinition,应为动态计算的class随时都可能会变,比如处理了某些逻辑后,这个BeanDefinition动态计算出来的class发生了改变
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
} // Prepare method overrides.
try {
//Look-method replace-method 这里主要是判断对应的方法是否存在(不存在直接抛错)和是否有重载,如果没有就标记没有重载,这样的话,在后面处理这些方法的时候就不需要获取所有的方法,然后进行最优筛选了
mbdToUse.prepareMethodOverrides();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
beanName, "Validation of method overrides failed", ex);
} try {
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
//调用实例化前置处理器,如果前置处理器返回了不为空的bean,那么还会调用实例化后置处理器,最终返回的bean不为空,那么就直接返回。
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);
} Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isDebugEnabled()) {
logger.debug("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
} AbstractAutowireCapableBeanFactory.doCreateBean protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
//调用bean的构造器去实例bean
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null); // Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
//应用合并BeanDe的后置处理器
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
mbd.postProcessed = true;
}
} // 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.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
//添加体检曝光工厂bean
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
} // Initialize the bean instance.
Object exposedObject = bean;
try {
//注入属性
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
//初始化bean
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) {
//如果已经发生过循环依赖,那么可以从早期单例结合中获取到对应的早期bean
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
//如果bean在初始化之后没有被增强处理,那么这里是相等的
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
//如果当前bean被增强过了,并且不允许包装注入并且它有依赖,那么就是发生了循环依赖,并且当前bean和注入到依赖它的bean的那个早期bean已经不是同一版本的bean了,将会抛出错误。
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<String>(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.");
}
}
}
} // Register bean as disposable.
try {
//注册销毁
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
} return exposedObject;
}

bean的创建(五)第一部分的更多相关文章

  1. Spring源码学习-容器BeanFactory(五) Bean的创建-探寻Bean的新生之路

    写在前面 上面四篇文章讲了Spring是如何将配置文件一步一步转化为BeanDefinition的整个流程,下面就到了正式创建Bean对象实例的环节了,我们一起继续学习吧. 2.初始化Bean对象实例 ...

  2. Spring源码分析之Bean的创建过程详解

    前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 Spring源码分析之BeanFactoryPostProcessor调用过程详解 本文内容: 在 ...

  3. Spring IoC源码解析——Bean的创建和初始化

    Spring介绍 Spring(http://spring.io/)是一个轻量级的Java 开发框架,同时也是轻量级的IoC和AOP的容器框架,主要是针对JavaBean的生命周期进行管理的轻量级容器 ...

  4. 0003 - 基于xml的Spring Bean 的创建过程

    一.目录 前言 创建 Bean 容器 加载 Bean 定义 创建 Bean Spring Bean 创建过程中的设计模式 总结 二.前言 2.1 Spring 使用配置 ApplicationCont ...

  5. 02 Spring框架 简单配置和三种bean的创建方式

    整理了一下之前学习Spring框架时候的一点笔记.如有错误欢迎指正,不喜勿喷. 上一节学习了如何搭建SpringIOC的环境,下一步我们就来讨论一下如何利用ioc来管理对象和维护对象关系. <? ...

  6. Spring IoC bean 的创建(上)

    前言 本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本.因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析. 本篇文章主要介绍 Spring IoC 容 ...

  7. Spring 源码(10)Spring Bean 的创建过程(1)

    Spring Bean的创建刚开始进行了一些准备工作,比如转换服务的初始化,占位符解析器的初始化,BeanDefinition元数据的冻结等操作,都是为了在创建Bean的过程中保证Bean的正确的创建 ...

  8. dubbo源码分析3-service bean的创建与发布

    dubbo源码分析1-reference bean创建 dubbo源码分析2-reference bean发起服务方法调用 dubbo源码分析3-service bean的创建与发布 dubbo源码分 ...

  9. Android开发之 Windows环境下通过Eclipse创建的第一个安卓应用程序(图文详细步骤)

    第一篇  windows环境下搭建创建的第一个安卓应用程序 为了方便,我这里只采用了一体包进行演示. 一.下载安卓环境的一体包. 官网下载:安卓官网(一般被墙了) 网盘下载: http://yunpa ...

随机推荐

  1. Markdown教程<2> mermaid图形绘制(1)

    Markdown教程<2> mermaid图形绘制(1) 博客园中的markdown编辑器同时支持mermaid图表引擎与tex公式引擎,可以使用mermaid直接画出流程图,时序图,甘特 ...

  2. 阿里云主机CentOS7设置远程连接MySQL数据库

    有一个困扰了我好久的问题,今天终于解决了. 看网上的答案只有一部分.今天把完整的发篇博客纪念一下下. 首先,连接阿里云主机并登录数据库, 1.添加一个Host mysql>select User ...

  3. CSS中浮动的使用

    CSS有两个性质 第一个是 :继承性 第二个是:层叠性: 选择器的一种选择能力,谁的权重大就选谁 { 里面分两种情况: 分别是 选中和没选中. 1.选不中的情况下,走继承性,(font,color,t ...

  4. webpack 4.0 版本的简单使用

    webpack 4.0 学习指南 最近前端又要变天了,vue作者推出了vue-cli 3版本,并且里面使用了webpack 4. 但是webpack 3 和webpack 4 二者的使用方式完全不一样 ...

  5. esxi开启SSH

  6. Java连载3-编译与运行阶段详解&JRE,JDK,JVM关系

    ·一. 1.JDK下载地址:https://www.oracle.com/technetwork/java/javase/downloads/jdk12-downloads-5295953.html ...

  7. ZooKeeper入门(二) Zookeeper选举

    1 背景 1.1 什么是leader选举 在zookeeper集群中,每个节点都会投票,如果某个节点获得超过半数以上的节点的投票,则该节点就是leader节点了 1.2 zookeeper集群选举le ...

  8. pod update更新error: RPC failed; curl 18 transfer closed with outstanding read data remaining

    1. pod update 的时候出现下边的错误 error: RPC failed; curl 18 transfer closed with outstanding read data remai ...

  9. Don’t Repeat Yourself

    The Don’t Repeat Yourself (DRY) principle states that duplication in logic should be eliminated via ...

  10. Altium Designer设计PCB--如何增大电源地的线宽

    笑话: 看见楼下老大爷在下棋,我看了一会儿,跟大爷说:大爷,你che没了. 大爷一脸不屑:小朋友,那叫ju. 然后我静静地在那看了两个小时. 对完棋,大爷起身要走. 我说:大爷,我刚才说的不是你的棋, ...