Spring實(shí)戰(zhàn)2:裝配bean—依賴注入的本質(zhì)

主要內(nèi)容

  • Spring的配置方法概覽
  • 自動(dòng)裝配bean
  • 基于Java配置文件裝配bean
  • 控制bean的創(chuàng)建和銷毀

任何一個(gè)成功的應(yīng)用都是由多個(gè)為了實(shí)現(xiàn)某個(gè)業(yè)務(wù)目標(biāo)而相互協(xié)作的組件構(gòu)成的邻眷,這些組件必須相互了解游沿、能夠相互協(xié)作完成工作赘理。例如墨闲,在一個(gè)在線購(gòu)物系統(tǒng)中另凌,訂單管理組件需要與產(chǎn)品管理組件以及信用卡認(rèn)證組件協(xié)作;這些組件還需要跟數(shù)據(jù)庫(kù)組件協(xié)作從而進(jìn)行數(shù)據(jù)庫(kù)讀寫操作兔港。

在Spring應(yīng)用中啥酱,對(duì)象無(wú)需自己負(fù)責(zé)查找或者創(chuàng)建與其關(guān)聯(lián)的其他對(duì)象,由容器負(fù)責(zé)將創(chuàng)建各個(gè)對(duì)象南誊,并創(chuàng)建各個(gè)對(duì)象之間的依賴關(guān)系身诺。例如,一個(gè)訂單管理組件需要使用信用卡認(rèn)證組件弟疆,它不需要自己創(chuàng)建信用卡認(rèn)證組件戚长,只需要定義它需要使用信用卡認(rèn)證組件即可,容器會(huì)創(chuàng)建信用卡認(rèn)證組件然后將該組件的引用注入給訂單管理組件怠苔。

創(chuàng)建各個(gè)對(duì)象之間協(xié)作關(guān)系的行為通常被稱為裝配(wiring)同廉,這就是依賴注入(DI)的本質(zhì)。

2.1 Spring的配置方法概覽

正如在Spring初探一文中提到的柑司,Spring容器負(fù)責(zé)創(chuàng)建應(yīng)用中的bean迫肖,并通過(guò)DI維護(hù)這些bean之間的協(xié)作關(guān)系。作為開發(fā)人員攒驰,你應(yīng)該負(fù)責(zé)告訴Spring容器需要?jiǎng)?chuàng)建哪些bean以及如何將各個(gè)bean裝配到一起蟆湖。Spring提供三種裝配bean的方式:

  • 基于XML文件的顯式裝配
  • 基于Java文件的顯式裝配
  • 隱式bean發(fā)現(xiàn)機(jī)制和自動(dòng)裝配

絕大多數(shù)情況下,開發(fā)人員可以根據(jù)個(gè)人品味選擇這三種裝配方式中的一種玻粪。Spring也支持在同一個(gè)項(xiàng)目中混合使用不同的裝配方式隅津。

我的建議是:盡可能使用自動(dòng)裝配,越少寫顯式的配置文件越好劲室;當(dāng)你必須使用顯式配置時(shí)(例如伦仍,你要配置一個(gè)bean,但是該bean的源碼不是由你維護(hù))很洋,盡可能使用類型安全充蓝、功能更強(qiáng)大的基于Java文件的裝配方式;最后喉磁,在某些情況下只有XML文件中才又你需要使用的名字空間時(shí)谓苟,再選擇使用基于XML文件的裝配方式。

2.2 自動(dòng)裝配bean

Spring通過(guò)兩個(gè)特性實(shí)現(xiàn)自動(dòng)裝配:

  • Component scanning——Spring自動(dòng)掃描和創(chuàng)建應(yīng)用上下文中的beans协怒;
  • Autowiring——Spring自動(dòng)建立bean之間的依賴關(guān)系涝焙;

這里用一個(gè)例子來(lái)說(shuō)明:假設(shè)你需要實(shí)現(xiàn)一個(gè)音響系統(tǒng),該系統(tǒng)中包含CDPlayer和CompactDisc兩個(gè)組件斤讥,Spring將自動(dòng)發(fā)現(xiàn)這兩個(gè)bean纱皆,并將CompactDisc的引用注入到CDPlayer中湾趾。

2.2.1 創(chuàng)建可發(fā)現(xiàn)的beans

