淺析Spring自定義標(biāo)簽的使用

作者: 一字馬胡
轉(zhuǎn)載標(biāo)志 【2017-11-17】

更新日志

日期 更新內(nèi)容 備注
2017-11-17 新建文章 初版
2017-11-18 修改幾個(gè)錯(cuò)誤 xx

導(dǎo)入

Spring框架的一大強(qiáng)大之處就是框架的設(shè)計(jì)具有很好的可擴(kuò)展性,所以只要有想象力陆赋,就可以在Spring框架上作出擴(kuò)展旺罢,比如纱控,在學(xué)會(huì)了熟練使用Spring的內(nèi)置標(biāo)簽之后猫十,如果我們想要設(shè)計(jì)自己的標(biāo)簽鸟蜡,Spring是支持這種創(chuàng)新的艺糜,本文將結(jié)合實(shí)際的例子來(lái)說(shuō)明如何使用Spring提供的擴(kuò)展接口來(lái)設(shè)計(jì)自己的自定義標(biāo)簽微王,并且實(shí)現(xiàn)一些動(dòng)作蔼夜。在閱讀本文之前兼耀,你可以首先閱讀下面的兩篇鏈接文章,以更快的屬性Spring的生命周期等內(nèi)容求冷,可以更流暢的閱讀和理解本文的內(nèi)容:

Spring的BeanFactory和FactoryBean
Spring Bean 的生命周期

下面再次放上Spring Bean的生命周期圖瘤运,因?yàn)楸疚牡膬?nèi)容涉及到Bean的生命周期,自定義標(biāo)簽需要在Bean的生命周期內(nèi)做一些事情來(lái)操作bean匠题,所以屬性Spring Bean的生命周期在閱讀本文之前是必須的:

上面的流程圖已經(jīng)展示了Spring bean生命周期的詳細(xì)細(xì)節(jié)拯坟,我們知道了這些加載、初始化韭山、設(shè)置等一系列流程之后郁季,就可以在合適的環(huán)節(jié)加上我們想要的動(dòng)作,比如钱磅,我們可以使用BeanFactoryPostProcessor的postProcessBeanFactory方法來(lái)修改bean的屬性梦裂,例如,我們有一個(gè)bean的一個(gè)屬性A在spring配置文件中找不到盖淡,但是我們可以在BeanFactoryPostProcessor的postProcessBeanFactory方法里面使用方法的參數(shù)beanFactory來(lái)注冊(cè)一個(gè)A年柠。我們還可以使用BeanPostProcessor來(lái)修改我們的bean的屬性值,比如一個(gè)bean的一個(gè)屬性A褪迟,我們可以在BeanPostProcessor的postProcessBeforeInitialization方法和postProcessAfterInitialization方法來(lái)修改其值冗恨,這些方法需要配合其他的與Spring bean生命周期相關(guān)的類來(lái)做答憔。

可以將Spring bean的生命周期根據(jù)不同特點(diǎn)劃分為下面的幾類:

Bean自身的方法

包括我們?cè)谂渲胋ean時(shí)候設(shè)置的init-method方法和destroy-method方法。

Spring Bean級(jí)別的生命周期方法

包括BeanNameAware掀抹、BeanFactoryAware虐拓、InitializingBean和DiposableBean這些接口的方法。

Spring容器級(jí)別生命周期方法

包括InstantiationAwareBeanPostProcessor傲武、BeanPostProcessor蓉驹、BeanFactoryPostProcessor的實(shí)現(xiàn)類的方法。

特別說(shuō)明谱轨,本文僅結(jié)合實(shí)際的例子來(lái)說(shuō)明Spring 自定義標(biāo)簽的使用方法戒幔,而在此過(guò)程中涉及到的額外的技術(shù)點(diǎn)(比如xsd文檔的編寫規(guī)則)將不再本文的描述范圍之內(nèi),需要自行查找資料來(lái)學(xué)習(xí)土童,本文的定位是學(xué)會(huì)使用Spring自定義標(biāo)簽做一些事情,所以需要自行去查閱相關(guān)技術(shù)資料來(lái)學(xué)習(xí)一些內(nèi)容來(lái)理解Spring自定義標(biāo)簽工坊。

