Spring AOP簡(jiǎn)介

為什么需要AOP?

AOP(面向切面編程)和OOP(面向?qū)ο缶幊?一樣芳来,也是一種編程思想。具體來(lái)說(shuō),AOPOOP的一種有效補(bǔ)充资溃,以求解決OOP中的一些弊端。在OOP的思想下烈炭,我們可以很輕松的將一些業(yè)務(wù)需求抽象成一個(gè)個(gè)類(lèi)溶锭,形成可重用的模塊。但是遇到系統(tǒng)需求時(shí)符隙,往往捉襟見(jiàn)肘趴捅,造成大量的重復(fù)代碼,比如我們最常見(jiàn)的打印日志和權(quán)限驗(yàn)證的需求霹疫。

橫切關(guān)注點(diǎn)

上圖中上拱绑,Class AClass B丽蝎、Class C這三個(gè)不同的類(lèi)欺栗,卻都需要在某個(gè)方法執(zhí)行前進(jìn)行權(quán)限驗(yàn)證,在執(zhí)行后進(jìn)行日志記錄征峦。這樣橫跨了多個(gè)類(lèi)的共同需求迟几,我們稱(chēng)為橫切關(guān)注點(diǎn)。在這里顯然varify()log()在多個(gè)類(lèi)中重復(fù)栏笆,當(dāng)然重復(fù)代碼還不是最主要的問(wèn)題类腮,當(dāng)我們需要修改verify()log()方法時(shí),我們要在A蛉加、B蚜枢、C三個(gè)類(lèi)中都進(jìn)行修改缸逃,當(dāng)類(lèi)的數(shù)目越來(lái)越多,就會(huì)牽一發(fā)而動(dòng)全身厂抽。那么有人會(huì)說(shuō)需频,我們可以把verify()log()抽象成一個(gè)類(lèi),如果需要進(jìn)行修改時(shí),就在這個(gè)類(lèi)中進(jìn)行挪丢。這個(gè)方案似乎可行,但是仍然存在問(wèn)題

  • 因?yàn)楹芏鄷r(shí)候橫切關(guān)注點(diǎn)的邏輯和業(yè)務(wù)邏輯糾纏在一起乾蓬,并不是很好的進(jìn)行抽取族奢。
業(yè)務(wù)邏輯橫切邏輯糾纏
  • 假設(shè)我們想將log()調(diào)整到方法執(zhí)行之前铜跑,或者說(shuō)在方法執(zhí)行前也添加log()打印日志囤锉,那我們還是需要去大量的類(lèi)中手動(dòng)添加代碼驱入,這個(gè)方法治標(biāo)不治本。
  • 如果能把所有的橫切關(guān)注點(diǎn)的邏輯直接抽離出來(lái),讓程序員專(zhuān)注于業(yè)務(wù)代碼就好了正卧,這樣子代碼的可讀性也會(huì)大大提高窘行。
    AOP就是為了幫助我們解決上述問(wèn)題而生的捏顺,具體來(lái)說(shuō)就是
  1. 幫助我們把橫切關(guān)注點(diǎn)從多個(gè)類(lèi)中抽取出來(lái),形成Aspect(切面)
  2. 程序運(yùn)行時(shí)/編譯時(shí)逛艰,幫我們把這些橫切邏輯重新插入到每個(gè)類(lèi)中對(duì)應(yīng)的位置(pointcut)脸甘,這個(gè)過(guò)程叫做weaver(織入)。

這種在運(yùn)行時(shí)铆遭,動(dòng)態(tài)地將代碼切入到類(lèi)的指定方法枚荣、指定位置上的編程思想就是面向切面的編程(AOP)衙伶。AOP是一種編程思想蹦漠,而Spring AOP則是AOP思想的具體實(shí)現(xiàn)车海。

Spring AOP的使用

在具體應(yīng)用之前研铆,讓我們先熟悉AOP下的一些術(shù)語(yǔ)

