Spring 源码分析(二)之 Spring IOC 容器源码分析

spring.png

Spring 源码分析(二)之 Spring IOC 容器源码分析

在之前介绍了Spring IOC 容器在项目中的作用

  • 将对象的构建统一解决
  • 并自动维护对象的依赖关系,从而降低实现成本
  • ...

源码介绍之前,看几个问题:

  • Bean的承载对象是什么?
  • Bean的定义如何存储的?
  • Bean工厂是如何生产bean的?
  • Bean的依赖关系由谁来解决?
  • Bean工厂和ApplicationContext的区别?
Bean的构建过程

spring.xml文件中保存了我们对Bean的描述配置,BeanFactory会读取这些配置然后生成对应的Bean。

这些配置信息最后由BeanDefinition来承载。

BeanDefinition(Bean定义)

IOC实现中,我们在xml中的描述的Bean信息最后都将保存至BeanDefinition对象中,其中xml bean与BeanDefinition是一对一的关系。

xml bean的属性配置与BeanDefinition对应关系

XML-Bean BeanDefinition
class beanClassName
scope SCOPE_SINGLETON、SCOPE_PROTOTYPE
lazy-init AbstractBeanDefinition.lazyInit
constructor-arg AbstractBeanDefinition.ConstructorArgument
property AbstractBeanDefinition.propertyValues
factory-method AbstractBeanDefinition.factoryMethondName
destroy-method AbstractBeanDefinition.destroyMethodName
init-method AbstractBeanDefinition.initMethodName
autowire AbstractBeanDefinition.autowireMode
id
name

BeanDefinition.java

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
    String SCOPE_SINGLETON = "singleton";
    String SCOPE_PROTOTYPE = "prototype";
    int ROLE_APPLICATION = 0;
    int ROLE_SUPPORT = 1;
    int ROLE_INFRASTRUCTURE = 2;
    ...
}

AbstractBeanDefinition.java

public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor implements BeanDefinition, Cloneable {
    public static final String SCOPE_DEFAULT = "";
    public static final int AUTOWIRE_NO = 0;
    public static final int AUTOWIRE_BY_NAME = 1;
    public static final int AUTOWIRE_BY_TYPE = 2;
    public static final int AUTOWIRE_CONSTRUCTOR = 3;
    /** @deprecated */
    @Deprecated
    public static final int AUTOWIRE_AUTODETECT = 4;
    public static final int DEPENDENCY_CHECK_NONE = 0;
    public static final int DEPENDENCY_CHECK_OBJECTS = 1;
    public static final int DEPENDENCY_CHECK_SIMPLE = 2;
    public static final int DEPENDENCY_CHECK_ALL = 3;
    public static final String INFER_METHOD = "(inferred)";
    private volatile Object beanClass;
    private String scope;
    private boolean abstractFlag;
    private boolean lazyInit;
    private int autowireMode;
    private int dependencyCheck;
    private String[] dependsOn;
    private boolean autowireCandidate;
    private boolean primary;
    private final Map<String, AutowireCandidateQualifier> qualifiers;
    private boolean nonPublicAccessAllowed;
    private boolean lenientConstructorResolution;
    private String factoryBeanName;
    private String factoryMethodName;
    private ConstructorArgumentValues constructorArgumentValues;
    private MutablePropertyValues propertyValues;
    private MethodOverrides methodOverrides;
    private String initMethodName;
    private String destroyMethodName;
    private boolean enforceInitMethod;
    private boolean enforceDestroyMethod;
    private boolean synthetic;
    private int role;
    private String description;
    private Resource resource;
    ...
}

BeanDefinitionRegistry(Bean注册器)
在上面没有看到xml bean中的id、name属性对应在bean定义中,原因是id其作为当前bean的存储key注册到BeanDefinitionRegistry注册器中。name作为别名key注册到AliasRegistry注册中心。其最后都是指向对象的BeanDefinition。

BeanDefinitionRegistry.java

public interface BeanDefinitionRegistry extends AliasRegistry {
    void registerBeanDefinition(String var1, BeanDefinition var2) throws BeanDefinitionStoreException;

    void removeBeanDefinition(String var1) throws NoSuchBeanDefinitionException;

    BeanDefinition getBeanDefinition(String var1) throws NoSuchBeanDefinitionException;

    boolean containsBeanDefinition(String var1);

    String[] getBeanDefinitionNames();

    int getBeanDefinitionCount();

    boolean isBeanNameInUse(String var1);
}

AliasRegistry.java

public interface AliasRegistry {
    void registerAlias(String var1, String var2);

    void removeAlias(String var1);

    boolean isAlias(String var1);

    String[] getAliases(String var1);
}

BeanDefinitionReader(Bean定义读取)
前面知道了BeanDefinition存储了xml bean信息,而BeanDefinitionRegistry基于id和name保存bean的定义,下面是xml bean到BeanDefinition然后注册到BeanDefinitionRegistry整个过程。

分为3步:

1、BeanDefinitionReader读取spring.xml

