Spring源码分析-----加载bean

先看下简单的Spring应用

public static void main(String[] args) {
   BeanFactory bf = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
   TestBean bean = (TestBean)bf.getBean("test");
   bean.test();
 }

获取bean的时候调用getBean方法,跟踪源码发现最后调用的是doGetBean方法

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

下面我们详细来看下doGetBean方法

获取Bean对象整体流程

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
      @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
   /*进行beanName转换,有两个原因
    *1)bean配置的时候可以配置别名,name可能是别名,需要转换成对应的真实beanName
    *2)name可能是以&字符开头的,表明调用者想要获取FactoryBean本身,而非FactoryBean实现类所创建的bean
    */
   final String beanName = transformedBeanName(name);
   Object bean;

   // Eagerly check singleton cache for manually registered singletons.
   /*
    *
    * 检查缓存中或者实例工厂中是否有实例对象
    *
    * 因子在创建单例bean的时候回存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖
    * Spring创建bean的原则是不等bean创建完成就会将创建bean的ObjectFactory提早曝光
    * 也就是将ObjectFactory加入到缓存中,一旦下个bean创建时需要依赖上个bean则直接使用ObjectFactory
    */

   //直接尝试从缓存中获取或者singletonFactories中的ObjectFactory中获取
   Object sharedInstance = getSingleton(beanName);

   /*
    *如果sharedInstance为空,则说明缓存中没有对应的实例,表明这个类还没创建
    * BeanFactory并不会在一开始就将所有的单例bean实例化好,而且在调用getBean获取
    * bean时再实例化,也即是懒加载
    *
    * getBean有很多重载方法,在首次获取某个bean时,可以传入用于初始化bean的参数数组(args),BeanFactory会根据这些
    * 参数去匹配何时的构造方法构造bean实例。当然,如果单例bean早已创建好,这里的args就没用了
    *
    */
   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 + "'");
         }
      }
      /*
       *如果sharedInstance是普通的单例bean,下面的方法会直接返回
       * 但是如果sharedInstance是FactoryBean类型,则需要调用getObject工厂方法获取
       * 真正的bean实例。如果用户想要获取FactoryBean本身,这里也不会特别的处理,直接返回即可
       */
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
   }

   /*
    * 如果上面的条件不满足,则表明sharedInstance可能为空,此时beanName对应的bean实例
    * 可能还没创建
    * 还有一种可能,如果当前容器有父容器,beanName对应的bean实例可能在父容器中被创建
    * 所以在创建实例前,需要先去父容器里检查一下
    */
   else {
      // Fail if we're already creating this bean instance:
      // We're assumably within a circular reference.
      //BeanFactory不缓存Prototype类型的bean,无法处理该类型的循环依赖问题
      if (isPrototypeCurrentlyInCreation(beanName)) {
         throw new BeanCurrentlyInCreationException(beanName);
      }

      // Check if bean definition exists in this factory.
      //如果sharedInstance为空,则到父容器中查询bean的实例
      BeanFactory parentBeanFactory = getParentBeanFactory();
      //如果beanDefinitionMap中也就是在所有已经加载过的类中不包括beanName则尝试从parentFactory中查找
      if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
         // Not found -> check parent.
         //获取name对应的beanName,如果name是以&字符开头,则返回&+beanName
         String nameToLookup = originalBeanName(name);
         //父容器是AbstractBeanFactory类型,调用父容器的doGetBean方法查找
         if (parentBeanFactory instanceof AbstractBeanFactory) {
            return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                  nameToLookup, requiredType, args, typeCheckOnly);
         }
         //根据args是否为空,已决定调用父容器哪个方法获取bean
         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);
         }
      }
      //如果不是仅仅做类型检查则是创建bean,这里要进行记录
      if (!typeCheckOnly) {
         markBeanAsCreated(beanName);//标记当前beanName对应的bean已创建
      }

      try {
         //合并父BeanDefinition和子BeanDefinition
         //将存在XML配置文件的GenericBeanDefinition转成RootBeanDefinition,如果指定BeanName是子bean的话同时合并父类的相关属性
         final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
         checkMergedBeanDefinition(mbd, beanName, args);

         // Guarantee initialization of beans that the current bean depends on.
         //获取bean所有的dependsOn,如果有依赖,则先加载依赖的bean
         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 {
                  //先加载依赖的bean
                  getBean(dep);
               }
               catch (NoSuchBeanDefinitionException ex) {
                  throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                        "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
               }
            }
         }

         // Create bean instance.
         if (mbd.isSingleton()) {
            /*
             *这里并没有直接调用 createBean 方法创建bean实例,而且通过getSingleton(String,ObjectFactory)方法获取bean实例
             * getSingleton(String,ObjectFactory)方法会在内部调用ObjectFactory的getObject()方法创建bean
             * 并在创建完成后,将bean放入缓存中
             */
            sharedInstance = getSingleton(beanName, () -> {
               try {
                  //创建bean实例
                  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是FactoryBean类型,则调用工厂方法获取真正的bean实例,否则直接返回bean实例
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);ge'�
         }

         //创建Prototype类型的bean实例
         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);
         }

         //创建其他类型的bean实例
         else {
            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, () -> {
                  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;
}
doGetBean整体流程.png

beanName转换

protected String transformedBeanName(String name) {
   return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}

public static String transformedBeanName(String name) {
        Assert.notNull(name, "'name' must not be null");
        if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
            return name;
        }
        return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
            do {
                //处理掉beanName中的&字符
                beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
            }
            while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
            return beanName;
        });
    }

    /*
     *  该方法用于转换别名
     */
    public String canonicalName(String name) {
        String canonicalName = name;
        // Handle aliasing...
        String resolvedName;
        /**
         * 这里采用循环处理的原因是,存在别名指向别名的可能
         * <bean id="hello" class="com.df.Hello" />
         * <alias name="hello" alias="aliasA" />
         * <alias name="aliasA" alias="aliasB" />
         * alisaB --> aliasA --> hello
         */
        do {
            resolvedName = this.aliasMap.get(canonicalName);
            if (resolvedName != null) {
                canonicalName = resolvedName;
            }
        }
        while (resolvedName != null);
        return canonicalName;
    }