首先創(chuàng)建CD的概念——CompactDisc接口芭商,如下所示:

package com.spring.sample.soundsystem;

public interface CompactDisc {
    void play();
}

CompactDisc接口的作用是將CDPlayer與具體的CD實(shí)現(xiàn)解耦合派草,即面向接口編程。這里還需定義一個(gè)具體的CD實(shí)現(xiàn)铛楣,如下所示:

package com.spring.sample.soundsystem;

import org.springframework.stereotype.Component;

@Component
public class SgtPeppers implements CompactDisc {
    private String title = "Sgt. Perppers' Lonely Hearts Club Band";
    private String artist = "The Beatles";

    public void play() {
        System.out.println("Playing " + title + " by " + artist);
    }
}

這里最重要的是@Component注解近迁,它告訴Spring需要?jiǎng)?chuàng)建SgtPeppers bean。除此之外簸州,還需要啟動(dòng)自動(dòng)掃描機(jī)制鉴竭,有兩種方法:基于XML配置文件;基于Java配置文件岸浑,代碼如下(二選一):

  • 創(chuàng)建soundsystem.xml配置文件
<?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:component-scan base-package="com.spring.sample.soundsystem" />
</beans>

在這個(gè)XML配置文件中搏存,使用<context:component-scan>標(biāo)簽啟動(dòng)Component掃描功能,并可設(shè)置base-package屬性矢洲。

  • 創(chuàng)建Java配置文件
package com.spring.sample.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackages = "com.spring.sample.soundsystem")
public class SoundSystemConfig {
}

在這個(gè)Java配置文件中有兩個(gè)注解值得注意:@Configuration表示這個(gè).java文件是一個(gè)配置文件璧眠;@ComponentScan表示開啟Component掃描,并且可以設(shè)置basePackages屬性——Spring將會(huì)設(shè)置該目錄以及子目錄下所有被@Component注解修飾的類读虏。

  • 自動(dòng)配置的另一個(gè)關(guān)鍵注解是@Autowired责静,基于之前的兩個(gè)類和一個(gè)Java配置文件,可以寫個(gè)測(cè)試
package com.spring.sample.soundsystem;

import com.spring.sample.config.SoundSystemConfig;
import org.junit.Assert;import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SoundSystemConfig.class)
public class SoundSystemTest {
    @Autowired
    private CompactDisc cd;

    @Test
    public void cdShouldNotBeNull() {
        Assert.assertNotNull(cd);
    }
}

運(yùn)行測(cè)試盖桥,測(cè)試通過(guò)灾螃,說(shuō)明@Autowired注解起作用了:自動(dòng)將掃描機(jī)制創(chuàng)建的CompactDisc類型的bean注入到SoundSystemTest這個(gè)bean中。

2.2.2 給被掃描的bean命名

在Spring上下文中揩徊,每個(gè)bean都有自己的ID腰鬼。在上一個(gè)小節(jié)的例子中并沒(méi)有提到這一點(diǎn),但Spring在掃描到SgtPeppers這個(gè)組件并創(chuàng)建對(duì)應(yīng)的bean時(shí)塑荒,默認(rèn)給它設(shè)置的ID為sgtPeppers——是的熄赡,這個(gè)ID就是將類名稱的首字母小寫。

如果你需要給某個(gè)類對(duì)應(yīng)的bean一個(gè)特別的名字袜炕,則可以給@Component注解傳入指定的參數(shù)本谜,例如:

@Component("lonelyHeartsClub")
public class SgtPeppers implements CompactDisc {
  ...
}

2.2.3 設(shè)置需要掃描的目標(biāo)basepackage

在之前的例子中,我們通過(guò)給@Component注解傳入字符串形式的包路徑偎窘,來(lái)設(shè)置需要掃描指定目錄下的類并為之創(chuàng)建bean乌助。

可以看出,basePackages是復(fù)數(shù)陌知,意味著你可以設(shè)置多個(gè)目標(biāo)目錄他托,例如:

@Configuration
@ComponentScan(basePackages = {"com.spring.sample.soundsystem", "com.spring.sample.video"})
public class SoundSystemConfig {
}

這種字符串形式的表示雖然可以,但是不具備“類型安全”仆葡,因此Spring也提供了更加類型安全的機(jī)制赏参,即通過(guò)類或者接口來(lái)設(shè)置掃描機(jī)制的目標(biāo)目錄志笼,例如:

@Configuration
@ComponentScan(basePackageClasses = {CDPlayer.class, DVDPlayer.class})
public class SoundSystemConfig {
}

通過(guò)如上設(shè)置,會(huì)將CDPlayer和DVDPlayer各自所在的目錄作為掃描機(jī)制的目標(biāo)根目錄把篓。

如果應(yīng)用中的對(duì)象是孤立的纫溃,并且互相之間沒(méi)有依賴關(guān)系,例如SgtPeppersbean韧掩,那么這就夠了紊浩。

2.2.4 自動(dòng)裝配bean

簡(jiǎn)單得說(shuō),自動(dòng)裝配的意思是讓Spring從應(yīng)用上下文中找到對(duì)應(yīng)的bean的引用疗锐,并將它們注入到指定的bean坊谁。通過(guò)@Autowired注解可以完成自動(dòng)裝配。

例如滑臊,考慮下面代碼中的CDPlayer類口芍,它的構(gòu)造函數(shù)被@Autowired修飾,表明當(dāng)Spring創(chuàng)建CDPlayer的bean時(shí)雇卷,會(huì)給這個(gè)構(gòu)造函數(shù)傳入一個(gè)CompactDisc的bean對(duì)應(yīng)的引用鬓椭。

package com.spring.sample.soundsystem;

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

@Component
public class CDPlayer implements MediaPlayer {
    private CompactDisc cd;

    @Autowired
    public CDPlayer(CompactDisc cd) {
        this.cd = cd;
    }
    public void play() {
        cd.play();
    }
}

還有別的實(shí)現(xiàn)方法,例如將@Autowired注解作用在setCompactDisc()方法上:

@Autowired
public void setCd(CompactDisc cd) {
    this.cd = cd;
}

或者是其他名字的方法上聋庵,例如:

@Autowired
public void insertCD(CompactDisc cd) {
    this.cd = cd;
}

更簡(jiǎn)單的用法是膘融,可以將@Autowired注解直接作用在成員變量之上,例如:

@Autowired
private CompactDisc cd;

只要對(duì)應(yīng)類型的bean有且只有一個(gè)祭玉,則會(huì)自動(dòng)裝配到該屬性上氧映。如果沒(méi)有找到對(duì)應(yīng)的bean,應(yīng)用會(huì)拋出對(duì)應(yīng)的異常脱货,如果想避免拋出這個(gè)異常岛都,則需要設(shè)置@Autowired(required=false)。不過(guò)振峻,在應(yīng)用程序設(shè)計(jì)中臼疫,應(yīng)該謹(jǐn)慎設(shè)置這個(gè)屬性,因?yàn)檫@會(huì)使得你必須面對(duì)NullPointerException的問(wèn)題扣孟。

如果存在多個(gè)同一類型的bean烫堤,則Spring會(huì)拋出異常,表示裝配有歧義凤价,解決辦法有兩個(gè):(1)通過(guò)@Qualifier注解指定需要的bean的ID鸽斟;(2)通過(guò)@Resource注解指定注入特定ID的bean;

2.2.5 驗(yàn)證自動(dòng)配置

通過(guò)下列代碼利诺,可以驗(yàn)證:CompactDisc的bean已經(jīng)注入到CDPlayer的bean中富蓄,同時(shí)在測(cè)試用例中是將CDPlayer的bean注入到當(dāng)前測(cè)試用例。

package com.spring.sample.soundsystem;

import com.spring.sample.config.SoundSystemConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SoundSystemConfig.class)
public class CDPlayerTest {
    public final Logger log = LoggerFactory.getLogger(CDPlayerTest.class);
    @Autowired
    private MediaPlayer player;

    @Test
    public void playTest() {
        player.play();
    }
}

2.3 基于Java配置文件裝配bean

Java配置文件不同于其他用于實(shí)現(xiàn)業(yè)務(wù)邏輯的Java代碼慢逾,因此不能將Java配置文件業(yè)務(wù)邏輯代碼混在一起立倍。一般都會(huì)給Java配置文件新建一個(gè)單獨(dú)的package灭红。

2.3.1 創(chuàng)建配置類

實(shí)際上在之前的例子中我們已經(jīng)實(shí)踐過(guò)基于Java的配置文件,看如下代碼:

@Configuration
@ComponentScan(basePackageClasses = {CDPlayer.class, DVDPlayer.class})
public class SoundSystemConfig {
}

@Configuration注解表示這個(gè)類是配置類口注,之前我們是通過(guò)@ComponentScan注解實(shí)現(xiàn)bean的自動(dòng)掃描和創(chuàng)建变擒,這里我們重點(diǎn)是學(xué)習(xí)如何顯式創(chuàng)建bean,因此首先將@ComponentScan(basePackageClasses = {CDPlayer.class, DVDPlayer.class})這行代碼去掉疆导。

2.3.2 定義bean

通過(guò)@Bean注解創(chuàng)建一個(gè)Spring bean赁项,該bean的默認(rèn)ID和函數(shù)的方法名相同葛躏,即sgtPeppers澈段。例如:

@Bean
public CompactDisc sgtPeppers() {
    return new SgtPeppers();
}

同樣,可以指定bean的ID舰攒,例如:

@Bean(name = "lonelyHeartsClub")
public CompactDisc sgtPeppers() {
    return new SgtPeppers();
}

可以利用Java語(yǔ)言的表達(dá)能力败富,實(shí)現(xiàn)類似工廠模式的代碼如下:

@Bean
public CompactDisc randomBeatlesCD() {
    int choice = (int)Math.floor(Math.random() * 4);

    if (choice == 0) {
        return new SgtPeppers();
    } else if (choice == 1) {
        return new WhiteAlbum();
    } else if (choice == 2) {
        return new HardDaysNight();
    } else if (choice == 3) {
        return new Revolover();
    }
}

2.3.3 JavaConfig中的屬性注入

最簡(jiǎn)單的辦法是將被引用的bean的生成函數(shù)傳入到構(gòu)造函數(shù)或者set函數(shù)中,例如:

@Bean
public CDPlayer cdPlayer() {
    return new CDPlayer(sgtPeppers());
}

看起來(lái)是函數(shù)調(diào)用摩窃,實(shí)際上不是:由于sgtPeppers()方法被@Bean注解修飾兽叮,所以Spring會(huì)攔截這個(gè)函數(shù)調(diào)用,并返回之前已經(jīng)創(chuàng)建好的bean——確保該SgtPeppers bean為單例猾愿。

假如有下列代碼:

@Bean
public CDPlayer cdPlayer() {
    return new CDPlayer(sgtPeppers());
}

@Bean
public CDPlayer anotherCDPlayer() {
    return new CDPlayer(sgtPeppers());
}

如果把sgtPeppers()方法當(dāng)作普通Java方法對(duì)待鹦聪,則cdPlayerbean和anotherCDPlayerbean會(huì)持有不同的SgtPeppers實(shí)例——結(jié)合CDPlayer的業(yè)務(wù)場(chǎng)景看:就相當(dāng)于將一片CD同時(shí)裝入兩個(gè)CD播放機(jī)中,顯然這不可能蒂秘。

默認(rèn)情況下泽本,Spring中所有的bean都是單例模式,因此cdPlayeranotherCDPlayer這倆bean持有相同的SgtPeppers實(shí)例姻僧。

當(dāng)然规丽,還有一種更清楚的寫法:

@Bean
public CDPlayer cdPlayer(CompactDisc compactDisc) {
    return new CDPlayer(compactDisc);
}

@Bean
public CDPlayer anotherCDPlayer() {
    return new CDPlayer(sgtPeppers());
}

這種情況下,cdPlayeranotherCDPlayer這倆bean持有相同的SgtPeppers實(shí)例撇贺,該實(shí)例的ID為lonelyHeartsClub赌莺。這種方法最值得使用,因?yàn)樗灰驝ompactDisc bean在同一個(gè)配置文件中定義——只要在應(yīng)用上下文容器中即可(不管是基于自動(dòng)掃描發(fā)現(xiàn)還是基于XML配置文件定義)松嘶。

2.4 基于XML配置文件裝配bean

這種是Spring中最原始的定義方式艘狭,在此不再詳述。

2.5 混合使用多種配置方法

通常翠订,可能在一個(gè)Spring項(xiàng)目中同時(shí)使用自動(dòng)配置和顯式配置巢音,而且,即使你更喜歡JavaConfig蕴轨,也有很多場(chǎng)景下更適合使用XML配置港谊。幸運(yùn)的是,這些配置方法可以混合使用橙弱。

