SpringBoot入門篇2 -- 簡(jiǎn)單工程hello-world深度解析

智慧源于勤奮偿曙,偉大出自平凡

謹(jǐn)以上句獻(xiàn)給每一個(gè)勤奮的平凡的人氮凝。
什么是平凡,在軟件開發(fā)中望忆,從不缺乏平凡之人罩阵,但是你真的甘心做一個(gè)平凡的人嗎?在目前平凡的生活中启摄,如何拒絕平庸稿壁,如何擁有智慧呢?那就需要不斷的學(xué)習(xí)和善于總結(jié)了~今天歉备,我們來從平凡的事物(hello-world程序)入手傅是,來看看在平凡中我們?nèi)绾螌W(xué)的透徹~

啟動(dòng)類代碼深度解析

上一篇文章http://www.reibang.com/p/c261ba6a4bd4簡(jiǎn)單的工程hello-world搭建,我并沒有詳細(xì)的介紹程序中的代碼,那么今天讓我們來揭開SpringBoot簡(jiǎn)單的工程下神秘的面紗吧喧笔。首先帽驯,上我們啟動(dòng)類的代碼:

package top.flygrk.ishare.helloworld;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class HelloWorldApplication {

    public static void main(String[] args) {
        SpringApplication.run(HelloWorldApplication.class, args);
    }
}

在上面的代碼,我們可以看出這段程序代碼非常的簡(jiǎn)潔明了书闸,只有一個(gè)main方法尼变,并且在類上方有注解@SpringBootApplication修飾。我們首先看一下@SpringBootApplication注解的含義梗劫,我們查看下該注解的源碼如下:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.boot.autoconfigure;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    Class<?>[] exclude() default {};

    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    String[] excludeName() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackages"
    )
    String[] scanBasePackages() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackageClasses"
    )
    Class<?>[] scanBasePackageClasses() default {};
}

從@SpringBootApplication注解的源碼可以看出享甸,@SpringBootApplication是一個(gè)復(fù)合注解,包括@ComponentScan梳侨、@SpringBootConfiguration蛉威、@EnableAutoConfiguration等注解。那么我們先不關(guān)心這幾個(gè)注解的作用走哺,我們將SpringBoot的hello-world程序啟動(dòng)類HelloWorldApplcation的@SpringBootApplication注解替換成以上三個(gè)蚯嫌,程序能不能夠運(yùn)行呢?我們來嘗試一下:

package top.flygrk.ishare.helloworld;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

//@SpringBootApplication
@ComponentScan
@EnableAutoConfiguration
@SpringBootConfiguration
public class HelloWorldApplication {

    public static void main(String[] args) {
        SpringApplication.run(HelloWorldApplication.class, args);
    }
}

我們啟動(dòng)工程丙躏,發(fā)現(xiàn)啟動(dòng)過程中并沒有報(bào)錯(cuò)择示,打開瀏覽器,輸入http://localhost:8080/sayHello訪問晒旅,能夠成功打印出我們想要得到的結(jié)果栅盲,所以,用上面的三個(gè)注解替換@SpringBootApplication是可以的废恋。

@SringBootConfiguration

好了谈秫,我們來仔細(xì)看看這幾個(gè)注解及@SpringBootApplication的方法。首先鱼鼓,我們來看下@SringBootConfiguration注解(關(guān)于前面四個(gè)java注解這里不再細(xì)述拟烫,可查閱其他資料)

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.boot;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Configuration;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}

從@SpringBootConfiguration注解的源碼可以看出其繼承自@Configuration,并且沒有自己獨(dú)有的方法迄本,即我們認(rèn)為它們之間是is-a關(guān)系硕淑,也就是替代原則,表示的功能也是一致的嘉赎,標(biāo)識(shí)當(dāng)前類是配置類置媳,將會(huì)將當(dāng)前類內(nèi)聲明的一個(gè)或者多個(gè)以@Bean注解標(biāo)識(shí)的方法的實(shí)例注入到Spring容器中,且實(shí)例名為方法名曹阔。我們繼續(xù)延伸一下半开,看看@Configuration注解的源碼:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.context.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Component;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}

