Dubbo源碼-Dubbo是如何隨心所欲自定義XML標簽的

叨叨

今天考慮了很久要不要寫這篇文章莫其。

距離《Dubbo源碼》系列的開篇到現(xiàn)在已經(jīng)快兩個月時間了。當時是想著工作上的RPC框架使用存在一些讓人頭疼的問題少辣,就來看看Dubbo給出了一套什么樣的解決方案讲婚。

結(jié)果另锋,寫完第一篇沒幾天总处,工作上因為要趕一個項目的進度,關(guān)小黑屋了睛蛛,前段時間剛放出來-_-!

琢磨著鹦马,做事不能半途而廢。今天就又打開了Dubbo項目忆肾,pull下代碼荸频,在十多個子模塊之間來回滾動,感覺都不是好惹的客冈,一時不知道從哪下手了旭从。再一想,Dubbo源碼系列不能就這么唐突的出一篇就結(jié)束了啊。

行和悦,思來想去退疫,還是接著從上篇提到的dubbo-demo模塊繼續(xù)往下說……

正文

下面是dubbo-demo-provider模塊下的dubbo-demo-provider.xml配置文件內(nèi)容


<pre style="margin: 0.5em 0px; padding: 0.4em 0.6em; border-radius: 8px; background: rgb(255, 255, 255); color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; font-family: Menlo; font-size: 9pt;"><?xml version="1.0" encoding="UTF-8"?> <!--
 Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.  You may obtain a copy of the License at   http://www.apache.org/licenses/LICENSE-2.0   Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
  xmlns="http://www.springframework.org/schema/beans"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
 http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">

    <!-- provider's application name, used for tracing dependency relationship -->
 <dubbo:application name="demo-provider"/>

    <!-- use zookeeper registry center to export service -->
 <dubbo:registry address="zookeeper://127.0.0.1:2181"/>

    <!-- use dubbo protocol to export service on port 20880 -->
 <dubbo:protocol name="dubbo" port="20880"/>

    <!-- service implementation, as same as regular local bean -->
 <bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl"/>

    <!-- declare the service interface to be exported -->
 <dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService"/>

</beans></pre>

注意:這里的dubbo:registry在上篇已經(jīng)說明,有過改動鸽素,這里使用的是zookeeper的配置

dubbo-demo-provider與常見的xml文件有何不同

1褒繁、除了<bean id="demoService" ... 其他的標簽我們?nèi)粘6紱]有使用過

2、<beans>標簽中除了常見的“http://www.springframework.org/schema/beans/spring-beans-4.3.xsd”還多了“http://dubbo.apache.org/schema/dubbo/dubbo.xsd

我們平常使用的xml都是在Spring框架下馍忽,所以可以看到熟悉的<beans>棒坏、<bean> 、<import>等遭笋。那有沒有想過坝冕,為什么定義一個<bean>標簽就是生命一個bean,就能夠在Spring上下文注冊一個類的實例呢瓦呼?其實喂窟,這些工作Spring在幕后都幫我們做好了,這個我在之前的《Spring讀書筆記》系列有著重寫過吵血。

稍稍掃一眼Dubbo的代碼谎替,就會發(fā)現(xiàn),Dubbo也是基于Spring開發(fā)的蹋辅,使用了Spring的很多特性钱贯,但是鑒于自己的業(yè)務(wù)框架需求,需要做相應的拓展和定制化侦另,實現(xiàn)一套自己的自定義XML標簽秩命。那么這些標簽又是如何生效和被使用的呢

基于Spring的Schema提供自定義配置支持

在dubbo-demo-provider.xml中見到的那些標簽也是基于Spring的Schema實現(xiàn)的一套自定義標簽。

這一套流程主要包括以下幾個步驟:

  • 編寫配置類和屬性

  • 編寫XSD文件

  • 編寫spring.handlers和spring.schemas

  • 編寫DubboNamespaceHandler和DubboBeanDefinitionParser褒傅,主要負責標簽解析

編寫配置類和屬性

針對dubbo-demo-provider中的<dubbo:application name="demo-provider"/>來說弃锐,該標簽對應的配置類在dubbo-config-api模塊下的ApplicationConfig。


<pre style="margin: 0.5em 0px; padding: 0.4em 0.6em; border-radius: 8px; background: rgb(255, 255, 255); color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; font-family: Menlo; font-size: 9pt;">package com.alibaba.dubbo.config;