術(shù)語(yǔ) 解釋
jointpoint 系統(tǒng)運(yùn)行前,AOP的功能模塊需要織入到OOP的功能模塊中去州叠,jointpoint就是指能夠進(jìn)行織入操作的執(zhí)行點(diǎn)
pointcut 切點(diǎn)棵红,一次織入過(guò)程中, 具體的jointpoint信息,比如要在A()方法處織入橫切邏輯咧栗,那么A()就是pointcut
advice 通知逆甜,代表具體的橫切邏輯虱肄,可以類(lèi)比OOP中的method,注意:advice還指明了執(zhí)行橫切邏輯的時(shí)間的交煞,比如在A()執(zhí)行方法之前執(zhí)行咏窿,還是在其之后執(zhí)行等
aspect 切面,point + advice = aspect素征, 在哪些切點(diǎn)(切點(diǎn)是個(gè)集合)上執(zhí)行何種橫切邏輯(比如打印日志)就是一個(gè)切面

在不同的AOP實(shí)現(xiàn)中集嵌,jointpoint的粒度不同,在Spring AOP中御毅,這個(gè)jointpoint是方法級(jí)別的根欧,也就是只提供方法攔截,但即便這樣端蛆,也足以滿(mǎn)足80%的業(yè)務(wù)需求了凤粗。advice除了定義了橫切邏輯,還定義了橫切邏輯執(zhí)行的時(shí)機(jī)欺税,在Spring AOP中有前置侈沪、后置、返回晚凿、異常亭罪、環(huán)繞五種Advice,例如前置型Advice,表示在pointcut前執(zhí)行橫切邏輯歼秽,下面會(huì)舉例詳細(xì)說(shuō)明应役。

前置Advice

首先讓我們定義一個(gè)People類(lèi),它包含一個(gè)eatFruit表示吃水果的這個(gè)行為燥筷,我們將嘗試以這個(gè)訪(fǎng)問(wèn)為pointcut箩祥,來(lái)進(jìn)行織入工作。然后我們來(lái)定義Advice肆氓,在Spring AOP中袍祖,Advice是實(shí)現(xiàn)了對(duì)應(yīng)接口的類(lèi),如果我們要實(shí)現(xiàn)一個(gè)前置型的Advice谢揪,就要實(shí)現(xiàn)MethodBeforeAdvice中的方法蕉陋。在這里我們定義了一個(gè)名為BeforeEat的前置型Advice,表示吃之前要執(zhí)行的橫切邏輯拨扶。

  • people 類(lèi)
public class People {
    public void eatFruit(){
        System.out.println("正在吃水果");
    }
}
  • BeforeEat 類(lèi)
public class BeforeEat implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("eat方法的前置通知: 我要開(kāi)始吃了凳鬓!");
    }
}

接下來(lái)讓我們把這兩個(gè)類(lèi)注入到Spring IOC容器中,交由Spring管理患民。

    <bean id="people" class="aop.People">
    </bean>

    <bean id="beforeEat" class="aop.BeforeEat">
    </bean>

之后最重要的是告訴Spring缩举,pointcut是哪些方法?,和pointcut關(guān)聯(lián)Advice是哪一個(gè)仅孩,讓我們完善aop config托猩。

    <aop:config>
        <aop:pointcut expression="execution(public void aop.People.eatFruit())" id="pointcut"></aop:pointcut>
        <aop:advisor advice-ref="beforeEat" pointcut-ref="pointcut"></aop:advisor>
    </aop:config>

<aop:pointcut>表示pointcutPeople類(lèi)的eatFruit方。之前我們有提到過(guò)point + advice = aspect杠氢,而<aop:advisor>標(biāo)簽中的就可以理解為aspect,它關(guān)聯(lián)了與advice對(duì)應(yīng)的pointcut站刑。下面讓我們調(diào)用下People類(lèi)的eatFruit()方法看看是什么效果。

執(zhí)行前需要先導(dǎo)入aspectJweaver.jar包

  • 調(diào)用earFruit()方法
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        People people = (People)applicationContext.getBean("people");
        people.eatFruit();
    }
  • 執(zhí)行結(jié)果
eat方法的前置通知: 我要開(kāi)始吃了鼻百!
正在吃水果

可以發(fā)現(xiàn)橫切邏輯在方法執(zhí)行前被調(diào)用了绞旅。
之前我們說(shuō)過(guò), pointcut在這里可以看作要被織入橫切邏輯的具體位置(方法)的集合,因此pointcut內(nèi)部可以包含多種方法温艇,讓我們?cè)?code>People類(lèi)中添加一個(gè)drinkSomething方法因悲。