這里我們可以看到其繼承于Spring的@Component注解,哈哈赃份,@Component注解的作用不就是定義Spring管理Bean的嗎~~~(@Component注解的源碼大家可以自行去閱讀啦~后續(xù)也會(huì)進(jìn)行分享)

@EnableAutoConfiguration

接下來,我們來看下@EnableAutoConfiguration的作用及其實(shí)現(xiàn)。從其字面意思上來看抓韩,其作用是實(shí)現(xiàn)能夠自動(dòng)加載配置纠永,確實(shí),@EnableAutoConfiguration的作用是從classpath中搜索所有的META-INF/spring.factoriies配置文件谒拴,然后將其中的org.springframework.boot.autoconfigure.EnableAutoConfiguration的 key對(duì)應(yīng)的配置項(xiàng)加載到Spring容器尝江。并且只有當(dāng)spring.boot.enableautoconfiguration為true時(shí)(默認(rèn)為true)才啟動(dòng)該自動(dòng)配置。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.boot.autoconfigure;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

@Import注解:
@Import注解通過導(dǎo)入的方式實(shí)現(xiàn)把實(shí)例加入到SpringIOC容器中英上,其只注解在類上炭序,并且其唯一的參數(shù)value上可以配置3種類型的值:Configuration、ImportSelector苍日、ImportBeanDefinitionRegistrar

我們可以看到@Import將AutoConfigurationImportSelector實(shí)例加入到SpringIOC容器中惭聂,我們來看下該類的具體實(shí)現(xiàn)的部分代碼:


    ……
    private static final AutoConfigurationImportSelector.AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationImportSelector.AutoConfigurationEntry();
    private static final String[] NO_IMPORTS = new String[0];
    private static final Log logger = LogFactory.getLog(AutoConfigurationImportSelector.class);
    private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";
    private ConfigurableListableBeanFactory beanFactory;
    private Environment environment;
    private ClassLoader beanClassLoader;
    private ResourceLoader resourceLoader;

    ……
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
            AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }
    protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            configurations = this.removeDuplicates(configurations);
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.filter(configurations, autoConfigurationMetadata);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
        }
    }

其中List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);表示去獲取類路徑下spring.factories下key為EnableAutoConfiguration全限定名對(duì)應(yīng)值。

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
        return configurations;
    }

    protected Class<?> getSpringFactoriesLoaderFactoryClass() {
        return EnableAutoConfiguration.class;
    }

從上面代碼getCandidateConfigurations會(huì)到classpath下讀取META-INF/spring.factories文件的配置相恃,返回一個(gè)字符串?dāng)?shù)組辜纲。spring.factories的內(nèi)容為:(位于spring-boot-autoconfigure-2.1.0.RELEASE.jar包下)

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\

…………(省略)

org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration

# Failure analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer,\
org.springframework.boot.autoconfigure.session.NonUniqueSessionRepositoryFailureAnalyzer

# Template availability providers
org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.web.servlet.JspTemplateAvailabilityProvider

此外,需要注意的還有@EnableAutoConfiguration可以進(jìn)行排除拦耐,其有2種排除方式:

  • 根據(jù)class來排除(exclude)
  • 根據(jù)class name 來排除(excludeName)來排除
    讀者可以閱讀其源碼深度了解其含義
@ComponentScan

接下來耕腾,我們來看看第三個(gè)注解@ComponentScan注解的含義。@ComponentScan主要是從定義的掃描路徑中找出標(biāo)識(shí)了需要裝配的類杀糯,將其自動(dòng)裝配到Spring的Bean容器中扫俺。@ComponentScan注解默認(rèn)會(huì)裝配標(biāo)識(shí)了@Controller、@Service固翰、@Repository狼纬、@Component等注解類到Spring容器中。我們看下其源碼:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.context.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.core.annotation.AliasFor;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
    @AliasFor("basePackages")
    String[] value() default {};

    @AliasFor("value")
    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};

    Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

    Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;

    ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;

    String resourcePattern() default "**/ *.class";

    boolean useDefaultFilters() default true;
    
    ComponentScan.Filter[] includeFilters() default {};
    
    //指定不適合組件掃描的類型
    ComponentScan.Filter[] excludeFilters() default {};
    
    //是否啟用懶加載倦挂,默認(rèn)不啟動(dòng)
    boolean lazyInit() default false;

    @Retention(RetentionPolicy.RUNTIME)
    @Target({})
    public @interface Filter {
        FilterType type() default FilterType.ANNOTATION;

        @AliasFor("classes")
        Class<?>[] value() default {};

        @AliasFor("value")
        Class<?>[] classes() default {};

        String[] pattern() default {};
    }
}

