Spring框架中的装配模式

Spring框架中,对于不同的bean进行组合实现复杂功能的过程称为装配。

1、装配模式介绍

在Spring中,支持5种装配模式:

  • no – 缺省情况下,自动配置是通过“ref”属性手动设定。
  • byType – 按数据类型自动装配。如果一个bean的数据类型是用其它bean属性的数据类型,兼容并自动装配它。
  • byName – 根据属性名称自动装配。如果一个bean的名称和其他bean属性的名称是一样的,将会自装配它。
  • constructor – 在构造函数参数的byType方式。
  • autodetect – 如果找到默认的构造函数,使用“自动装配用构造”; 否则,使用“按类型自动装配”。

接下来,我们通过如下的代码对这几种装配模式做下简单测试。代码结构如下:


structure of the project

代码如下:
pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.SpaceCat.SpringHelloWorld</groupId>
    <artifactId>SpringHelloWorld</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!-- Spring Core -->
        <!-- http://mvnrepository.com/artifact/org.springframework/spring-core -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.1.4.RELEASE</version>
        </dependency>
        <!-- Spring Context -->
        <!-- http://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.1.4.RELEASE</version>
        </dependency>
    </dependencies>
</project>
// Interface HelloWorld
public interface HelloWorld {
    public void sayHello();
}
 
// Class implements HelloWorld
public class SpringHelloWorld implements HelloWorld {
   public void sayHello()  {
           System.out.println("Spring say Hello!");
   }
}
 
// Other class implements HelloWorld
public class StrutsHelloWorld implements HelloWorld {
   public void sayHello()  {
           System.out.println("Struts say Hello!");
   }
}
 
public class HelloWorldService {
    private HelloWorld helloWorld;

    public HelloWorldService() {

    }

    public HelloWorldService(HelloWorld h) {
        this.helloWorld = h;
    }

    public void setHelloWorld(HelloWorld helloWorld) {
        this.helloWorld = helloWorld;
    }

    public HelloWorld getHelloWorld() {
        return this.helloWorld;
    }
}

HelloWorldProgram.java:

package com.SpaceCat.HelloWorld.spring;

import com.SpaceCat.HelloWorld.spring.helloworld.HelloWorld;
import com.SpaceCat.HelloWorld.spring.helloworld.HelloWorldService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Created by chengxia on 2019/4/30.
 */
public class HelloWorldProgram {
    public static void main(String[] args) {

        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        HelloWorldService service = (HelloWorldService) context.getBean("helloWorldService");
        HelloWorld hw= service.getHelloWorld();
        hw.sayHello();
    }
}

在下面的例子中,我们通过修改beans.xml配置文集爱你,然后,运行HelloWorldProgram.java就可以对这几种装配模式进行举例说明。

1.1 no

缺省情况下,可以通过“ref”属性手动设定bean之间的关联关系。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="springHelloWorld" class="com.SpaceCat.HelloWorld.spring.helloworld.impl.SpringHelloWorld">
    </bean>
    <bean id="strutsHelloWorld" class="com.SpaceCat.HelloWorld.spring.helloworld.impl.StrutsHelloWorld">
    </bean>

    <bean id="helloWorldService"
          class="com.SpaceCat.HelloWorld.spring.helloworld.HelloWorldService">
        <property name="helloWorld" ref="springHelloWorld"/>
    </bean>
</beans>

运行结果:

Spring Say Hello!!

Process finished with exit code 0

1.2 byType

按数据类型自动装配。如果一个bean的数据类型是用其它bean属性的数据类型,兼容并自动装配它。
接下来的例子中,我们只定义一个HelloWord类型的bean,然后,在定义bean时,通过autowire属性设置自动装配类型为byType。即可完成bean装配。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="strutsHelloWorld" class="com.SpaceCat.HelloWorld.spring.helloworld.impl.StrutsHelloWorld">
    </bean>

    <bean id="helloWorldService"
          class="com.SpaceCat.HelloWorld.spring.helloworld.HelloWorldService" autowire="byType">
    </bean>
</beans>

运行结果:

Struts Say Hello!!

Process finished with exit code 0

1.3 byName

根据属性名称自动装配。如果一个bean的名称和其他bean属性的名称是一样的,将会自装配它。
接下来,我们将一个bean的id设置为与HelloWorldService类的属性名(helloWorld)一致。这样,这个bean就会自动被装配到HelloWorldService。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="helloWorld" class="com.SpaceCat.HelloWorld.spring.helloworld.impl.SpringHelloWorld">
    </bean>

    <bean id="helloWorldService"
          class="com.SpaceCat.HelloWorld.spring.helloworld.HelloWorldService" autowire="byName">
    </bean>
</beans>