public class People {

    public void eatFruit(){
        System.out.println("正在吃水果");
    }
    public void drinkSomething(String sth){
        System.out.println("正在喝"+sth);
    }
}

把這個(gè)方法也加入到當(dāng)前的pointcut中去。

    <aop:config>
        <aop:pointcut expression="execution(public void aop.People.eatFruit()) or
                execution(public void aop.People.drinkSomething(String)) " id="pointcut"></aop:pointcut>
        <aop:advisor advice-ref="beforeEat" pointcut-ref="pointcut"></aop:advisor>
    </aop:config>

pointcut中兩個(gè)方法用or連接勺爱。運(yùn)行結(jié)果是在這2個(gè)方法調(diào)用前都會(huì)執(zhí)行橫切邏輯BeforeEat

eat方法的前置通知: 我要開(kāi)始吃了晃琳!
正在吃水果
eat方法的前置通知: 我要開(kāi)始吃了!
正在喝牛奶

Process finished with exit code 0

可以看到pointcut中的expression是支持集合的交并補(bǔ)運(yùn)算的琐鲁,此外還支持通配符的方式卫旱,來(lái)指代一類(lèi)方法。比如我們可以修改<aop:config>為:

    <aop:config>
        <aop:pointcut expression="execution(public void * (String))" id="pointcut"></aop:pointcut>
        <aop:advisor advice-ref="beforeEat" pointcut-ref="pointcut"></aop:advisor>
    </aop:config>

就表示任何以String為參數(shù)(不限方法名)的方法围段,在這里也就只有drinkSomething(String sth)滿(mǎn)足條件顾翼,嘗試運(yùn)行發(fā)現(xiàn)也的確只在這個(gè)方法前執(zhí)行了橫切邏輯。通過(guò)通配符和集合運(yùn)算的方式奈泪,可以容易的指定一類(lèi)具體的的方法為pointcut适贸。

正在吃水果
eat方法的前置通知: 我要開(kāi)始吃了!
正在喝牛奶

Process finished with exit code 0

現(xiàn)在讓我們?cè)倩氐?code>Advice類(lèi)的定義上涝桅,看看接口方法中的參數(shù)都代表了什么拜姿。

public class BeforeEat implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println(method+" " + Arrays.toString(objects) + " " + o);
        System.out.println("eat方法的前置通知: 我要開(kāi)始吃了!");
    }
}

執(zhí)行結(jié)果

public void aop.People.drinkSomething(java.lang.String) [牛奶] aop.People@424e1977
eat方法的前置通知: 我要開(kāi)始吃了冯遂!
正在喝牛奶

Process finished with exit code 0

可以發(fā)現(xiàn)method即與橫切邏輯advice關(guān)聯(lián)的具體方法蕊肥,在這里就是public void aop.People.drinkSomething(java.lang.String), Object[] objects則是傳入該方法的參數(shù),object則是執(zhí)行橫切邏輯的方法所屬的對(duì)象實(shí)例蛤肌,這里就是IOCid=people的這個(gè)bean壁却。

后置Advice

后置型Advice與前置型Advice正相反,表示在pointcut之后執(zhí)行橫切邏輯寻定。我們編寫(xiě)一個(gè)名為AfterEat的后置型Advice儒洛。

public class AfterEat implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("吃完了精耐,洗洗手狼速。");
    }
}

為其編寫(xiě)xml配置。

    <bean id="afterEat" class="aop.AfterEat">
    </bean>
    <aop:config>
        <aop:pointcut expression="execution(public void aop.People.eatFruit()) or
                execution(public void aop.People.drinkSomething(String)) " id="pointcut"></aop:pointcut>
        <aop:advisor advice-ref="afterEat" pointcut-ref="pointcut"></aop:advisor>
    </aop:config>

執(zhí)行結(jié)果

public void aop.People.eatFruit() [] aop.People@1190200a
eat方法的前置通知: 我要開(kāi)始吃了卦停!//前置
正在吃水果
吃完了向胡,洗洗手恼蓬。//后置
public void aop.People.drinkSomething(java.lang.String) [牛奶] aop.People@1190200a
eat方法的前置通知: 我要開(kāi)始吃了!//前置
正在喝牛奶
吃完了僵芹,洗洗手处硬。//后置