其可以通過includeFilters加入掃描路徑下沒有以上注解的類到Spring容器畸颅;并且可以通過excludeFilters過濾出不用加入Spring容器的類。其默認(rèn)掃描地址為其所在的包及其所有的子包方援。

好啦没炒,我們將@SpringBootApplication的注解簡(jiǎn)單的看了下,那么接下來犯戏,我們看下該main方法的作用吧~看下SpringApplication的代碼:

    public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
        return run(new Class[]{primarySource}, args);
    }

    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        return (new SpringApplication(primarySources)).run(args);
    }

    …………

    public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        this.configureHeadlessProperty();
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();

        Collection exceptionReporters;
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            Banner printedBanner = this.printBanner(environment);
            context = this.createApplicationContext();
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

            listeners.started(context);
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, exceptionReporters, listeners);
            throw new IllegalStateException(var10);
        }

        try {
            listeners.running(context);
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }

@SpringBootApplication方法釋義

我們可以看到@SpringBootApplication的源碼中有以下方法:

    //根據(jù)class 來排除特定的類加入Spring 容器送火,傳入的value類型是class類型
    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    Class<?>[] exclude() default {};

    //根據(jù)class name來排除特定的類加入Spring容器,傳入?yún)?shù)value類型是class的全類名字符串?dāng)?shù)組
    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    String[] excludeName() default {};
  
    //指定掃描包先匪,參數(shù)名是包名的字符串?dāng)?shù)組
    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackages"
    )
    String[] scanBasePackages() default {};

    //掃描特定的包种吸,參數(shù)是Class類型的數(shù)組
    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackageClasses"
    )
    Class<?>[] scanBasePackageClasses() default {};

我們可以在@SpringBootApplication后配置其指定掃描的包等信息,例如:

package top.flygrk.ishare.helloworld;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

//設(shè)置掃描的包路徑為top.flygrk及其所有的子包
@SpringBootApplication(scanBasePackages = "top.flygrk")
//@ComponentScan
//@EnableAutoConfiguration
//@SpringBootConfiguration
public class HelloWorldApplication {

    public static void main(String[] args) {
        SpringApplication.run(HelloWorldApplication.class, args);
    }
}

控制器controller層代碼深度解析

了解了啟動(dòng)類的相關(guān)原理呀非,那么我們來看下控制器層做了什么坚俗?看一下@RestController的作用是什么镜盯?它和@Controller究竟有什么區(qū)別?
我們先看下HelloController的代碼:

package top.flygrk.ishare.helloworld.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author: flygrk
 * @Date: Created in 2018/11/12 22:26
 * @Description: HelloWorld 控制器類
 */
@RestController
public class HelloController {

    @RequestMapping("sayHello")
    public String sayHello() {
        return "Hello World!";
    }
}

在類上方的@RestController究竟有什么作用呢猖败?先來看下@RestController的源代碼:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.web.bind.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Controller;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
    @AliasFor(
        annotation = Controller.class
    )
    String value() default "";
}

呀哈速缆,我們看到了修飾它的注解竟然是@Controller和@ResponseBody,這個(gè)時(shí)候我們?cè)贎SpringBootApplication的源碼中看到的@ComponentScan就可以掃描到該類恩闻,并將其注入Spring容器中了艺糜。另外@RestController具有它們兩個(gè)注解語義,所以在注解處理時(shí)@RestController要比@Controller多具有一個(gè)注解@ResponseBody語義(@ResponseBody返回值是json格式的)幢尚,這也就造成了@RestController的返回值為什么都是經(jīng)過轉(zhuǎn)換的json的原因啦~
但是破停,注意了,當(dāng)我們?cè)赾ontroller返回一個(gè)html(或者其他模板語言)尉剩,我們這個(gè)時(shí)候一般都是采用@Controller注解真慢;如果同一個(gè)Controller中,即包含接口開發(fā)边涕,又包含返回頁面的配置晤碘,那么這個(gè)時(shí)候我們可以在接口開發(fā)(此處指json報(bào)文)的方法上使用@ResponseBody注解即可(該注解值得細(xì)細(xì)學(xué)習(xí),后期分享~)