自定義標(biāo)簽以實(shí)現(xiàn)bean注冊(cè)

首先献汗,如何自定義一個(gè)Spring標(biāo)簽來(lái)實(shí)現(xiàn)bean的注冊(cè)呢?我想要實(shí)現(xiàn)的功能是類似于<bean .../>這樣的王污,下面將一步一步來(lái)說(shuō)明如何進(jìn)行操作罢吃,達(dá)到最后的效果。

編寫xsd文件

第一步是需要編寫xsd文件昭齐,下面是一個(gè)例子:


<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://code.hujian.com/schema/ok"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:beans="http://www.springframework.org/schema/beans"
            targetNamespace="http://code.hujian.com/schema/ok"
            elementFormDefault="qualified" attributeFormDefault="unqualified">

    <xsd:complexType name="server">
        <xsd:attribute name="id" type="xsd:string">
            <xsd:annotation>
                <xsd:documentation><![CDATA[ The unique identifier for a bean. ]]></xsd:documentation>
            </xsd:annotation>
        </xsd:attribute>
        <xsd:attribute name="serverName" type="xsd:string">
            <xsd:annotation>
                <xsd:documentation><![CDATA[ The name of the bean. ]]></xsd:documentation>
            </xsd:annotation>
        </xsd:attribute>
    </xsd:complexType>
    
        <xsd:element name="service" type="server">
        <xsd:annotation>
            <xsd:documentation><![CDATA[ The service config ]]></xsd:documentation>
        </xsd:annotation>
    </xsd:element>

</xsd:schema>

將這個(gè)文件命名為任意你喜歡的名字尿招,后綴為.xsd,比如例子中的該文件被命名為ok-1.0.xsd阱驾,這個(gè)名字將在后文中用到就谜。上面定義的xsd文件中,我想要實(shí)現(xiàn)類似于:


<service id = "" serverName= "" />

看起來(lái)很簡(jiǎn)單里覆,并且我希望可以通過(guò)加載配置文件后可以獲取到這個(gè)bean(根據(jù)id來(lái)獲壬ゼ觥)。但是看起來(lái)很奇怪的是這個(gè)bean的類似是什么呢喧枷?你當(dāng)然可以在xsd文件中增加一個(gè)attr叫做“class”來(lái)控制生成的bean的類型虹统,但是本文中的例子為了簡(jiǎn)單,只可以配置一個(gè)屬性隧甚,具體返回的類似后面會(huì)說(shuō)到车荔。

編寫Schema文件和handler文件

這一步是比較關(guān)鍵的一步,你需要編寫兩個(gè)文件戚扳,分別為spring.schemas和spring.handlers忧便,然后將這兩個(gè)文件放在resource文件夾下的META-INF文件夾下,在spring.schemas文件里面咖城,你需要寫上茬腿;類似下面的內(nèi)容:


http\://code.hujian.com/schema/ok/ok-1.0.xsd=./ok-1.0.xsd

前面的http://code.hujian.com/schema/ok/ok-1.0.xsd是我們的命名空間呼奢,后面是我們上面編寫的xsd文件,這里需要注意文件名切平。寫好spring.schemas文件后握础,需要寫spring.handlers文件,在這個(gè)文件里面你需要定義一個(gè)處理器來(lái)處理你自定義的哪些標(biāo)簽悴品,我們可以在里面做很豐富的事情禀综,下面是為本文例子編寫的spring.handlers文件的內(nèi)容:


http\://code.hujian.com/schema/ok=com.hujian.spring.handler.CommonNamespaceHandler


編寫Handler

經(jīng)過(guò)上面兩步之后,現(xiàn)在我們可以開(kāi)始寫處理我們的自定義標(biāo)簽的Handler了苔严,下面首先展示了代碼:


