Spring IOC-源码分析-Bean的加载

概述

本文将介绍Bean的加载过程,即容器中的BeanDefinition如何在需要时加载为Bean,核心有:

  • 获取bean,getBeandoGetBean的源码逻辑
  • createBean方法,包括bean实例创建、循环依赖、属性注入、初始化等bean创建的逻辑

入口

上一节介绍了xml形式的配置元数据加载解析到容器中,以BeanDefinition的形式存在,这一节将研究这些BeanDefinition将如何进一步使用,也就是从容器中获得Bean,加载Bean的过程。

我们研究的入口即BeanFactory接口的一个方法:

前面讲BeanFactory的各种实现类时,提到过,AbstractBeanFactory 抽象类实现了获取Bean的核心逻辑,即重要的,它的getBean方法:

@Override
public Object getBean(String name) throws BeansException {
    return doGetBean(name, null, null, false);
}

几个getBean的重载方法最后都调用了doGetBean方法:

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
            @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException 

doGetBean方法

可以先思考一下,如果让你设计从容器中获得bean,你要考虑那些?这里简要列一下doGetBean方法都做了什么事情:

  • beanName可能是别名

  • 单例bean只加载一次,需要缓存,尝试从缓存中取

  • 单例bean的循环依赖如何解决

  • FactoryBean类型的bean获取特殊处理

  • BeanFactory容器继承,从父容器中加载

  • 设置了dependsOn属性,要先加载器依赖的bean,递归

  • 创建bean,根据bean的不同scope,需要不同的创建方式

    • 单例bean
    • prototype类型bean,直接创建
    • 其它Scope类型,将创建工厂方法传入,让其实现的Scope接口管理并决定何时创建
  • 参数里有一个requiredType,拿到bean之后进行类型转换

下面将针对doGetBean方法如何做的以上事情进行分步骤分析

beanName及别名

final String beanName = transformedBeanName(name);

首先,对FactoryBean的支持(后面**FactoryBean的使用**中介绍),可能在name中会含有&,对其进行转换。

另外spring容器提供根据别名获得bean功能,需要转换beanName,这个方法将使用前面提到的AliasRegistry接口和SimpleAliasRegistry实现来找到最规范的别名canonicalName,其内部维护了一个Map,每个别名最终链式指向canonicalNamecanonicalName对应指向null。

尝试从缓存中获得单例bean

Object sharedInstance = getSingleton(beanName);
// 允许从第三级缓存即早期引用工厂缓存中获取
getSingleton(beanName, true);

前面BeanFactory实现节提到过,单例bean相关功能操作由SingletonBeanRegistry接口定义,DefaultSingletonBeanRegistry类实现,这里便调用了DefaultSingletonBeanRegistry的Singleton方法:

@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    Object singletonObject = this.singletonObjects.get(beanName);
    // 只有要获取的bean正在创建时,才考虑使用两个为解决循环依赖的缓存。
    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;
}

这里尝试从DefaultSingletonBeanRegistry的单例缓存中获取bean,缓存中都没有的话可能返回null,注意判断条件,isSingletonCurrentlyInCreation,只有要获取的bean正在创建时,才考虑使用两个为解决循环依赖的缓存。

可以看到使用了三个缓存,这里顺便介绍这三个缓存用来干嘛,具体单例bean/工厂/早期bean什么时候放在缓存中的,大胆猜测后面的过程中一定会有。

/** Cache of singleton objects: bean name to bean instance. */
// 即缓存真正的单例bean,name->bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** Cache of singleton factories: bean name to ObjectFactory. */
// 缓存bean创建工厂,ObjectFactory,其实就是一个函数式接口,调用后真正去创建bena
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

/** Cache of early singleton objects: bean name to bean instance. */
// 缓存早期单例bean对象,解决循环依赖用,其中的bean并没有完全初始化完成,property并没有set
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

FactoryBean的使用

FactoryBean接口用于定义一个bean,通过它的name获取到的是这个FactoryBeangetObject()方法返回的对象,对应类型为它FactoryBean的泛型参数,通过&name才能获取到真正的FactoryBean bean。主要是为了在getObject方法中能够实现更多的编程创建操作,有很多实际应用,如mybatis的Mapper加载到spring容器中时用到。

所以这里获得bean是要经过处理,在单例缓存中存储的映射是 beanName -> FactoryBean bean,同样的,这个方法调用在后面每次获得/创建成功bean后均要使用:

bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);

内部检查了sharedInstance是不是FactoryBean,如果不是直接返回。如果是,直接调用getObject方法,能直接调用吗,不能,需要考虑单例bean的感受啊:

object = getCachedObjectForFactoryBean(beanName);

object = getObjectFromFactoryBean(factory, beanName, !synthetic);

这两个方法均来自前面介绍BeanFactory实现时提到过的提供FactoryBean支持的FactoryBeanRegistrySupport类,因为可能有单例bean的原因,不能每次都调用getObject方法,它提供了一个工厂bean的缓存来解决这个问题,同时,这个类中当然也包含了getObject方法的调用和一些后置处理器的调用。

尝试父BeanFactory中获得

比较简单,就是几个判断:

parentBeanFactory.getBean(nameToLookup);

Bean定义获取与dependsOn属性的解决

到了这一步,必须自己创建bean了,首先需要获取BeanDefinition

// 解析时即完成了父子beanDefinition的融合
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

一个递归的过程,让它的依赖先加载,如果它的依赖依赖它(使用一个map(name -> set)存储依赖关系),就抛出异常喽:

for (String dep : dependsOn) {
    if (isDependent(beanName, dep)) {
            throw new BeanCreationException(...);
    }
    registerDependentBean(dep, beanName);
    // 有省略
    getBean(dep);
}

加载单例bean

第一次获得bean,缓存中当然没有啊,尝试从缓存中加载为null后,就要尝试创建了,同样使用DefaultSingletonBeanRegistrygetSingleton的重载方法:

// 将具体的创建方法作为一个函数式接口传入
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;
   }
});

将具体的创建方法作为一个函数式接口传入,交个DefaultSingletonBeanRegistry 进行单例bean的加载/创建。

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(beanName, "Bean name must not be null");
    // 要完成bean的创建并加入单例bean缓存中,这里需要进行同步
    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 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 + "'");
            }
            // 创建前工作,检查是否处于创建状态,抛出异常
            beforeSingletonCreation(beanName);
            boolean newSingleton = false;
            boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
            if (recordSuppressedExceptions) {
                this.suppressedExceptions = new LinkedHashSet<>();
            }
            try {
                // 调用上一步传进来的函数式接口,进行创建,即调用上面提到的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;
                }
                // 创建后工作,移出正在创建集合
                afterSingletonCreation(beanName);
            }
            if (newSingleton) {
                // 如果是新bean,加入到单例bean缓存中,注意这里已经是初始化完成状态的bean
                addSingleton(beanName, singletonObject);
            }
        }
        return singletonObject;
    }
}

在这个getSingleton方法中,将完成:

  • 单例bean的进一步检查,同步集合,准备创建,记录创建状态
  • 调用传入的创建bean函数式接口,即createBean方法,获得新创建的bean
  • 移除正在创建状态

这里我在想,还没有用到之前提到的三个缓存,但是最后步骤已经将结果放在了单例bean缓存的singletonObjects,即初始化完成的bean集合。

也就是这里只是创建bean的外层结构,真正实例化、解决循环依赖、初始化的过程应该在真正的创建bean过程,也就是传进来的函数式接口中,也就是我们还要回到doGetBean方法找到上面那个createBean方法,但是因为单例循环依赖等要在本类DefaultSingletonBeanRegistry 中解决,所以大胆猜测,一会儿我们还会回来调用添加入早期引用工厂方法缓存中和创建早期引用,移除早期引用工厂缓存,加入早期引用缓存等方法。

createBean方法

顾名思义,createBean将完成bean的创建,也就是BeanDefinition要加载出bean了,这个方法定义在我们在介绍BeanFactory实现类中提到的重要实现类AbstractAutowireCapableBeanFactory,它完成了初始化前代理检查构造属性依赖注入初始化等重要操作。

实例化前

首先,完成一些前期工作,覆盖方法即为后面的lookup-methodreplace-method功能做准备。

// 获得类型放在bean定义中,应该是要后面反射实例化用的吧
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
mbdToUse.setBeanClass(resolvedClass);
// 准备覆盖方法功能
mbdToUse.prepareMethodOverrides();
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance. InstantiationAwareBeanPostProcessor可以在实例化之前创建代理作为目标bean直接返回
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
// 真正的根据bean创建bean
Object beanInstance = doCreateBean(beanName, mbdToUse, args);