平凡~但不可平庸~歡迎大佬指點(diǎn)指點(diǎn)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末功蜓,一起剝皮案震驚了整個(gè)濱河市园爷,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌式撼,老刑警劉巖童社,帶你破解...
    沈念sama閱讀 211,817評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異著隆,居然都是意外死亡扰楼,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門美浦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來弦赖,“玉大人,你說我怎么就攤上這事浦辨〉攀” “怎么了?”我有些...
    開封第一講書人閱讀 157,354評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵流酬,是天一觀的道長(zhǎng)币厕。 經(jīng)常有香客問我,道長(zhǎng)芽腾,這世上最難降的妖魔是什么旦装? 我笑而不...
    開封第一講書人閱讀 56,498評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮摊滔,結(jié)果婚禮上阴绢,老公的妹妹穿的比我還像新娘店乐。我一直安慰自己,他們只是感情好旱函,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評(píng)論 6 386
  • 文/花漫 我一把揭開白布响巢。 她就那樣靜靜地躺著描滔,像睡著了一般棒妨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上含长,一...
    開封第一講書人閱讀 49,829評(píng)論 1 290
  • 那天券腔,我揣著相機(jī)與錄音,去河邊找鬼拘泞。 笑死纷纫,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的陪腌。 我是一名探鬼主播辱魁,決...
    沈念sama閱讀 38,979評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼诗鸭!你這毒婦竟也來了染簇?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,722評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤强岸,失蹤者是張志新(化名)和其女友劉穎锻弓,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蝌箍,經(jīng)...
    沈念sama閱讀 44,189評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡青灼,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了妓盲。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片杂拨。...
    茶點(diǎn)故事閱讀 38,654評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖悯衬,靈堂內(nèi)的尸體忽然破棺而出弹沽,到底是詐尸還是另有隱情,我是刑警寧澤甚亭,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布贷币,位于F島的核電站,受9級(jí)特大地震影響亏狰,放射性物質(zhì)發(fā)生泄漏役纹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評(píng)論 3 313
  • 文/蒙蒙 一暇唾、第九天 我趴在偏房一處隱蔽的房頂上張望促脉。 院中可真熱鬧辰斋,春花似錦、人聲如沸瘸味。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽旁仿。三九已至藕夫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間枯冈,已是汗流浹背毅贮。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評(píng)論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留尘奏,地道東北人滩褥。 一個(gè)月前我還...
    沈念sama閱讀 46,382評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像炫加,于是被迫代替她去往敵國和親瑰煎。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評(píng)論 2 349

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

  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,773評(píng)論 6 342
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理俗孝,服務(wù)發(fā)現(xiàn)酒甸,斷路器,智...
    卡卡羅2017閱讀 134,633評(píng)論 18 139
  • 入門 介紹 Spring Boot Spring Boot 使您可以輕松地創(chuàng)建獨(dú)立的驹针、生產(chǎn)級(jí)的基于 Spring ...
    Hsinwong閱讀 16,864評(píng)論 2 89
  • 工作中要用心觀察烘挫,記住領(lǐng)導(dǎo)說過的話,改過的文章柬甥,部置過的工作任務(wù)饮六。 我發(fā)現(xiàn)自己在工作中,還是沒有能真正做到細(xì)心觀察...
    向著太陽奔跑的石頭閱讀 255評(píng)論 0 0
  • 三個(gè)朋友聚會(huì)苛蒲,一個(gè)學(xué)士卤橄、一個(gè)碩士、一個(gè)博士臂外,席間窟扑,三人舉杯祝愿,學(xué)士說:祝我們新的一年混得越來越好漏健,碩士說嚎货,我們不...
    長(zhǎng)安孤客閱讀 243評(píng)論 0 0