2、读取后创建BeanDefinition

3、创建好后注册到BeanDefinitionRegister

BeanDefinitionReader.java

public interface BeanDefinitionReader {
    //获取注册器
    BeanDefinitionRegistry getRegistry();
    //获取资源装载器
    ResourceLoader getResourceLoader();
    //获取bean类加载器
    ClassLoader getBeanClassLoader();

    BeanNameGenerator getBeanNameGenerator();
    //基于资源装载Bean定义并注册到注册器
    int loadBeanDefinitions(Resource var1) throws BeanDefinitionStoreException;
    
    int loadBeanDefinitions(Resource... var1) throws BeanDefinitionStoreException;
    //基于资源路径装载Bean定义并注册到注册器
    int loadBeanDefinitions(String var1) throws BeanDefinitionStoreException;

    int loadBeanDefinitions(String... var1) throws BeanDefinitionStoreException;
}

装载过程demo

package com.demo.spring;

import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.SimpleBeanDefinitionRegistry;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;

import java.util.Arrays;

/**
 * com.demo.spring
 *
 * @author Zyy
 * @date 2019/2/13 11:34
 */
public class BeanDefinitionReaderTest {

    public static void main(String[] args) {
        //创建一个简单注册器
        BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
        //创建bean定义读取器
        BeanDefinitionReader reader = new XmlBeanDefinitionReader(registry);
        //创建资源读取器
        DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
        //获取资源
        Resource resource = resourceLoader.getResource("spring.xml");
        //装载类定义
        reader.loadBeanDefinitions(resource);
        //打印构建bean名称
        System.out.println(Arrays.toString(registry.getBeanDefinitionNames()));
    }
}

运行结果

[di, driver, com.demo.spring.HelloSpring#0, com.demo.spring.LookUpTest#0, helloSpring, com.demo.spring.DI#1, com.demo.spring.DI#0, helloByName]

如果未给bean设置id则:class+#+索引,来标识id

com.demo.spring.HelloSpring#0

打印一下bean定义信息

spring.xml

 <bean id="di" name="di2" lazy-init="false" class="com.demo.spring.DI" scope="singleton" init-method="init" destroy-method="destroy"/>

打印语句:

System.out.println(registry.getBeanDefinition("di"));
System.out.println(registry.getAliases("di2"));

结果信息:

Generic bean: class [com.demo.spring.DI]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=init; destroyMethodName=destroy; defined in class path resource [spring.xml]
[Ljava.lang.String;@73a28541
BeanFactory(bean工厂)

有了Bean的定义,则下面可以用BeanFactory来进行构建bean。

BeanFactory.java

public interface BeanFactory {

    String FACTORY_BEAN_PREFIX = "&";
    //基于id或name获取一个bean
    Object getBean(String name) throws BeansException;
    //基于bean的类别获取bean(如果出现多个该类的实例,则会报错)但是可以指定primary="true"调整优先级来解决该报错
    <T> T getBean(String name, Class<T> requiredType) throws BeansException;
    
    <T> T getBean(Class<T> requiredType) throws BeansException;
    //基于名词获取一个bean,并覆盖默认的构造参数
    Object getBean(String name, Object... args) throws BeansException;

    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

    boolean containsBean(String name);

    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
    //指定bean与指定class是否匹配
    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;


    Class<?> getType(String name) throws NoSuchBeanDefinitionException;

    String[] getAliases(String name);

}

测试demo如下:

package com.demo.spring;

import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.DefaultResourceLoader;

/**
 * com.demo.spring
 *
 * @author Zyy
 * @date 2019/2/13 12:19
 */
public class BeanFactoryTest {

    public static void main(String[] args) {
        //注册中心
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        //读取器
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
        //设置资源加载器
        reader.setResourceLoader(new DefaultResourceLoader());
        //装载构建bean的定义
        reader.loadBeanDefinitions("spring.xml");
        //打印
        System.out.println(beanFactory.getBean("di"));
        System.out.println(beanFactory.getBean("di2"));
    }
}

当getBean时,看下堆栈信息,如下

 at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:142)
  at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:89)
  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1147)
  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1099)
  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:513)
  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
  at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
  at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
  - locked <0x593> (a java.util.concurrent.ConcurrentHashMap)
  at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
  at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
  at com.demo.spring.BeanFactoryTest.main(BeanFactoryTest.java:25)

看具体调用方法

instantiateClass:142, BeanUtils (org.springframework.beans)
instantiate:89, SimpleInstantiationStrategy (org.springframework.beans.factory.support)
instantiateBean:1147, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBeanInstance:1099, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
doCreateBean:513, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBean:483, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
getObject:306, AbstractBeanFactory$1 (org.springframework.beans.factory.support)
getSingleton:230, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)
doGetBean:302, AbstractBeanFactory (org.springframework.beans.factory.support)
getBean:197, AbstractBeanFactory (org.springframework.beans.factory.support)
main:25, BeanFactoryTest (com.demo.spring)

逐个点进去查看

org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String)

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