首先明確一點(diǎn):對(duì)于自動(dòng)配置歧寺,它從整個(gè)容器上下文中查找合適的bean燥狰,無(wú)論這個(gè)bean是來(lái)自JavaConfig還是XML配置。

2.5.1 在JavaConfig中解析XML配置

  • 通過(guò)@Import注解導(dǎo)入其他的JavaConfig斜筐,并且支持同時(shí)導(dǎo)入多個(gè)配置文件龙致;
@Configuration
@Import({CDPlayerConfig.class, CDConfig.class})
public class SoundSystemConfig {
}
  • 通過(guò)@ImportResource注解導(dǎo)入XML配置文件;
@Configuration
@Import(CDPlayerConfig.class)
@ImportResource("classpath: cd-config.xml")
public class SoundSystemConfig {
}

2.5.2 在XML配置文件中應(yīng)用JavaConfig

  • 通過(guò)<import>標(biāo)簽引入其他的XML配置文件顷链;
  • 通過(guò)<bean>標(biāo)簽導(dǎo)入Java配置文件到XML配置文件目代,例如
<bean class="soundsystem.CDConfig" />

通常的做法是:無(wú)論使用JavaConfig或者XML裝配,都要?jiǎng)?chuàng)建一個(gè)root configuration嗤练,即模塊化配置定義榛了;并且在這個(gè)配置文件中開啟自動(dòng)掃描機(jī)制:<context:component-scan>或者@ComponentScan

2.6 總結(jié)

這一章中學(xué)習(xí)了Spring 裝配bean的三種方式:自動(dòng)裝配煞抬、基于Java文件裝配和基于XML文件裝配霜大。

由于自動(dòng)裝配幾乎不需要手動(dòng)定義bean,建議優(yōu)先選擇自動(dòng)裝配革答;如何必須使用顯式配置战坤,則優(yōu)先選擇基于Java文件裝配這種方式,因?yàn)橄啾扔赬ML文件残拐,Java文件具備更多的能力途茫、類型安全等特點(diǎn);但是也有一種情況必須使用XML配置文件溪食,即你需要使用某個(gè)名字空間(name space)囊卜,該名字空間只在XML文件中可以使用。

參考資料

  1. http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html

本號(hào)專注于后端技術(shù)眠菇、JVM問(wèn)題排查和優(yōu)化边败、Java面試題、個(gè)人成長(zhǎng)和自我管理等主題捎废,為讀者提供一線開發(fā)者的工作和成長(zhǎng)經(jīng)驗(yàn)笑窜,期待你能在這里有所收獲。


javaadu
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末登疗,一起剝皮案震驚了整個(gè)濱河市排截,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌辐益,老刑警劉巖断傲,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異智政,居然都是意外死亡认罩,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門续捂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)垦垂,“玉大人宦搬,你說(shuō)我怎么就攤上這事〗俎郑” “怎么了间校?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)页慷。 經(jīng)常有香客問(wèn)我憔足,道長(zhǎng),這世上最難降的妖魔是什么酒繁? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任滓彰,我火速辦了婚禮,結(jié)果婚禮上欲逃,老公的妹妹穿的比我還像新娘找蜜。我一直安慰自己,他們只是感情好稳析,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著弓叛,像睡著了一般彰居。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上撰筷,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天陈惰,我揣著相機(jī)與錄音,去河邊找鬼毕籽。 笑死抬闯,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的关筒。 我是一名探鬼主播溶握,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蒸播!你這毒婦竟也來(lái)了睡榆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤袍榆,失蹤者是張志新(化名)和其女友劉穎胀屿,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體包雀,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡宿崭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了才写。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片葡兑。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡奴愉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出铁孵,到底是詐尸還是另有隱情锭硼,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布蜕劝,位于F島的核電站檀头,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏岖沛。R本人自食惡果不足惜暑始,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望婴削。 院中可真熱鬧廊镜,春花似錦、人聲如沸唉俗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)虫溜。三九已至雹姊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間衡楞,已是汗流浹背吱雏。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瘾境,地道東北人歧杏。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像迷守,于是被迫代替她去往敵國(guó)和親犬绒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容