bean的创建(五)第一部分
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的创建(五)第一部分的更多相关文章
- Spring源码学习-容器BeanFactory(五) Bean的创建-探寻Bean的新生之路
写在前面 上面四篇文章讲了Spring是如何将配置文件一步一步转化为BeanDefinition的整个流程,下面就到了正式创建Bean对象实例的环节了,我们一起继续学习吧. 2.初始化Bean对象实例 ...
- Spring源码分析之Bean的创建过程详解
前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 Spring源码分析之BeanFactoryPostProcessor调用过程详解 本文内容: 在 ...
- Spring IoC源码解析——Bean的创建和初始化
Spring介绍 Spring(http://spring.io/)是一个轻量级的Java 开发框架,同时也是轻量级的IoC和AOP的容器框架,主要是针对JavaBean的生命周期进行管理的轻量级容器 ...
- 0003 - 基于xml的Spring Bean 的创建过程
一.目录 前言 创建 Bean 容器 加载 Bean 定义 创建 Bean Spring Bean 创建过程中的设计模式 总结 二.前言 2.1 Spring 使用配置 ApplicationCont ...
- 02 Spring框架 简单配置和三种bean的创建方式
整理了一下之前学习Spring框架时候的一点笔记.如有错误欢迎指正,不喜勿喷. 上一节学习了如何搭建SpringIOC的环境,下一步我们就来讨论一下如何利用ioc来管理对象和维护对象关系. <? ...
- Spring IoC bean 的创建(上)
前言 本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本.因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析. 本篇文章主要介绍 Spring IoC 容 ...
- Spring 源码(10)Spring Bean 的创建过程(1)
Spring Bean的创建刚开始进行了一些准备工作,比如转换服务的初始化,占位符解析器的初始化,BeanDefinition元数据的冻结等操作,都是为了在创建Bean的过程中保证Bean的正确的创建 ...
- dubbo源码分析3-service bean的创建与发布
dubbo源码分析1-reference bean创建 dubbo源码分析2-reference bean发起服务方法调用 dubbo源码分析3-service bean的创建与发布 dubbo源码分 ...
- Android开发之 Windows环境下通过Eclipse创建的第一个安卓应用程序(图文详细步骤)
第一篇 windows环境下搭建创建的第一个安卓应用程序 为了方便,我这里只采用了一体包进行演示. 一.下载安卓环境的一体包. 官网下载:安卓官网(一般被墙了) 网盘下载: http://yunpa ...
随机推荐
- C++按格式接收输入字符(京东,滴滴,360笔试必用)
头一次起这种标题,为了对得起这个标题,我尽量多写点~ 最近还是一边实习一遍投简历--笔试--面试,然而发现了自己的好多问题. 在答了京东笔试(滴滴,360也是这样的)的题后,发现与腾讯,阿里等公司的不 ...
- tensorflow学习笔记-bili莫烦
bilibili莫烦tensorflow视频教程学习笔记 1.初次使用Tensorflow实现一元线性回归 # 屏蔽警告 import os os.environ[' import numpy as ...
- string类总结第一部分函数介绍
在前面几章,看了整个String类的源码,给每个方法都行写了注释,但是太过凌乱,今天我就把String类的方法整理归纳,然后再讲一下String类比较难以理解的部分 特此声明:本文篇幅较大,涵盖知识点 ...
- Cisco packet tracer下dhcp的配置的vlan的应用
话不多说,先上拓扑图. pc0和pc1分别接在三层交换机的F0/1.F0/2接口,ser接在F0/24接口,用ser用作dhcp的服务器. 0x01:配置server0 先配置server的IP地址. ...
- k8s对象类资源格式
k8s api仅接受及响应json格式的数据,同时,为了便于使用,它也允许用户提供yaml格式的post对象,但apiserver需要事先自行将其转换为json格式后方能提交.每个资源通常仅接受并返回 ...
- 阿里巴巴 -- MySQL DBA 面试题
1.MySQL的复制原理以及流程 (1).先问基本原理流程,3个线程以及之间的关联: (2).再问一致性延时性,数据恢复: (3).再问各种工作遇到的复制bug的解决方法. 2.MySQL中myisa ...
- Effective Java第三版(一) ——用静态工厂代替构造器
此文做为<Effective Java>系列的第一篇,所以有必要大概说下此书的特点,当然很多人可能都看过,毕竟是有着Java四大名著之一的大名在外,不过总会有萌新不了解,例如我!<E ...
- Oracle数据库---触发器
SQL> --当我们对empnew执行删除操作之后,它就会出现一个提示信息,提示:这是删除操作!SQL> CREATE TRIGGER first_trigger 2 AFTER DELE ...
- kuangbin专题 专题一 简单搜索 Fire Game FZU - 2150
题目链接:https://vjudge.net/problem/FZU-2150 题意:’ . '代表火无法烧着的地方,‘ # ’表示草,火可以烧着.选择任意两个‘ # ’(可以两个都选同一个 ‘ # ...
- Creating a Manager for Multiple Threads_翻译
The previous lesson showed how to define a task that executes on a separate thread. If you only want ...