在Spring容器中,是按照BeanName和Bean对象一样对应进行存储的,通过beanName可以直接找到对应的bean对象。在配置时,支持别名的方式进行配置。通过别名获取bean时,先找到别名对应的真实BeanName,然后再通过beanName进行查找

从缓存中获取单例bean

public Object getSingleton(String beanName) {
   return getSingleton(beanName, true);
}

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        //尝试从缓存中获取单例bean实例
        Object singletonObject = this.singletonObjects.get(beanName);
        /*
         * singletonObject为空,说明还没创建,或者还没有完全创建好
         * isSingletonCurrentlyInCreation是判断beanName对应的bean是否正在创建中
         * 解决bean之间的循环依赖
         */
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                //earlySingletonObjects中获取提前曝光的bean 用于处理循环引用
                singletonObject = this.earlySingletonObjects.get(beanName);
                // 如果如果 singletonObject = null,且允许提前曝光 bean 实例,则从相应的 ObjectFactory 获取一个原始的(raw)bean(尚未填充属性)
                if (singletonObject == null && allowEarlyReference) {
                    //获取相应的工厂类
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        //提前曝光bean实例 ,用于解决循环依赖
                        singletonObject = singletonFactory.getObject();
                        //放入缓存中 这样如果其他的bean依赖当前bean,则可以从earlySingletonObjects中获取
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }

在Spring中,有很多缓存

singletonObjects:用于存放单例bean对象

earlySingletonObjects:也是用于存放单例bean对象,它用于解决循环依赖

singletonFactories:存放创建bean对象的factoryObject

对于单类对象,由于每次获取的都是同一个对象,为了提供效率,Spring会将这些对象缓存起来。Spring容器中绝大多数对象都是单类的,所以每次调用getBean方法时,首先尝试从缓存中获取对应的bean对象,提高了性能。

对于循环依赖的问题,后面专门会说

从实例中获取bean对象