注意到AfterReturningAdvice接口中的afterReturning方法中的參數(shù)與前置Advice有差別,讓我們嘗試打印一下拇派。

public class AfterEat implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println(method+" " + Arrays.toString(objects) + " " + o + " " + o1);
        System.out.println("吃完了荷辕,洗洗手。");
    }
}

輸出結(jié)果

public void aop.People.eatFruit() [] null aop.People@1190200

可以看到o1輸出的是對(duì)象實(shí)例件豌,而o輸出的值是null, 那么o代表什么呢疮方?讓我們修改drinkSomething(String)的返回值為int,再打印一次

    public int drinkSomething(String sth){
        System.out.println("正在喝"+sth);
        return 0;
    }
public int aop.People.drinkSomething(java.lang.String) [牛奶] 0 aop.People@1190200a

發(fā)現(xiàn)o的值變?yōu)?code>0,也就是說(shuō)其代表了橫切邏輯執(zhí)行前這個(gè)方法的返回值茧彤。

異常Advice

異常Advice指的是當(dāng)pointcut中的方法拋出異常時(shí)骡显,將會(huì)執(zhí)行的橫切邏輯。

  • 編寫(xiě)異常Advice
public class WhenException implements ThrowsAdvice {
/*
 * <pre class="code">public void afterThrowing(Exception ex)</pre>
 * <pre class="code">public void afterThrowing(RemoteException)</pre>
 * <pre class="code">public void afterThrowing(Method method, Object[] args, Object target, Exception ex)</pre>
 * <pre class="code">public void afterThrowing(Method method, Object[] args, Object target, ServletException ex)</pre>
*/

}

ThrowsAdvice這個(gè)接口并沒(méi)有要求我們實(shí)現(xiàn)任何接口方法曾掂,而是在文檔里給出了一些示例惫谤,還告訴我們Method method, Object[] args, Object target,這3個(gè)打包在一起的參數(shù)是可選的珠洗,如果你想獲得更詳細(xì)的信息溜歪,就加上它們。

  • 實(shí)現(xiàn)異常Advice類(lèi)
public class WhenException implements ThrowsAdvice {
    public void afterThrowing(Exception ex) {
        System.out.println("異常Advice : 發(fā)生了異常");
        System.out.println(ex.getMessage());
    }
}

編寫(xiě)app config

    <bean id="whenException" class="aop.WhenException"></bean>
    <aop:config>
        <aop:pointcut expression="execution(public void aop.People.eatFruit()) or
                execution(public int aop.People.drinkSomething(String)) " id="pointcut"></aop:pointcut>
        <aop:advisor advice-ref="whenException" pointcut-ref="pointcut"></aop:advisor>
    </aop:config>

再在drinkSomething()方法里故意引起一個(gè)異常险污。

    public int drinkSomething(String sth){
        System.out.println("正在喝"+sth);
        int a = 1 / 0;
        return 0;
    }

執(zhí)行結(jié)果

public int aop.People.drinkSomething(java.lang.String) [牛奶] aop.People@4c39bec8
eat方法的前置通知: 我要開(kāi)始吃了痹愚!
正在喝牛奶
異常Advice : 發(fā)生了異常
/ by zero
環(huán)繞Advice

截至目前為止,我們已經(jīng)實(shí)驗(yàn)了前置蛔糯、后置拯腮、異常三種Advice。它們執(zhí)行的時(shí)機(jī)如下蚁飒。

advice

環(huán)繞型Advice动壤,可以實(shí)現(xiàn)以上三種Advice的所有功能,即可以同時(shí)在上述的所有位置執(zhí)行橫切邏輯淮逻。

  • 實(shí)現(xiàn)一個(gè)環(huán)繞型Advice
public class AroundEat implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        try {

            System.out.println("環(huán)繞Advice: 方法執(zhí)行前" );// 前置

            Object result = invocation.proceed();// pointcut中方法的執(zhí)行

            System.out.println("環(huán)繞Advice: 方法執(zhí)行后" );// 后置

        } catch (Exception e) {
            System.out.println("環(huán)繞Advice: 發(fā)生異常");
        }

        return null;
    }
}