import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.compiler.support.AdaptiveCompiler;
import com.alibaba.dubbo.common.logger.LoggerFactory;
import com.alibaba.dubbo.config.support.Parameter;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * ApplicationConfig * * @export
  */ public class ApplicationConfig extends AbstractConfig {

    private static final long serialVersionUID = 5508512956753757169L;

    // application name
 private String name;

    // module version
 private String version;

    // application owner
 private String owner;

    // application's organization (BU)
 private String organization;

    // architecture layer
 private String architecture;

    // environment, e.g. dev, test or production
 private String environment;

    // Java compiler
 private String compiler;

    // logger
 private String logger;

    // registry centers
 private List<RegistryConfig> registries;

    // monitor center
 private MonitorConfig monitor;

    // is default or not
 private Boolean isDefault;

    // directory for saving thread dump
 private String dumpDirectory;

    private Boolean qosEnable;

    private Integer qosPort;

    private Boolean qosAcceptForeignIp;

    // customized parameters
 private Map<String, String> parameters;

    public ApplicationConfig() {
    }

    public ApplicationConfig(String name) {
        setName(name);
    }

    @Parameter(key = Constants.APPLICATION_KEY, required = true)
    public String getName() {
        return name;
    }

    public void setName(String name) {
        checkName("name", name);
        this.name = name;
        if (id == null || id.length() == 0) {
            id = name;
        }
    }

    @Parameter(key = "application.version")
    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    public String getOwner() {
        return owner;
    }

    public void setOwner(String owner) {
        checkMultiName("owner", owner);
        this.owner = owner;
    }

    public String getOrganization() {
        return organization;
    }

    public void setOrganization(String organization) {
        checkName("organization", organization);
        this.organization = organization;
    }

    public String getArchitecture() {
        return architecture;
    }

    public void setArchitecture(String architecture) {
        checkName("architecture", architecture);
        this.architecture = architecture;
    }

    public String getEnvironment() {
        return environment;
    }

    public void setEnvironment(String environment) {
        checkName("environment", environment);
        if (environment != null) {
            if (!("develop".equals(environment) || "test".equals(environment) || "product".equals(environment))) {
                throw new IllegalStateException("Unsupported environment: " + environment + ", only support develop/test/product, default is product.");
            }
        }
        this.environment = environment;
    }

    public RegistryConfig getRegistry() {
        return registries == null || registries.isEmpty() ? null : registries.get(0);
    }

    public void setRegistry(RegistryConfig registry) {
        List<RegistryConfig> registries = new ArrayList<RegistryConfig>(1);
        registries.add(registry);
        this.registries = registries;
    }

    public List<RegistryConfig> getRegistries() {
        return registries;
    }

    @SuppressWarnings({"unchecked"})
    public void setRegistries(List<? extends RegistryConfig> registries) {
        this.registries = (List<RegistryConfig>) registries;
    }

    public MonitorConfig getMonitor() {
        return monitor;
    }

    public void setMonitor(MonitorConfig monitor) {
        this.monitor = monitor;
    }

    public void setMonitor(String monitor) {
        this.monitor = new MonitorConfig(monitor);
    }

    public String getCompiler() {
        return compiler;
    }

    public void setCompiler(String compiler) {
        this.compiler = compiler;
        AdaptiveCompiler.setDefaultCompiler(compiler);
    }

    public String getLogger() {
        return logger;
    }

    public void setLogger(String logger) {
        this.logger = logger;
        LoggerFactory.setLoggerAdapter(logger);
    }

    public Boolean isDefault() {
        return isDefault;
    }

    public void setDefault(Boolean isDefault) {
        this.isDefault = isDefault;
    }

    @Parameter(key = Constants.DUMP_DIRECTORY)
    public String getDumpDirectory() {
        return dumpDirectory;
    }

    public void setDumpDirectory(String dumpDirectory) {
        this.dumpDirectory = dumpDirectory;
    }

    @Parameter(key = Constants.QOS_ENABLE)
    public Boolean getQosEnable() {
        return qosEnable;
    }

    public void setQosEnable(Boolean qosEnable) {
        this.qosEnable = qosEnable;
    }

    @Parameter(key = Constants.QOS_PORT)
    public Integer getQosPort() {
        return qosPort;
    }

    public void setQosPort(Integer qosPort) {
        this.qosPort = qosPort;
    }

    @Parameter(key = Constants.ACCEPT_FOREIGN_IP)
    public Boolean getQosAcceptForeignIp() {
        return qosAcceptForeignIp;
    }

    public void setQosAcceptForeignIp(Boolean qosAcceptForeignIp) {
        this.qosAcceptForeignIp = qosAcceptForeignIp;
    }

    public Map<String, String> getParameters() {
        return parameters;
    }

    public void setParameters(Map<String, String> parameters) {
        checkParameterName(parameters);
        this.parameters = parameters;
    }
}</pre>

在ApplicationConfig同級目錄下殿托,還包括其他出現(xiàn)在dubbo-demo-provider.xml中出現(xiàn)自定義標簽類霹菊,如RegistryConfig、ProtocolConfig

image

注意:<dubbo:application name="demo-provider"/>標簽中的dubbo對應的聲明在dubbo-demo-provider.xml中的xmlns:dubbo="http://dubbo.apache.org/schema/dubbo支竹,這里的xmlns其實就是一個命名空間的概念旋廷。