运行结果:

Spring Say Hello!!

Process finished with exit code 0

1.4 constructor

在构造函数参数的byType方式。就是根据构造函数参数的类型进行自动装配,如果有一个bean的类型和HelloWorldService类的构造函数类型一样,就会被自动装配过去。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="strutsHelloWorld" class="com.SpaceCat.HelloWorld.spring.helloworld.impl.StrutsHelloWorld">
    </bean>

    <bean id="helloWorldService"
          class="com.SpaceCat.HelloWorld.spring.helloworld.HelloWorldService" autowire="constructor">
    </bean>
</beans>

运行结果:

Struts Say Hello!!

Process finished with exit code 0

1.5 autodetect

这种有些资料写的有些歧义,下面是Stack Overflow上面的一个解释:

Autowiring by autodetect uses either of two modes i.e. constructor or byType modes. First it will try to look for valid constructor with arguments, If found the constructor mode is chosen. If there is no constructor defined in bean, or explicit default no-args constructor is present, the autowire byType mode is chosen.

意思就是:如果发现带参数的构造函数,就使用constructor模式进行装配;如果没有定义构造函数,或者是显式定义了没有参数的构造函数,就使用byType模式进行装配。

这里我测试用的环境中,spring的版本是4.1.4.RELEASE,在测试时IDEA提示报错,如下图:

autodetect error tips

可能在高版本的Spring框架中这种装配模式已经不推荐使用了。但是,运行时仍然可以正常运行,如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="strutsHelloWorld" class="com.SpaceCat.HelloWorld.spring.helloworld.impl.StrutsHelloWorld">
    </bean>

    <bean id="helloWorldService"
          class="com.SpaceCat.HelloWorld.spring.helloworld.HelloWorldService" autowire="autodetect">
    </bean>
</beans>

运行结果:

Struts Say Hello!!

Process finished with exit code 0

2、更加细粒度的装配控制

2.1 特定属性的自动装配

在前面装配模式介绍的例子中,如果一个bean配置了autowire属性,那么它的所有属性都会被自动装配。但是,有时候,我们只希望某些特定的属性配装配。这时候,就需要用到@Autowired注解。
在Spring框架中,可以使用@Autowired注解通过setter方法,构造函数或字段自动装配Bean。通过它,可以对某个特定的bean属性完成自动装配。@Autowired注解是通过匹配数据类型自动装配Bean的。
启用@Autowired需要注册“AutowiredAnnotationBeanPostProcessor',可以通过两种方式:

  1. 在beans.xml文件中,加入<context:annotation-config />(包括xmlns:context="http://www.springframework.org/schema/context")
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <context:annotation-config />
    <bean id="strutsHelloWorld" class="com.SpaceCat.HelloWorld.spring.helloworld.impl.StrutsHelloWorld">
    </bean>

    <bean id="helloWorldService"
          class="com.SpaceCat.HelloWorld.spring.helloworld.HelloWorldService">
    </bean>
</beans>
  1. 在beans.xml中添加<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
    <bean id="strutsHelloWorld" class="com.SpaceCat.HelloWorld.spring.helloworld.impl.StrutsHelloWorld">
    </bean>

    <bean id="helloWorldService"
          class="com.SpaceCat.HelloWorld.spring.helloworld.HelloWorldService">
    </bean>
</beans>

这样,我们就可以通过将@Autowired注解用到bean的特定属性、setter方法或者构造函数等,来实现对自动装配更加细粒度的控制。
下面的例子中,三种实现的效果一样:

  1. 用在属性上
package com.SpaceCat.HelloWorld.spring.helloworld;

import org.springframework.beans.factory.annotation.Autowired;

/**
 * Created by chengxia on 2019/4/30.
 */
public class HelloWorldService {
    @Autowired
    private HelloWorld helloWorld;

    public HelloWorldService() {

    }
    
    public HelloWorldService(HelloWorld h) {
        this.helloWorld = h;
    }

    public void setHelloWorld(HelloWorld helloWorld) {
        this.helloWorld = helloWorld;
    }

    public HelloWorld getHelloWorld() {
        return this.helloWorld;
    }
}
  1. 用在构造函数上
package com.SpaceCat.HelloWorld.spring.helloworld;

import org.springframework.beans.factory.annotation.Autowired;

/**
 * Created by chengxia on 2019/4/30.
 */
public class HelloWorldService {
    private HelloWorld helloWorld;

    public HelloWorldService() {

    }

    @Autowired
    public HelloWorldService(HelloWorld h) {
        this.helloWorld = h;
    }

    public void setHelloWorld(HelloWorld helloWorld) {
        this.helloWorld = helloWorld;
    }