class CommonNamespaceHandler extends NamespaceHandlerSupport{
    @Override
    public void init() {
        this.registerBeanDefinitionParser("service",
                new OkServerDefinitionParser(ServerBean.class));
    }
}

class OkServerDefinitionParser implements BeanDefinitionParser {

    private final Class<?> clazz;
    private static final String default_prefix = "ok-";
    private static final AtomicLong COUNT = new AtomicLong(0);

    public OkServerDefinitionParser(Class<?> clazz) {
        this.clazz = clazz;
    }

    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        return parseHelper(element, parserContext, this.clazz);
    }

    private BeanDefinition parseHelper(Element element, ParserContext parserContext, Class<?> clazz) {
        RootBeanDefinition bd = new RootBeanDefinition();

        bd.setLazyInit(false);
        String id = element.getAttribute("id");
        if (id == null || id.isEmpty()) {
            id = default_prefix + COUNT.getAndDecrement();
        }

        String serverName = element.getAttribute("serverName");

        bd.setBeanClass(clazz);
        bd.setInitMethodName("init");

        MutablePropertyValues propertyValues = bd.getPropertyValues();
        propertyValues.addPropertyValue("serverName", serverName);

        parserContext.getRegistry().registerBeanDefinition(id, bd);

        return bd;
    }
}

上面說(shuō)到我們自定義的標(biāo)簽還不知道返回的bean是什么類型的定枷,為了簡(jiǎn)單,上面的代碼中將返回的類型定義為了ServerBean這個(gè)類型届氢,下面是這個(gè)類的信息:


class ServerBean {
    private String serverName;

    //init method
    public void init() {
        System.out.println("bean ServerBean init.");
    }

    @Override
    public String toString() {
        return "[Service]=>" + serverName;
    }

    public String getServerName() {
        return serverName;
    }

    public void setServerName(String serverName) {
        this.serverName = serverName;
    }
}


其實(shí)流程還是比較容易看懂的欠窒,首先我們需要注冊(cè)一個(gè)bean,而Spring中注冊(cè)的bean是AbstractBeanDefinition的子類退子,所以你可以使用任意AbstractBeanDefinition的子類來(lái)注冊(cè)你的bean岖妄,上面的例子中使用了RootBeanDefinition這個(gè)AbstractBeanDefinition的子類來(lái)注冊(cè)一個(gè)bean,設(shè)置一些配置信息之后就使用ParserContext的注冊(cè)器來(lái)將我們自定義的bean注冊(cè)到Spring中去了寂祥,需要注意的是荐虐,我們?cè)?lt;service id = "" .../>中配置的id就是我們往Spring容器中注冊(cè)的bean的id,所以在我們想要使用該bean的時(shí)候就可以使用這個(gè)id來(lái)獲取這個(gè)bean了丸凭。

測(cè)試

經(jīng)過(guò)上面的步驟之后福扬,下面來(lái)測(cè)試一下我自定義的標(biāo)簽是否可以正常工作,首先需要編寫Spring 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:ok="http://code.hujian.com/schema/ok"

       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://code.hujian.com/schema/ok
           http://code.hujian.com/schema/ok/ok-1.0.xsd">


    <ok:service id="testServer" serverName="HelloWorldService"/>

</beans>

需要注意的是需要引入我們自定義的命名空間: xmlns:ok="http://code.hujian.com/schema/ok"惜犀,并且需要將我們的Schema位置也告訴Spring铛碑,也就是需要在xsi:schemaLocation中設(shè)置我們的Schema路徑。然后就可以使用我們的自定義標(biāo)簽<ok:service .../>了向拆,可以看出上面我們配置了一個(gè)自定義bean亚茬,id為testServer,serverName屬性為HelloWorldService浓恳,下面是測(cè)試代碼:


    public static void main(String ... args) {

        String xmlFile = "tagTest.xml";
        String beanId = "testServer";

        ApplicationContext context = new ClassPathXmlApplicationContext(xmlFile);

        ServerBean bean = (ServerBean) context.getBean(beanId);

        System.out.println(bean);

    }