AbstractBeanFactory#getBean中继续调用doGetBean,查看doGetBean,由于其中代码太多,删除检查判断,只保留核心代码

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

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

    final String beanName = transformedBeanName(name);
    Object bean;

    // Eagerly check singleton cache for manually registered singletons.
    //从单例缓存中获取
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    } else {
        
        // 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 (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);
            }
        }

        if (!typeCheckOnly) {
            markBeanAsCreated(beanName);
        }

        try {
            
            // Create bean instance.
            //mbd -> RootBeanDefinition
            //判断是否单例
            if (mbd.isSingleton()) {
                sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                    @Override
                    public Object getObject() throws BeansException {
                        try {
                            return createBean(beanName, mbd, args);
                        }
                        catch (BeansException 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();
                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) {
                    ...
                }
            }
        }
        catch (BeansException ex) {
            ...
        }
    }

    return (T) bean;
}

其中

Object sharedInstance = getSingleton(beanName);


org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        Object singletonObject = this.singletonObjects.get(beanName);
        //如果取到的为null且判断是正在创建
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            //则锁住map
            synchronized (this.singletonObjects) {
                //从正在创建的earlySingletonObjects这个map中去取
                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 != NULL_OBJECT ? singletonObject : null);
    }

其中singletonObjects、earlySingletonObjects、singletonFactories均是一个map

private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);

private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

对于其中的

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

org.springframework.beans.factory.support.AbstractBeanFactory#getObjectForBeanInstance

if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
        return beanInstance;
    }

其中如果是FactoryBean,则调用getObject()来获取我们自定义的bean,例如

<!-- FactoryBean创建 自定义创建bean-->
<bean id="driver" class="com.demo.spring.DriverFactoryBean" >
   <property name="jdbcUrl" value="jdbc:mysql://192.168.5.104:3306"></property>
</bean>

其中返回的不是DriverFactoryBean,而是返回Driver

public Class<?> getObjectType() {
    return Driver.class;
}

总结:

1、调用BeanFactory.getBean()会触发Bean的实例化。

2、DefaultSingletonBeanRegistry中缓存了单例Bean。

3、Bean的创建于初始化是由AbstractAutowireCapableBeanFactory完成的。

另外:

IOC容器只存放单例Bean,IOC容器在初始化的时候,会将所以Bean初始化在singletonObjects这个ConcurrentHashMap中。
在获取bean的时候,首先会去singletonObjects中去取,如果scope是单例则可以获取bean,如果是多例,则取不到bean,需要从mergedBeanDefinitions这个ConcurrentHashMap中获取RootBeanDefinition,这个里面包含bean的基础信息,然后判断scope是prototype(多例),则每次都会创建一个新的实例。

对于@Autowire、@Resource等注解,在启动SpringIOC容器时,容器会装载一个AutowiredAnnotationBeanPostProcessor 后置处理器,当容易扫描到@Autowire、@Resource等注解时,就会在IOC容器中自动查找需要的Bean,并装配给该对象的属性,在使用@Autowire时,首先在容器中查询对应类型的bean,如果查询不止一个,则根据名称来查,如果名称没有则抛异常,可以将required=false来解决,如果正好查询到一个,则就将该bean装配给@Autowire指定的变量。

对于bean之间相互引用的情况,比如A依赖B,B依赖A,这种情况时,先去实例化A,然后发现依赖B,接着去实例化B,如果此时发现B依赖A,容器会获取A的一个早期引用(early reference),将这个早期引用的A注入给B,然后B实例化完了,则实例化A也就完成了。当然对于这种循环引用的代码应该规避,这种不是正常的场景。

涉及的类:

BeanDefinition

DefaultResourceLoader

XmlBeanDefinitionReader

BeanDefinitionRegistry

BeanFactory

DefaultListableBeanFactory

AutowireCapableBeanFactory

AbstractAutowireCapableBeanFactory

SingletonBeanRegistry

DefaultSingletonBeanRegistry

整个流程:

run->getBean->BeanFactory->doGetBean->AbstructBeanFactory
->getSingleton(从缓存中获取,如果是单例,为空则创建并缓存,如果是多例则直接创建)
->DefaultSingleonBeanRegistry->createBean->AbstractAutowireCapableBeanFactory
->createBeanInstance->newInstance->Constructor->反射实例化Bean
BeanFactory 与 ApplicationContext区别

看下结构图

ApplicationContext.java

ApplicationContext.png

从图中可以看到ApplicationContext是由BeanFactory接口派生出来的,具有BeanFactory所以功能。除此之外,还具有以下功能:

1、MessageSource,提供国际化的消息访问

2、资源访问,入url和文件

3、事件传播,实现了ApplicationListener接口的bean

4、载入多个上下文(有继承关系),使每个上下文都专注于一个特定的层次,比如应用web层

类似的

DefaultListableBeanFactory.java

DefaultListableBeanFactory.png

github地址 : https://github.com/zhaoyybalabala/spring-test

如有问题,欢迎提问:)

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

推荐阅读更多精彩内容