protected Object getObjectForBeanInstance(
      Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

   //如果name以&开头,但beanInatance却不是FactoryBean,则认为有问题
   if (BeanFactoryUtils.isFactoryDereference(name)) {
      if (beanInstance instanceof NullBean) {
         return beanInstance;
      }
      if (!(beanInstance instanceof FactoryBean)) {
         throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
      }
   }

    /*
      上面判断通过后,表明beanInstance可能是一个普通的bean,也可能是一个FactoryBean
      如果是一个普通的bean,这里直接返回beanInstance即可
      如果name是以&开头的,则说明用户最终想要获取的是Factory本身,也直接返回
      如果是FactoryBean,则要调用工厂方法生成一个实例
    */
   if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
      return beanInstance;
   }

   Object object = null;
   if (mbd == null) {
      /*
         如果mbd为空,则从缓存中加载bean。FactoryBean生成的单例bean会被缓存
         在factoryBeanObjectCache集合中,不用每次都创建
       */
      object = getCachedObjectForFactoryBean(beanName);
   }
   if (object == null) {
      // Return bean instance from factory.
      //到了这里,可以保证是FactoryBean类型了
      FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
      // Caches object obtained from FactoryBean if it is a singleton.
      //如果mbd为空。则判断是否存在名字为beanName的beanDefine
      if (mbd == null && containsBeanDefinition(beanName)) {
         //合并BeanDefinition
         mbd = getMergedLocalBeanDefinition(beanName);
      }
      //跟Aop相关,如果被Aop增强了,则为true
      //是否是用户定义的而不是应用程序本事定义的
      boolean synthetic = (mbd != null && mbd.isSynthetic());
      //调用getObjectFromFactoryBean
      object = getObjectFromFactoryBean(factory, beanName, !synthetic);
   }
   return object;
}

beanInstance只是初始状态的bean,不一定是最终我们想要的bean。如果是FactoryBean类型,beanInstance只是FactoryBean的初始状态,我们正在需要的bean需要调用FactoryBean的factory-method方法来获取

处理加载依赖

在bean配置中,可以通过depends-on属性来配置加载依赖类。此时Srping会优先加载依赖的类,然后再加载当前bean

<bean id="sysinit" class="SystemInit">
<bean id="manager" class="CacheManager"  depends-on="sysinit"/>
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
   for (String dep : dependsOn) {
      /*
         检测是否存在循环依赖,两个bean
       */
      if (isDependent(beanName, dep)) {
         throw new BeanCreationException(mbd.getResourceDescription(), beanName,
               "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
      }
      //注册依赖记录
      registerDependentBean(dep, beanName);
      try {
         //先加载依赖的bean
         getBean(dep);
      }
      catch (NoSuchBeanDefinitionException ex) {
         throw new BeanCreationException(mbd.getResourceDescription(), beanName,
               "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
      }
   }
}

代码逻辑很简单,从当前bean的定义中获取依赖类,然后注册依赖记录,加载依赖类

根据不同的scope获取bean对象

获取单类bean
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
   Assert.notNull(beanName, "Bean name must not be null");
   //全局变量需要同步
   synchronized (this.singletonObjects) {
      //首先检测beanName对应的bean是否已经加载过,因为singleton模式其实就是复用以创建的bean,所以这一步是必须的
      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 + "'");
         }
         //创建前的操作 将此beanName加入singletonsCurrentlyInCreation缓存中, 标记此bean正在创建中
         beforeSingletonCreation(beanName);
         boolean newSingleton = false;
         boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
         if (recordSuppressedExceptions) {
            this.suppressedExceptions = new LinkedHashSet<>();
         }
         try {
            //初始化bean
            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中删除
            afterSingletonCreation(beanName);
         }
         if (newSingleton) {
            //加入到缓存 并删除创建过程中的辅助状态
            addSingleton(beanName, singletonObject);
         }
      }
      return singletonObject;
   }
}

1)首先锁住singletonObjects防止并发操作

2)再次确认对应的bean对象没有创建

3)在创建前标记beanName对应的bean正在创建中,上面从缓存中获取单例的时候,可能会判断

4)然后调用singletonFactory的getObejct方法创建bean对象

5)创建完成之后,从singletonsCurrentlyInCreation中删除beanName

6)如果当前bean是第一次创建对象,则将bean对象加入到单例缓存中

Prototype类型bean的获取

Prototype类型的bean每次获取都会创建一个新的bean对象,代码很简单,就是在对象创建前标记正在创建,创建后删除此标记

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);
}
其他scope类型bean的获取

和Prototype类型bean获取的区别在于会从scope中获取

else {
      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, () -> {
            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);
      }
   }
}

类型转换

当参数requiredType不为空,且bean对象不是requiredType类型实例时,就会进行类型缓存,requiredType在调用getBean方法时传入,例如:

applicationContext.getBean("test",TestBean.class);
//进行类型转换
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());
   }
}

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

推荐阅读更多精彩内容