下面是輸出的結(jié)果:


[Service]=>HelloWorldService

可以看到刹缝,我們自定義的標(biāo)簽可以正常工作了,更為復(fù)雜的Spring自定義標(biāo)簽可以借助這個(gè)例子來(lái)擴(kuò)展颈将。

自定義標(biāo)簽以實(shí)現(xiàn)bean掃描

上面展示了一個(gè)簡(jiǎn)單的Spring自定義標(biāo)簽的用法梢夯,當(dāng)然任意復(fù)雜的自定義標(biāo)簽都可以基于這個(gè)簡(jiǎn)單的標(biāo)簽來(lái)模仿出來(lái),下面一個(gè)例子和注解有關(guān)晴圾,有時(shí)候我們希望借助Spring來(lái)幫我們解析代碼中的注解颂砸,下面的例子可以在xml中使用自定義的標(biāo)簽設(shè)定需要掃描的package,Spring會(huì)掃描我們配置的這個(gè)package,然后我希望可以找到這個(gè)package下所有注解了OkService的類人乓,并且基于該注解做一些統(tǒng)計(jì)勤篮,比如將這些注解的信息收集起來(lái),然后最后展示出這些收集到的注解信息色罚,因?yàn)椴襟E和上面的例子一樣碰缔,所以不再贅述:

首先是xsd文件:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://code.hujian.com/schema/ok"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:beans="http://www.springframework.org/schema/beans"
            targetNamespace="http://code.hujian.com/schema/ok"
            elementFormDefault="qualified" attributeFormDefault="unqualified">

    <xsd:complexType name="annotationType">
        <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="scan" type="xsd:string" use="optional">
            <xsd:annotation>
                <xsd:documentation><![CDATA[ The scan package. ]]></xsd:documentation>
            </xsd:annotation>
        </xsd:attribute>
        <xsd:attribute name="url" type="xsd:string" use="optional">
            <xsd:annotation>
                <xsd:documentation><![CDATA[ The url string ]]></xsd:documentation>
            </xsd:annotation>
        </xsd:attribute>
    </xsd:complexType>

    <xsd:element name="annotation" type="annotationType">
        <xsd:annotation>
            <xsd:documentation><![CDATA[ The annotation config ]]></xsd:documentation>
        </xsd:annotation>
    </xsd:element>
</xsd:schema>

接下來(lái)編寫handler:


class CommonNamespaceHandler extends NamespaceHandlerSupport{
    @Override
    public void init() {
        this.registerBeanDefinitionParser("annotation",
                new OkAnnotationDefinitionParser(ScanBeanReference.class));
    }
}


class OkAnnotationDefinitionParser implements BeanDefinitionParser {
    private final Class<?> clazz;
    private static final String default_prefix = "scan-";
    private static final AtomicLong COUNT = new AtomicLong(0);

    public OkAnnotationDefinitionParser(Class<?> clazz) {
        this.clazz = clazz;
    }

    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        return parseHelper(element, parserContext, clazz);
    }

    private BeanDefinition parseHelper(Element element, ParserContext parserContext, Class<?> clazz) {
        RootBeanDefinition bd = new RootBeanDefinition();

        bd.setLazyInit(false);
        String id = element.getAttribute("id");
        if (id == null || id.isEmpty()) {
            id = default_prefix + COUNT.getAndDecrement();
        }

        String scanPackage = element.getAttribute("scan");
        String url = element.getAttribute("url");

        bd.setBeanClass(ScanBeanParser.class);
        bd.setInitMethodName("init");

        MutablePropertyValues propertyValues = bd.getPropertyValues();
        propertyValues.addPropertyValue("scan", scanPackage);
        propertyValues.addPropertyValue("url", url);

        parserContext.getRegistry().registerBeanDefinition(id, bd);

        return bd;
    }
}