resolveBeforeInstantiation 方法会检查bean是否有实例化前置处理器InstantiationAwareBeanPostProcessor,它的postProcessBeforeInstantiation方法可能会在bean实例化前便创建一些代理返回,这里如果创建了,将直接使用代理作为bean,并调用实例化的后置处理器,直接返回bean。后面重要的AOP即通过这个实现。

创建

真正的创建bean方法为doCreateBean,可以发现规律了,do~才是做事情的方法。

这里将进入比较复杂的创建bean环节,代码量较大,希望我们能够一起读写下去。

深度遍历容易迷失那我们就广度遍历嘛,先来看下doCreateBean整体上、大概上主要做哪些事情,一会儿再慢慢解析,暂时想不明白不要放弃,往下看完详细咱再回来捋一遍:

  • 如果单例bean,需要移除之前的缓存
  • createBeanInstance方法,将BeanDefinition转换为BeanWrapper,具体的后面详细说
  • applyMergedBeanDefinitionPostProcessors,bean定义合并后的处理器执行,Autowired、已过时的@Required等注解通过此方法实现注入类型的预解析。
  • 循环依赖处理,对于单例、允许循环依赖且本bean正在创建过程中的bean,将创建早期bean引用的创建函数式接口放入工厂方法缓存中
  • populateBean,属性填充
  • 初始化bean,依次调用aware方法,调用BeanPostProcessor处理器的bean初始化前方法,调用initMethod
  • 循环依赖检查,后面再看
  • 注册DisposableBean,如果配置了destroy-method,注册一下
  • 返回

下面再把以上doCreateBean做的事情详细分析下:

创建实例createBeanInstance

尝试instanceSupplier(具体的我也不知道在那里配置)、工厂方法不为空,使用它们创建:

return obtainFromSupplier(instanceSupplier, beanName);
return instantiateUsingFactoryMethod(beanName, mbd, args);

尝试使用解析过的构造方法创建,因为一个类有多个构造函数时,需要根据参数和类型判断最终使用那个构造函数,判断过程比较消耗性能,这里会缓存解析结果。

if (mbd.resolvedConstructorOrFactoryMethod != null) {
    resolved = true;
    autowireNecessary = mbd.constructorArgumentsResolved;
}
if (resolved) {
    if (autowireNecessary) {
        return autowireConstructor(beanName, mbd, null, null);
    }
    else {
        return instantiateBean(beanName, mbd);
    }
}

如果没有缓存的解析过的构造方法,说明是第一次,或者解析构造参数和方法调用,或者调用无参构造器:

// 传入参数使用
return autowireConstructor(beanName, mbd, ctors, args);
// 默认参数
return autowireConstructor(beanName, mbd, ctors, null);
// 无参构造器
return instantiateBean(beanName, mbd);

autowireConstructor 带参数实例化参数确定相关逻辑极其复杂:

  • 首先根据明确参数确定
  • 没有参数,考虑从解析过的缓存中获取
  • 再考虑从配置文件中获取构造器参数
  • 根据构造参数匹配构造器
  • 转换对应构造参数类型
  • 验证
  • 根据实例化策略、构造器、构造参数进行实例化

而无参构造器,则直接根据实例化策略实例化即完成。

关于实例化策略,如果bean没有定义覆盖方法(即前面提到的lookup-method,replace-method),则直接反射实例化,如果定义了,则需要Cglib动态代理动态织入方法拦截器,返回动态代理。在后续讲AOP时再详细讲。

此时,便成功实例化了Bean。

循环依赖处理
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");
    }
    // getEarlyBeanReference方法会在需要时,如AOP,生成代理对象
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

单例 & 允许循环依赖 & 当前正在创建,将获得其早期引用的函数式结构放在单例bean工厂缓存中。

当A与B循环依赖时,A的早期引用创建函数被放在工厂中,设置A的属性时会创建B,B设置属性时,会获得到A的早期引用以完成创建,然后B创建完成后,A的属性设置成功,也完成创建。

循环依赖简单的说是这样,但是详细的三级缓存的变化,要结合前面尝试从缓存中获取bean和看完后面联系在一起深入理解。