這里的關(guān)鍵是Object result = invocation.proceed()琼懊;,這里就相當(dāng)于執(zhí)行我們定義在pointcut中的方法,因此在這行語(yǔ)句前面執(zhí)行的邏輯爬早,相當(dāng)于前置advice哼丈,在這行語(yǔ)句后面執(zhí)行的邏輯,相當(dāng)于后置advice筛严。捕捉到異常后實(shí)現(xiàn)的邏輯就相當(dāng)于異常advice醉旦。
為其配置aop,進(jìn)行驗(yàn)證。

    <bean id="aroundEat" class="aop.AroundEat"></bean>

    <aop:config>
        <aop:pointcut expression="execution(public void aop.People.eatFruit()) or
                execution(public int aop.People.drinkSomething(String)) " id="pointcut"></aop:pointcut>
        <aop:advisor advice-ref="aroundEat" pointcut-ref="pointcut"></aop:advisor>
    </aop:config>
  • 運(yùn)行結(jié)果
環(huán)繞Advice: 方法執(zhí)行前
正在吃水果
環(huán)繞Advice: 方法執(zhí)行后
環(huán)繞Advice: 方法執(zhí)行前
正在喝牛奶
環(huán)繞Advice: 發(fā)生異常
利用注解的形式實(shí)現(xiàn)AOP

Spring AOP车胡,也提供了基于注解的形式實(shí)現(xiàn)AOP, 較XML配置的方法更加簡(jiǎn)單直觀檬输,我們來(lái)利用注解實(shí)現(xiàn)AOP,以前置Advice為例,將之前的BeforeEat改進(jìn)為基于注解的方式匈棘。

@Component("beforeEatAnnotation")
@Aspect
public class BeforeEatAnnotation {
    @Before("execution(public void aop.People.eatFruit())") //定義切點(diǎn)
    void before(){
        System.out.println("采用注解形式實(shí)現(xiàn)的前置通知");
    }

    @AfterReturning("execution(public void aop.People.eatFruit())")
    void after(){
        System.out.println("采用注解形式實(shí)現(xiàn)的后置通知");
    }
}

和我們之前基于XML的配置一樣丧慈,我們要定義具體的pointcut并且把其和關(guān)聯(lián)的Advice綁定起來(lái),在這個(gè)類(lèi)里我們可以在任意方法前加上@Before注解,表示該方法是一個(gè)前置advice主卫,然后在其括號(hào)內(nèi)注明pointcut逃默,這樣pointcutadvice很自然的關(guān)聯(lián)在一起了,所以也無(wú)需之前的<aop:advisor>來(lái)指明兩者關(guān)系了簇搅。@Aspect代表這個(gè)類(lèi)表示一個(gè)切面笑旺。@Component把這個(gè)類(lèi)交由Spring管理,注意配置自動(dòng)掃描馍资。
最后筒主,我還需要在xml中配置aop自動(dòng)代理。

    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

實(shí)驗(yàn)結(jié)果

采用注解形式實(shí)現(xiàn)的前置通知
正在吃水果
采用注解形式實(shí)現(xiàn)的后置通知

Process finished with exit code 0

之前利用接口的方式來(lái)實(shí)現(xiàn)AOP可以很容易的獲得目標(biāo)對(duì)象鸟蟹,方法名乌妙、參數(shù)等信息,利用注解的方式也可以實(shí)現(xiàn)建钥,這里需要借助一個(gè)特殊的JoinPoint類(lèi)藤韵。

@Component("adviceByAnnotation")
@Aspect
public class AdviceByAnnotation {
    @Before("execution(public void aop.People.eatFruit())") //定義切點(diǎn)
    void before(JoinPoint joinPoint){
        System.out.println(joinPoint.getTarget() + " " + Arrays.toString(joinPoint.getArgs()) + " " + joinPoint.getSignature());
        System.out.println("采用注解形式實(shí)現(xiàn)的前置通知");
    }

    @AfterReturning(pointcut="execution(public void aop.People.eatFruit())", returning = "returningValue")
    void after(JoinPoint joinPoint, Object returningValue){
        System.out.println("返回值為" + returningValue);
        System.out.println("采用注解形式實(shí)現(xiàn)的后置通知");
    }
}