上面的代碼和上面的例子中的代碼沒(méi)有什么區(qū)別,但是有一個(gè)地方需要特別注意:


bd.setBeanClass(ScanBeanParser.class);

而這個(gè)ScanBeanParser類的信息如下:


class ScanBeanParser implements BeanPostProcessor,
        BeanFactoryPostProcessor, ApplicationContextAware, PriorityOrdered {

    private static final Pattern COMMA_SPLIT_PATTERN = Pattern.compile("\\s*[,]+\\s*");
    private String scan; // the scan package
    private String url; // the url

    public void setScan(String scan) {
        this.scan = scan;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public void init() {
        System.out.println("ScanBeanParser start to run...");
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
            throws BeansException {
        String annotationPackage = scan == null || scan.isEmpty() ? "com.hujian" : scan;

        System.out.println("get the scan package:" + annotationPackage);

        if (beanFactory instanceof BeanDefinitionRegistry) {
            try {
                // init scanner
                Class<?> scannerClass = ClassUtils
                        .loadClass("org.springframework.context.annotation.ClassPathBeanDefinitionScanner");
                Object scanner = scannerClass.getConstructor(
                        new Class<?>[] { BeanDefinitionRegistry.class, boolean.class }).newInstance(
                                beanFactory, true);
                // add filter
                Class<?> filterClass = ClassUtils
                        .loadClass("org.springframework.core.type.filter.AnnotationTypeFilter");
                Object filter = filterClass.getConstructor(Class.class).newInstance(OkService.class);
                Method addIncludeFilter = scannerClass.getMethod("addIncludeFilter",
                        ClassUtils.loadClass("org.springframework.core.type.filter.TypeFilter"));
                addIncludeFilter.invoke(scanner, filter);
                // scan packages
                String[] packages = COMMA_SPLIT_PATTERN.split(annotationPackage);
                Method scan = scannerClass.getMethod("scan", String[].class);
                scan.invoke(scanner, new Object[] { packages });
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
        return o;
    }

    @Override
    public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
        Class<?> beanClass = AopUtils.getTargetClass(o);
        if (beanClass == null) {
            return o;
        }

        OkService service = beanClass.getAnnotation(OkService.class);
        if (service != null) {
            ScanBeanReference scanBeanReference = new ScanBeanReference();
            scanBeanReference.setScan(service.scan());
            scanBeanReference.setUrl(service.url());
            scanBeanReference.setMsg(service.msg());

            System.out.println("get a scan bean:" + scanBeanReference);

            ScanStorageFactory.addScanBean(scanBeanReference);
        }

        return o;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("get the ApplicationContext:" + applicationContext);
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

在OkAnnotationDefinitionParser中獲取了xml中的配置(比如scan屬性)戳护,然后將獲取到的屬性值傳遞給ScanBeanParser這個(gè)類金抡,這個(gè)類里面做了我們想要做的事情,就是收集所有注解了OKService的類的信息腌且,并存儲(chǔ)起來(lái)梗肝。讀到這里就需要回頭看一下文章開(kāi)頭的那張Spring Bean的生命周期圖,ScanBeanParser實(shí)現(xiàn)了很多涉及Spring Bean生命周期的類铺董。下面是測(cè)試代碼:


    public static void main(String ... args) {

        String xmlFile = "tagTest.xml";
        String beanId = "testServer";

        ApplicationContext context = new ClassPathXmlApplicationContext(xmlFile);

        ScanStorageFactory.getScanBeanReferenceList()
                .forEach(System.out::println);
    }

@OkService(scan = "com.hujian.io", url = "http://www.meituan.com", msg = "ScanTestClass1")
class ScanTestClass1 {

}

@OkService(scan = "com.hujian.rpc", url = "http://www.dianping.com", msg = "ScanTestClass2")
class ScanTestClass2 {

}

@OkService(scan = "io.hujian.com", url = "http://www.ok.com", msg = "ScanTestClass3")
class ScanTestClass3 {

}

測(cè)試的結(jié)果如下:


scanPackage:com.hujian.io, url:http://www.meituan.com, msg:ScanTestClass1
scanPackage:com.hujian.rpc, url:http://www.dianping.com, msg:ScanTestClass2
scanPackage:io.hujian.com, url:http://www.ok.com, msg:ScanTestClass3

結(jié)語(yǔ)

本文較為粗淺的解析了Spring中自定義標(biāo)簽的使用方法巫击,可以將本文中的代碼作為模板來(lái)進(jìn)行Spring自定義標(biāo)簽的設(shè)計(jì)和處理,更為深入的分析與總結(jié)將在未來(lái)進(jìn)行柄粹,關(guān)于Spring的相關(guān)分析總結(jié)會(huì)持續(xù)更新喘鸟,本文相當(dāng)于一個(gè)Spring自定義標(biāo)簽的“最佳實(shí)踐”吧!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末驻右,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子崎淳,更是在濱河造成了極大的恐慌堪夭,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,406評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拣凹,死亡現(xiàn)場(chǎng)離奇詭異森爽,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)嚣镜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,395評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門爬迟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人菊匿,你說(shuō)我怎么就攤上這事付呕。” “怎么了跌捆?”我有些...
    開(kāi)封第一講書人閱讀 167,815評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵徽职,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我佩厚,道長(zhǎng)姆钉,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 59,537評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮潮瓶,結(jié)果婚禮上陶冷,老公的妹妹穿的比我還像新娘。我一直安慰自己毯辅,他們只是感情好埂伦,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,536評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著悉罕,像睡著了一般赤屋。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上壁袄,一...
    開(kāi)封第一講書人閱讀 52,184評(píng)論 1 308
  • 那天类早,我揣著相機(jī)與錄音,去河邊找鬼嗜逻。 笑死涩僻,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的栈顷。 我是一名探鬼主播逆日,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼萄凤!你這毒婦竟也來(lái)了室抽?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,668評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤靡努,失蹤者是張志新(化名)和其女友劉穎坪圾,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體惑朦,經(jīng)...
    沈念sama閱讀 46,212評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡兽泄,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,299評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了漾月。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片病梢。...
    茶點(diǎn)故事閱讀 40,438評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖梁肿,靈堂內(nèi)的尸體忽然破棺而出蜓陌,到底是詐尸還是另有隱情,我是刑警寧澤栈雳,帶...
    沈念sama閱讀 36,128評(píng)論 5 349
  • 正文 年R本政府宣布护奈,位于F島的核電站,受9級(jí)特大地震影響哥纫,放射性物質(zhì)發(fā)生泄漏霉旗。R本人自食惡果不足惜痴奏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,807評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望厌秒。 院中可真熱鬧读拆,春花似錦、人聲如沸鸵闪。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,279評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蚌讼。三九已至辟灰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間篡石,已是汗流浹背芥喇。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,395評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留凰萨,地道東北人继控。 一個(gè)月前我還...
    沈念sama閱讀 48,827評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像胖眷,于是被迫代替她去往敵國(guó)和親武通。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,446評(píng)論 2 359

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

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理珊搀,服務(wù)發(fā)現(xiàn)冶忱,斷路器,智...
    卡卡羅2017閱讀 134,695評(píng)論 18 139
  • 1.1 spring IoC容器和beans的簡(jiǎn)介 Spring 框架的最核心基礎(chǔ)的功能是IoC(控制反轉(zhuǎn))容器境析,...
    simoscode閱讀 6,721評(píng)論 2 22
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,844評(píng)論 6 342
  • Spring Web MVC Spring Web MVC 是包含在 Spring 框架中的 Web 框架朗和,建立于...
    Hsinwong閱讀 22,434評(píng)論 1 92
  • Eclipse:建一個(gè)目錄,比如external-jars簿晓,然后把dl-lib.jar和support-v4.ja...
    花老鼠閱讀 2,735評(píng)論 0 2