    public HelloWorld getHelloWorld() {
        return this.helloWorld;
    }
}
  1. 用在setter方法上
package com.SpaceCat.HelloWorld.spring.helloworld;

import org.springframework.beans.factory.annotation.Autowired;

/**
 * Created by chengxia on 2019/4/30.
 */
public class HelloWorldService {
    private HelloWorld helloWorld;

    public HelloWorldService() {

    }
    
    public HelloWorldService(HelloWorld h) {
        this.helloWorld = h;
    }

    @Autowired
    public void setHelloWorld(HelloWorld helloWorld) {
        this.helloWorld = helloWorld;
    }

    public HelloWorld getHelloWorld() {
        return this.helloWorld;
    }
}

运行结果如下:

Struts Say Hello!!

Process finished with exit code 0

这里需要注意的是,这个注解并不是只通过byType的方式进行自动装配。准确地说:

@Autowired注入首先根据byType注入,当类型大于1时在根据byName注入。

2.2 指定自动装配特定的bean

在上面介绍的装配模式中,如果有两个符合条件的bean,框架就会报错,如下:

package com.SpaceCat.HelloWorld.spring.helloworld;

import org.springframework.beans.factory.annotation.Autowired;

/**
 * Created by chengxia on 2019/4/30.
 */
public class HelloWorldService {
    private HelloWorld helloWorld;

    public HelloWorldService() {

    }

    public HelloWorldService(HelloWorld h) {
        this.helloWorld = h;
    }

    @Autowired
    public void setHelloWorld(HelloWorld helloWorld) {
        this.helloWorld = helloWorld;
    }

    public HelloWorld getHelloWorld() {
        return this.helloWorld;
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
    <bean id="strutsHelloWorld" class="com.SpaceCat.HelloWorld.spring.helloworld.impl.StrutsHelloWorld">
    </bean>
    <bean id="springHelloWorld" class="com.SpaceCat.HelloWorld.spring.helloworld.impl.SpringHelloWorld">
    </bean>

    <bean id="helloWorldService"
          class="com.SpaceCat.HelloWorld.spring.helloworld.HelloWorldService">
    </bean>
</beans>

运行如下:

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'helloWorldService': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void com.SpaceCat.HelloWorld.spring.helloworld.HelloWorldService.setHelloWorld(com.SpaceCat.HelloWorld.spring.helloworld.HelloWorld); nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.SpaceCat.HelloWorld.spring.helloworld.HelloWorld] is defined: expected single matching bean but found 2: strutsHelloWorld,springHelloWorld
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1202)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:762)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:757)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
    at com.SpaceCat.HelloWorld.spring.HelloWorldProgram.main(HelloWorldProgram.java:14)
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void com.SpaceCat.HelloWorld.spring.helloworld.HelloWorldService.setHelloWorld(com.SpaceCat.HelloWorld.spring.helloworld.HelloWorld); nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.SpaceCat.HelloWorld.spring.helloworld.HelloWorld] is defined: expected single matching bean but found 2: strutsHelloWorld,springHelloWorld
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:649)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
    ... 13 more
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.SpaceCat.HelloWorld.spring.helloworld.HelloWorld] is defined: expected single matching bean but found 2: strutsHelloWorld,springHelloWorld
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1061)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:949)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:606)
    ... 15 more

Process finished with exit code 1

这时候,可以通过@Quanlifier告诉Spring哪些bean应当自动装配。如下:

package com.SpaceCat.HelloWorld.spring.helloworld;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

/**
 * Created by chengxia on 2019/4/30.
 */
public class HelloWorldService {

    @Autowired
    @Qualifier(value = "strutsHelloWorld")
    private HelloWorld helloWorld;

    public HelloWorldService() {

    }

    public HelloWorldService(HelloWorld h) {
        this.helloWorld = h;
    }

    public void setHelloWorld(HelloWorld helloWorld) {
        this.helloWorld = helloWorld;
    }

    public HelloWorld getHelloWorld() {
        return this.helloWorld;
    }
}

注意,这里其实要同时修改beans.xml文件,否则,@Qualifier注解不起作用(应该主要是为了添加<context:annotation-config />)。如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <context:annotation-config />
    <bean id="strutsHelloWorld" class="com.SpaceCat.HelloWorld.spring.helloworld.impl.StrutsHelloWorld">
    </bean>
    <bean id="springHelloWorld" class="com.SpaceCat.HelloWorld.spring.helloworld.impl.SpringHelloWorld">
    </bean>

    <bean id="helloWorldService"
          class="com.SpaceCat.HelloWorld.spring.helloworld.HelloWorldService">
    </bean>
</beans>

运行结果:

Struts Say Hello!!

Process finished with exit code 0

参考资料

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

推荐阅读更多精彩内容