至于这里为什么不直接创建早期引用放入早期引用缓存,而是使用了一个早期引用工厂缓存存储早期引用创建函数,因为getEarlyBeanReference里可能使用AbstractAutoProxyCreator会尝试创建代理对象并缓存起来,后面的初始化操作后置处理器也会使用AbstractAutoProxyCreator尝试创建代理对象(只有在传入的bean与缓存代理两者不同时才创建),后面会判断如果初始化后置处理器没有创建代理对象,将早期引用执行了后置处理器的代理对象直接覆盖最后要返回的bean,即使用三级缓存解决了代理创建问题和循环依赖问题。

三级缓存使没有循环依赖的bean不需要创建早期引用,因为只将工厂放入了缓存。

属性注入populateBean
populateBean(beanName, mbd, instanceWrapper);

属性注入方法也是一个比较复杂的方法,主要做了以下事情:

  • 因为刚刚做完了实例化,这里将检查是否有InstantiationAwareBeanPostProcessor,如果有将调用postProcessAfterInstantiation,完成实例化后置处理,可以控制是否继续进行属性填充(联想doCreateBean调用前是不是类似调用过该BeanPostProcessor的实例化前置处理)
  • 获得自动注入类型,通过beanName或type进行自动注入,存入PropertyValues
  • 调用InstantiationAwareBeanPostProcessorpostProcessProperties方法,对属性获取完毕填充前处理属性,已过时的@Required即在这里完成。
  • 检查依赖
  • applyPropertyValuesPropertyValues中的属性填充入BeanWrapper

关于自动注入:

  • autowireByName,通过名称注入,即递归的调用我们这篇文章的入口 getBean
  • autowireByType,通过类型注入,使用resolveDependency方法根据类型匹配依赖,根据属性的类型如数组、集合、映射进行不同处理。

applyPropertyValues将进行必要类型转换后设置到BeanWrapper上。

bean的初始化
// 初始化传入原始bean,可能返回代理对象
exposedObject = initializeBean(beanName, exposedObject, mbd);

bean的实例化完成,依赖及相关属性设置完成之后,进入初始化阶段,

  • 将调用其实现的Aware接口的相关方法,如BeanNameAwareBeanFactoryAware
  • 调用处理器BeanPostProcessor的初始化前方法
  • 调用自定义的init方法,InitializingBeanafterPropertiesSet方法,xml中的init-method属性
  • 调用处理器BeanPostProcessor的初始化后方法
循环依赖检查
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 " +
                                "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
            }
        }
    }
}

回想下前面的早期引用创建工厂,即循环依赖处理节,getEarlyReference方法,当使用产生循环依赖且AOP代理时,其将创建返回一个代理对象,而此时bean的初始化initializeBean方法不会再创建代理(只创建一次),这里需要校验循环依赖做的就是,判断如果没创建代理,直接使用可能创建了代理对象的早期引用,如果初始化过程创建了,需要检查依赖。

保证了使用代理时最后getBean得到的是需要的代理对象。

注册DisposableBean

如果该bean实现了DisposableBean接口或AutoCloseable接口,或定义了destory-method方法,将根据它的Scope,使用DisposableBeanAdapter注册为在销毁时会调用其销毁方法。

createBean方法调用完成

这是我们的createBean方法调用完成,还记得我们从哪里进来的吗,翻到前面标题为加载单例Bean的一节:

singletonObject = singletonFactory.getObject();
afterSingletonCreation(beanName);
addSingleton(beanName, singletonObject);

创建完成后,将执行移除正在创建集合,将单例bean加入到一级缓存中去,并移除二级和三级缓存。

非单例bean的加载

prototype scope的bean和其它scope的bean其实都调用了上面详细介绍的createBean方法。

只是prototye时每次加载bean都重新创建,而scope是使用了Scope接口的不同实现传入包含createBean的函数接口根据上下文决定是否要创建还是返回,类似于单例bean由DefaultSingletonBeanRegistry管理单例bean,只是管理方式不同,可能有request,session等不同实现管理。

需要注意的是,除了单例bean,其它scope的bean不允许存在循环依赖,因为没有对应的缓存来解决这些问题,如果有循环依赖,创建过程会抛出异常。

至此getBean方法我们便暂时分析完成了,其中一定还有许多没有详细分析的地方,因为spring是一个功能很强大、实现很复杂的东西,以后也许会再来更新其内容。

参考

《Spring源码深度解析》第二版

spring源码 5.1.x

spring core 文档:

https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#spring-core

关于循环依赖与三级缓存:

https://blog.csdn.net/f641385712/article/details/92801300

https://www.despairyoke.com/2019/12/17/2019/2019-12-17-spring-depand/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271