可以發(fā)現(xiàn)pointcut中的特定方法的有關(guān)信息都已經(jīng)被包裝到JoinPoint類(lèi)中去了。對(duì)于以@AfterReturning標(biāo)注的后置Advice熊经,還可以指明獲取返回值泽艘。
實(shí)驗(yàn)結(jié)果如下

aop.People@140c9f39 [] void aop.People.eatFruit()
采用注解形式實(shí)現(xiàn)的前置通知
正在吃水果
返回值為null
采用注解形式實(shí)現(xiàn)的后置通知

類(lèi)似的我們還可以實(shí)現(xiàn)基于注解的異常Advice環(huán)繞Advice以及最終Advice

    @After("execution(public int aop.People.drinkSomething(String))")
    void after(){
        System.out.println("最終通知镐依,無(wú)論有沒(méi)有發(fā)生異常匹涮,都會(huì)執(zhí)行");
    }
    //異常通知
    @AfterThrowing("execution(public int aop.People.drinkSomething(String))")
    void afterException(){
        System.out.println("采用注解形式的異常通知");
    }
    //環(huán)繞通知
    @Around("execution(public int aop.People.drinkSomething(String))")
    void around(ProceedingJoinPoint proceedingJoinPoint) {
        try {
            System.out.println("采用注解形式的環(huán)繞通知[前置]");
            proceedingJoinPoint.proceed();
            System.out.println("采用注解形式的環(huán)繞通知[后置]");

        }catch (Throwable e) {
            System.out.println("采用注解形式的環(huán)繞通知[異常]");
        } finally {
            System.out.println("采用注解形式的環(huán)繞通知[最終]");
        }
    }

環(huán)繞Advice里,proceedingJoinPoint.proceed();就是真正執(zhí)行了pointcut集合中某個(gè)具體方法槐壳。注意這里區(qū)別最終和后置的區(qū)別然低,后置Advice如果發(fā)生異常則不會(huì)被執(zhí)行,而最終Advice是一定會(huì)被執(zhí)行的务唐。
執(zhí)行結(jié)果如下

采用注解形式的環(huán)繞通知[前置]
正在喝牛奶
采用注解形式的環(huán)繞通知[異常]
采用注解形式的環(huán)繞通知[最終]
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末雳攘,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子枫笛,更是在濱河造成了極大的恐慌吨灭,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件刑巧,死亡現(xiàn)場(chǎng)離奇詭異喧兄,居然都是意外死亡涩咖,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門(mén)繁莹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人特幔,你說(shuō)我怎么就攤上這事咨演。” “怎么了蚯斯?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵薄风,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我拍嵌,道長(zhǎng)遭赂,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任横辆,我火速辦了婚禮撇他,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘狈蚤。我一直安慰自己困肩,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布脆侮。 她就那樣靜靜地躺著锌畸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪靖避。 梳的紋絲不亂的頭發(fā)上潭枣,一...
    開(kāi)封第一講書(shū)人閱讀 52,158評(píng)論 1 308
  • 那天弦撩,我揣著相機(jī)與錄音氮发,去河邊找鬼。 笑死诈铛,一個(gè)胖子當(dāng)著我的面吹牛篡九,可吹牛的內(nèi)容都是我干的蚣抗。 我是一名探鬼主播,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼瓮下,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼翰铡!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起讽坏,我...
    開(kāi)封第一講書(shū)人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤锭魔,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后路呜,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體迷捧,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡织咧,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了漠秋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片笙蒙。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖庆锦,靈堂內(nèi)的尸體忽然破棺而出捅位,到底是詐尸還是另有隱情,我是刑警寧澤搂抒,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布艇搀,位于F島的核電站,受9級(jí)特大地震影響求晶,放射性物質(zhì)發(fā)生泄漏焰雕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一芳杏、第九天 我趴在偏房一處隱蔽的房頂上張望矩屁。 院中可真熱鬧,春花似錦爵赵、人聲如沸档插。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)郭膛。三九已至,卻和暖如春氛悬,著一層夾襖步出監(jiān)牢的瞬間则剃,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工如捅, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留棍现,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓镜遣,卻偏偏與公主長(zhǎng)得像己肮,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子悲关,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359

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