編寫XSD文件

XSD文件已經(jīng)在dubbo-demo-provider文件中定義好了,dubbo.xsd在dubbo-config-spring模塊下礼搁,內(nèi)容較長饶碘,舉Application為例


...

<pre style="margin: 0.5em 0px; padding: 0.4em 0.6em; border-radius: 8px; background: rgb(255, 255, 255); color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; font-family: Menlo; font-size: 9pt;"><xsd:element name="application" type="applicationType">
    <xsd:annotation>
        <xsd:documentation><![CDATA[ The application config ]]></xsd:documentation>
    </xsd:annotation>
</xsd:element></pre>

...

<pre style="margin: 0.5em 0px; padding: 0.4em 0.6em; border-radius: 8px; background: rgb(255, 255, 255); color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; font-family: Menlo; font-size: 9pt;"><xsd:complexType name="applicationType">
    <xsd:sequence minOccurs="0" maxOccurs="unbounded">
        <xsd:element ref="parameter" minOccurs="0" maxOccurs="unbounded"/>
    </xsd:sequence>
    <xsd:attribute name="id" type="xsd:ID">
        <xsd:annotation>
            <xsd:documentation><![CDATA[ The unique identifier for a bean. ]]></xsd:documentation>
        </xsd:annotation>
    </xsd:attribute>
    <xsd:attribute name="name" type="xsd:string" use="required">
        <xsd:annotation>
            <xsd:documentation><![CDATA[ The application name. ]]></xsd:documentation>
        </xsd:annotation>
    </xsd:attribute>
    <xsd:attribute name="version" type="xsd:string">
        <xsd:annotation>
            <xsd:documentation><![CDATA[ The application version. ]]></xsd:documentation>
        </xsd:annotation>
    </xsd:attribute>
    <xsd:attribute name="owner" type="xsd:string">
        <xsd:annotation>
            <xsd:documentation><![CDATA[ The application owner name (email prefix). ]]></xsd:documentation>
        </xsd:annotation>
    </xsd:attribute>
    <xsd:attribute name="organization" type="xsd:string">
        <xsd:annotation>
            <xsd:documentation><![CDATA[ The organization name. ]]></xsd:documentation>
        </xsd:annotation>
    </xsd:attribute>
    <xsd:attribute name="architecture" type="xsd:string">
        <xsd:annotation>
            <xsd:documentation><![CDATA[ The architecture. ]]></xsd:documentation>
        </xsd:annotation>
    </xsd:attribute>
    <xsd:attribute name="environment" type="xsd:string">
        <xsd:annotation>
            <xsd:documentation><![CDATA[ The application environment, eg: dev/test/run ]]></xsd:documentation>
        </xsd:annotation>
    </xsd:attribute>
    <xsd:attribute name="compiler" type="xsd:string">
        <xsd:annotation>
            <xsd:documentation><![CDATA[ The java code compiler. ]]></xsd:documentation>
        </xsd:annotation>
    </xsd:attribute>
    <xsd:attribute name="logger" type="xsd:string">
        <xsd:annotation>
            <xsd:documentation><![CDATA[ The application logger. ]]></xsd:documentation>
        </xsd:annotation>
    </xsd:attribute>
    <xsd:attribute name="registry" type="xsd:string" use="optional">
        <xsd:annotation>
            <xsd:documentation><![CDATA[ The application registry. ]]></xsd:documentation>
        </xsd:annotation>
    </xsd:attribute>
    <xsd:attribute name="monitor" type="xsd:string" use="optional">
        <xsd:annotation>
            <xsd:documentation><![CDATA[ The application monitor. ]]></xsd:documentation>
        </xsd:annotation>
    </xsd:attribute>
    <xsd:attribute name="default" type="xsd:string" use="optional">
        <xsd:annotation>
            <xsd:documentation><![CDATA[ Is default. ]]></xsd:documentation>
        </xsd:annotation>
    </xsd:attribute>
</xsd:complexType></pre>

...

代碼中上面一部分代碼<xsd:element name="application" type="applicationType">表示標簽的名稱

<xsd:attribute name="name" type="xsd:string" use="required">表示的是application標簽的屬性,與ApplicationConfig類的屬性是一一對應的關(guān)系馒吴。這里表示有一個屬性名為name扎运,且是String類型瑟曲,必填字段。

編寫spring.handlers和spring.schemas

僅僅有上面的配置類和XSD文件還是無法讓自定義標簽工作豪治,因為Spring還無法發(fā)現(xiàn)這些自定義標簽洞拨,更別提讓其發(fā)揮該有的作用了。

這時候鬼吵,需要添加兩個配置文件spring.handlers和spring.schemas扣甲,從文件字面意思就可以知道,這兩個配置文件起到了貫通的作用齿椅。spring.handlers用于配置具體的解析類琉挖,下面會提到,spring.schemas用于指明schemas的文件路徑涣脚。

我們可以在dubbo-config-spring模塊中看到這兩個配置文件

image

Spring會在啟動容器的時候加載META-INF目錄下的這兩個配置文件并加載對應的解析類示辈。

編寫DubboNamespaceHandler和DubboBeanDefinitionParser,主要負責標簽解析

DubboNamespaceHandler類位于dubbo-config-spring模塊下


<pre style="margin: 0.5em 0px; padding: 0.4em 0.6em; border-radius: 8px; background: rgb(255, 255, 255); color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; font-family: Menlo; font-size: 9pt;">/*
 * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements.  See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License.  You may obtain a copy of the License at * *     http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.dubbo.config.spring.schema;

import com.alibaba.dubbo.common.Version;
import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.ConsumerConfig;
import com.alibaba.dubbo.config.ModuleConfig;
import com.alibaba.dubbo.config.MonitorConfig;
import com.alibaba.dubbo.config.ProtocolConfig;
import com.alibaba.dubbo.config.ProviderConfig;
import com.alibaba.dubbo.config.RegistryConfig;
import com.alibaba.dubbo.config.spring.ReferenceBean;
import com.alibaba.dubbo.config.spring.ServiceBean;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

/**
 * DubboNamespaceHandler * * @export
  */ public class DubboNamespaceHandler extends NamespaceHandlerSupport {

    static {
        Version.checkDuplicate(DubboNamespaceHandler.class);
    }

    @Override
 public void init() {
        registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
        registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
        registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
        registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
        registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
        registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
        registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
        registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
        registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
        registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
    }

}
</pre>

是不是又看到了之前在dubbo-demo-provider.xml配置文件中看到那些標簽遣蚀。沒錯矾麻,真正給那些標簽賦能的功能代碼就在這里。

具體的解析工作交給了dubbo-config-spring模塊下的DubboBeanDefinitionParser類芭梯,該類實現(xiàn)了Spring的BeanDefinitionParser接口险耀,該類的一個核心方法就是parse()方法,其抽絲剝繭解析標簽玖喘,加載bean的思路其實和之前在《Spring讀書筆記》系列中介紹的一樣甩牺。最終都是解析并轉(zhuǎn)化為BeanDefinition對象并塞到Spring的上下文中,完成Bean的加載累奈。

我們可以以debug模式啟動dubbo-demo-provider模塊中的Provider類贬派,通過打斷點,會發(fā)現(xiàn)首先會執(zhí)行DubboNamespaceHandler類中的init方法澎媒,然后進入DubboBeanDefinitionParser類中的parse方法搞乏。

配合dubbo-demo-provider.xml配置文件中的<dubbo:registry address="zookeeper://127.0.0.1:2181"/>,我們在調(diào)試的時候發(fā)現(xiàn)解析后對應的BeanDefinition如下

image

通過這樣一個過程戒努,就實現(xiàn)了將XML自定義的標簽加載到Spring容器中请敦,而不需要使用Spring自己的bean去定義。

明白了這個流程储玫,后面看Dubbo的其他配置文件里面那些陌生的標簽就不會蒙圈了侍筛。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市缘缚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌敌蚜,老刑警劉巖桥滨,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡齐媒,警方通過查閱死者的電腦和手機蒲每,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來喻括,“玉大人邀杏,你說我怎么就攤上這事』Q” “怎么了望蜡?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長拷恨。 經(jīng)常有香客問我脖律,道長,這世上最難降的妖魔是什么腕侄? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任小泉,我火速辦了婚禮,結(jié)果婚禮上冕杠,老公的妹妹穿的比我還像新娘微姊。我一直安慰自己,他們只是感情好分预,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布兢交。 她就那樣靜靜地躺著,像睡著了一般噪舀。 火紅的嫁衣襯著肌膚如雪魁淳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天与倡,我揣著相機與錄音界逛,去河邊找鬼。 笑死纺座,一個胖子當著我的面吹牛息拜,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播净响,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼叁幢,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了替饿?” 一聲冷哼從身側(cè)響起滤馍,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎配乓,沒想到半個月后仿滔,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體惠毁,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年崎页,在試婚紗的時候發(fā)現(xiàn)自己被綠了鞠绰。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡飒焦,死狀恐怖蜈膨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情牺荠,我是刑警寧澤翁巍,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站志电,受9級特大地震影響曙咽,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜挑辆,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一例朱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鱼蝉,春花似錦洒嗤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至洁奈,卻和暖如春间唉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背利术。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工呈野, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人印叁。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓被冒,卻偏偏與公主長得像,于是被迫代替她去往敵國和親轮蜕。 傳聞我的和親對象是個殘疾皇子昨悼,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

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