springboot原理—一步步分析springboot啟動機制(starter機制)

前言

使用過springboot的同學應該已經知道拱镐,springboot通過默認配置了很多框架的使用方式幫我們大大簡化了項目初始搭建以及開發(fā)過程。本文的目的就是一步步分析springboot的啟動過程湿酸,分析springboot是如何幫我們簡化這個過程的。

如果想學習Java工程化递胧、高性能及分布式囚痴、深入淺出。微服務族展、Spring森缠,MyBatis,Netty源碼分析的朋友可以加我的Java高級交流:854630135仪缸,群里有阿里大牛直播講解技術贵涵,以及Java大型互聯(lián)網技術的視頻免費分享給大家。

springboot幫我們做了什么

通常搭建一個基于spring的web應用恰画,我們需要做以下工作:

1宾茂、pom文件中引入相關jar包,包括spring拴还、springmvc跨晴、redis、mybaits片林、log4j端盆、mysql-connector-java 等等相關jar ...

2怀骤、配置web.xml,Listener配置焕妙、Filter配置蒋伦、Servlet配置、log4j配置焚鹊、error配置 ...

3痕届、配置數據庫連接、配置spring事務

4末患、配置視圖解析器

5研叫、開啟注解、自動掃描功能

6阻塑、配置完成后部署tomcat蓝撇、啟動調試

......

搭個初始項目不一會就一個小時甚至半天過去了。而用springboot后陈莽,一切都變得很簡便快速渤昌。下來我們來一步步分析springboot的起步依賴與自動配置這兩個核心原理。

回到頂部

起步依賴

在springboot中我們只需要引入下面簡單的幾步就可以完成一個ssm后臺項目的初始搭建走搁。

1独柑、引入jar

org.springframework.boot

spring-boot-starter-parent

2.0.4.RELEASE


org.mybatis.spring.boot

mybatis-spring-boot-starter

1.3.2


org.springframework.boot

spring-boot-starter-web

mysql

mysql-connector-java

runtime


com.alibaba

druid

1.0.31

如果想學習Java工程化、高性能及分布式私植、深入淺出忌栅。微服務、Spring曲稼,MyBatis索绪,Netty源碼分析的朋友可以加我的Java高級交流:854630135,群里有阿里大牛直播講解技術贫悄,以及Java大型互聯(lián)網技術的視頻免費分享給大家瑞驱。

spring-boot-starter-web包自動幫我們引入了web模塊開發(fā)需要的相關jar包,

mybatis-spring-boot-starter幫我們引入了dao開發(fā)相關的jar包窄坦。

spring-boot-starter-xxx是官方提供的starter唤反,xxx-spring-boot-starter是第三方提供的starter。

如下截圖:

可以看出在這個mybatis-spring-boot-starter 中鸭津,并沒有任何源碼彤侍,只有一個pom文件,它的作用就是幫我們引入了相關jar包逆趋。

2盏阶、配置數據源

spring:

datasource:

url: jdbc:mysql://127.0.0.1:3306/mybatis_test

username: root

password: root

driver-class-name: com.mysql.jdbc.Driver

type: com.alibaba.druid.pool.DruidDataSource

dbcp2:

min-idle: 5

initial-size: 5

max-total: 5

max-wait-millis: 200

stater機制幫我們完成了項目起步所需要的的相關jar包。那問題又來了闻书,傳統(tǒng)的spring應用中不是要在application.xml中配置很多bean的嗎般哼,比如dataSource的配置吴汪,transactionManager的配置 ... springboot是如何幫我們完成這些bean的配置的?下面我們來分析這個過程

回到頂部

自動配置

基于java代碼的bean配置

以mybatis為例蒸眠,在上面的截圖中,我們發(fā)下mybatis-spring-boot-starter這個包幫我們引入了mybatis-spring-boot-autoconfigure這個包杆融,如下圖:

里面有MybatisAutoConfiguration這個類楞卡,打開這個類看看有什么東西。

熟悉@Configuration&脾歇、@Bean這兩個bean的同學或許已經知道了蒋腮。這兩個注解一起使用就可以創(chuàng)建一個基于java代碼的配置類,可以用來替代相應的xml配置文件藕各。

@Configuration注解的類可以看作是能生產讓Spring IoC容器管理的Bean實例的工廠池摧。

@Bean注解告訴Spring,一個帶有@Bean的注解方法將返回一個對象激况,該對象應該被注冊到spring容器中作彤。

傳統(tǒng)的基于xml的bean配置方法如下:

相當于用基于java代碼的配置方式:

@Configuration

public class Conf {

@Bean

public Car car() {

Car car = new Car();

car.setWheel(wheel());

return car;

}

@Bean

public Wheel wheel() {

return new Wheel();

}

}

所以上面的MybatisAutoConfiguration這個類,自動幫我們生成了SqlSessionFactory這些Mybatis的重要實例并交給spring容器管理乌逐,從而完成bean的自動注冊竭讳。

自動配置條件依賴

從MybatisAutoConfiguration這個類中使用的注解可以看出,要完成自動配置是有依賴條件的浙踢。

@Configuration

@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})

@ConditionalOnBean({DataSource.class})

@EnableConfigurationProperties({MybatisProperties.class})

@AutoConfigureAfter({DataSourceAutoConfiguration.class})

public class MybatisAutoConfiguration {

//....

}

如果想學習Java工程化绢慢、高性能及分布式、深入淺出洛波。微服務胰舆、Spring,MyBatis蹬挤,Netty源碼分析的朋友可以加我的Java高級交流:854630135缚窿,群里有阿里大牛直播講解技術,以及Java大型互聯(lián)網技術的視頻免費分享給大家闻伶。

這些是springboot特有的滨攻,常見的條件依賴注解有:

@ConditionalOnBean,僅在當前上下文中存在某個bean時蓝翰,才會實例化這個Bean光绕。

@ConditionalOnClass,某個class位于類路徑上畜份,才會實例化這個Bean诞帐。

@ConditionalOnExpression,當表達式為true的時候爆雹,才會實例化這個Bean停蕉。

@ConditionalOnMissingBean愕鼓,僅在當前上下文中不存在某個bean時,才會實例化這個Bean慧起。

@ConditionalOnMissingClass菇晃,某個class在類路徑上不存在的時候,才會實例化這個Bean蚓挤。

@ConditionalOnNotWebApplication磺送,不是web應用時才會實例化這個Bean。

@AutoConfigureAfter灿意,在某個bean完成自動配置后實例化這個bean估灿。

@AutoConfigureBefore,在某個bean完成自動配置前實例化這個bean缤剧。

所以要完成Mybatis的自動配置馅袁,需要在類路徑中存在SqlSessionFactory.class、SqlSessionFactoryBean.class這兩個類荒辕,需要存在DataSource這個bean且這個bean完成自動注冊汗销。

進入DataSourceAutoConfiguration這個類,可以看到這個類屬于這個包:

org.springframework.boot.autoconfigure.jdbc

這個包又屬于spring-boot-autoconfigure-2.0.4.RELEASE.jar這個包兄纺,自動配置這個包幫們引入了jdbc大溜、kafka、logging估脆、mail钦奋、mongo等包。很多包需要我們引入相應jar后自動配置才生效疙赠。

bean參數獲取

到此我們已經知道了bean的配置過程付材,但是還沒有看到springboot是如何讀取yml或者properites配置文件的的屬性來創(chuàng)建數據源的?

在DataSourceAutoConfiguration類里面圃阳,我們注意到使用了EnableConfigurationProperties這個注解。

@Configuration

@ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class})

@EnableConfigurationProperties({DataSourceProperties.class})

@Import({DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class})

public class DataSourceAutoConfiguration {

...

}

DataSourceProperties中封裝了數據源的各個屬性捍岳,且使用了注解ConfigurationProperties指定了配置文件的前綴富寿。

@ConfigurationProperties(

prefix = "spring.datasource"

)

public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {

private ClassLoader classLoader;

private String name;

private boolean generateUniqueName;

private Class type;

private String driverClassName;

private String url;

private String username;

private String password;

private String jndiName;

...

}

@EnableConfigurationProperties與@ConfigurationProperties這兩個注解有什么用呢?我們先看一個例子:

@Component

@ConfigurationProperties(prefix="spring.datasource")

public class PropertiesBean {

private String url;

private String username;

private String password;

//省略getter锣夹、setter...

@Override

public String toString() {

return "PropertiesBean{" +

"url='" + url + ''' +

", username='" + username + ''' +

", password='" + password + ''' +

'}';

}

}

@SpringBootApplication

@MapperScan("com.itpsc.mapper*")

@EnableConfigurationProperties

public class SpringbootMybatisDemoApplication {

public static void main(String[] args) {

//SpringApplication.run(SpringbootMybatisDemoApplication.class, args);

ConfigurableApplicationContext context = SpringApplication.run(SpringbootMybatisDemoApplication.class, args);

//獲取yml配置轉換后的bean

System.out.println("----------------------"+context.getBean(PropertiesBean.class));

context.close();

}

}

如果想學習Java工程化页徐、高性能及分布式、深入淺出银萍。微服務变勇、Spring,MyBatis贴唇,Netty源碼分析的朋友可以加我的Java高級交流:854630135搀绣,群里有阿里大牛直播講解技術飞袋,以及Java大型互聯(lián)網技術的視頻免費分享給大家。

運行結果:

從運行結果可以看出@ConfigurationProperties與@EnableConfigurationPropertie的作用就是:

@ConfigurationProperties注解的作用是把yml或者properties配置文件轉化為bean链患。

@EnableConfigurationProperties注解的作用是使@ConfigurationProperties注解生效巧鸭。如果只配置@ConfigurationProperties注解,在spring容器中是獲取不到y(tǒng)ml或者properties配置文件轉化的bean的锣险。

通過這種方式蹄皱,把yml或者properties配置參數轉化為bean,這些bean又是如何被發(fā)現與加載的芯肤?

bean發(fā)現

springboot默認掃描啟動類所在的包下的主類與子類的所有組件,但并沒有包括依賴包的中的類压鉴,那么依賴包中的bean是如何被發(fā)現和加載的崖咨?

我們通常在啟動類中加@SpringBootApplication這個注解,點進去看

@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 {

...

}

實際上重要的只有三個Annotation:

@Configuration(@SpringBootConfiguration里面還是應用了@Configuration)

@EnableAutoConfiguration

@ComponentScan

@Configuration的作用上面我們已經知道了油吭,被注解的類將成為一個bean配置類击蹲。

@ComponentScan的作用就是自動掃描并加載符合條件的組件,比如@Component和@Repository等婉宰,最終將這些bean定義加載到spring容器中歌豺。

@EnableAutoConfiguration 這個注解的功能很重要,借助@Import的支持心包,收集和注冊依賴包中相關的bean定義类咧。

@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 {};

}

如上源碼,@EnableAutoConfiguration注解引入了@AutoConfigurationPackage和@Import這兩個注解蟹腾。@AutoConfigurationPackage的作用就是自動配置的包痕惋,@Import導入需要自動配置的組件。

進入@AutoConfigurationPackage娃殖,發(fā)現也是引入了@Import注解

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

@Import({Registrar.class})

public @interface AutoConfigurationPackage {

}

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

Registrar() {

}

public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {

AutoConfigurationPackages.register(registry, new String[]{(new AutoConfigurationPackages.PackageImport(metadata)).getPackageName()});

}

public Set determineImports(AnnotationMetadata metadata) {

return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));

}

}

new AutoConfigurationPackages.PackageImport(metadata)).getPackageName()

new AutoConfigurationPackages.PackageImport(metadata)

這兩句代碼的作用就是加載啟動類所在的包下的主類與子類的所有組件注冊到spring容器值戳,這就是前文所說的springboot默認掃描啟動類所在的包下的主類與子類的所有組件。

那問題又來了炉爆,要搜集并注冊到spring容器的那些beans來自哪里堕虹?

進入 AutoConfigurationImportSelector類,

如果想學習Java工程化芬首、高性能及分布式赴捞、深入淺出。微服務衩辟、Spring螟炫,MyBatis,Netty源碼分析的朋友可以加我的Java高級交流:854630135艺晴,群里有阿里大牛直播講解技術昼钻,以及Java大型互聯(lián)網技術的視頻免費分享給大家掸屡。

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {

private static final String[] NO_IMPORTS = new String[0];

...

public String[] selectImports(AnnotationMetadata annotationMetadata) {

if(!this.isEnabled(annotationMetadata)) {

return NO_IMPORTS;

} else {

AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);

AnnotationAttributes attributes = this.getAttributes(annotationMetadata);

List configurations = this.getCandidateConfigurations(annotationMetadata, attributes);

configurations = this.removeDuplicates(configurations);

Set exclusions = this.getExclusions(annotationMetadata, attributes);

this.checkExcludedClasses(configurations, exclusions);

configurations.removeAll(exclusions);

configurations = this.filter(configurations, autoConfigurationMetadata);

this.fireAutoConfigurationImportEvents(configurations, exclusions);

return StringUtils.toStringArray(configurations);

}

}

...

protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {

List 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;

}

...

}

SpringFactoriesLoader.loadFactoryNames方法調用loadSpringFactories方法從所有的jar包中讀取META-INF/spring.factories文件信息。

private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {

MultiValueMap result = (MultiValueMap)cache.get(classLoader);

if(result != null) {

return result;

} else {

try {

Enumeration ex = classLoader != null?classLoader.getResources("META-INF/spring.factories"):ClassLoader.getSystemResources("META-INF/spring.factories");

LinkedMultiValueMap result1 = new LinkedMultiValueMap();

while(ex.hasMoreElements()) {

URL url = (URL)ex.nextElement();

UrlResource resource = new UrlResource(url);

Properties properties = PropertiesLoaderUtils.loadProperties(resource);

Iterator var6 = properties.entrySet().iterator();

while(var6.hasNext()) {

Entry entry = (Entry)var6.next();

List factoryClassNames = Arrays.asList(StringUtils.commaDelimitedListToStringArray((String)entry.getValue()));

result1.addAll((String)entry.getKey(), factoryClassNames);

}

}

cache.put(classLoader, result1);

return result1;

} catch (IOException var9) {

throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var9);

}

}

}

下面是spring-boot-autoconfigure這個jar中spring.factories文件部分內容然评,其中有一個key為org.springframework.boot.autoconfigure.EnableAutoConfiguration的值定義了需要自動配置的bean仅财,通過讀取這個配置獲取一組@Configuration類。

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.OnClassCondition

# Auto Configure

org.springframework.boot.autoconfigure.EnableAutoConfiguration=

org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,

org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,

org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,

org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,

org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,

每個xxxAutoConfiguration都是一個基于java的bean配置類碗淌。實際上盏求,這些xxxAutoConfiguratio不是所有都會被加載,會根據xxxAutoConfiguration上的@ConditionalOnClass等條件判斷是否加載亿眠。

private static T instantiateFactory(String instanceClassName, Class factoryClass, ClassLoader classLoader) {

try {

Class ex = ClassUtils.forName(instanceClassName, classLoader);

if(!factoryClass.isAssignableFrom(ex)) {

throw new IllegalArgumentException("Class [" + instanceClassName + "] is not assignable to [" + factoryClass.getName() + "]");

} else {

return ReflectionUtils.accessibleConstructor(ex, new Class[0]).newInstance(new Object[0]);

}

} catch (Throwable var4) {

throw new IllegalArgumentException("Unable to instantiate factory class: " + factoryClass.getName(), var4);

}

}

如果想學習Java工程化碎罚、高性能及分布式、深入淺出纳像。微服務荆烈、Spring,MyBatis竟趾,Netty源碼分析的朋友可以加我的Java高級交流:854630135憔购,群里有阿里大牛直播講解技術,以及Java大型互聯(lián)網技術的視頻免費分享給大家岔帽。

如上代碼段玫鸟,通過反射機制將spring.factories中@Configuration類實例化為對應的java實列。到此我們已經知道怎么發(fā)現要自動配置的bean了犀勒,最后一步就是怎么樣將這些bean加載到spring容器屎飘。

bean加載

如果要讓一個普通類交給Spring容器管理,通常有以下方法:

1账蓉、使用 @Configuration與@Bean 注解

2枚碗、使用@Controller @Service @Repository @Component 注解標注該類,然后啟用@ComponentScan自動掃描

3铸本、使用@Import 方法

springboot中使用了@Import 方法

@EnableAutoConfiguration注解中使用了@Import({AutoConfigurationImportSelector.class})注解肮雨,AutoConfigurationImportSelector實現了DeferredImportSelector接口,

DeferredImportSelector接口繼承了ImportSelector接口箱玷,ImportSelector接口只有一個selectImports方法怨规。

public class AutoConfigurationImportSelector implements DeferredImportSelector{

...

public String[] selectImports(AnnotationMetadata annotationMetadata) {

if(!this.isEnabled(annotationMetadata)) {

return NO_IMPORTS;

} else {

AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);

AnnotationAttributes attributes = this.getAttributes(annotationMetadata);

List configurations = this.getCandidateConfigurations(annotationMetadata, attributes);

configurations = this.removeDuplicates(configurations);

Set exclusions = this.getExclusions(annotationMetadata, attributes);

this.checkExcludedClasses(configurations, exclusions);

configurations.removeAll(exclusions);

configurations = this.filter(configurations, autoConfigurationMetadata);

this.fireAutoConfigurationImportEvents(configurations, exclusions);

return StringUtils.toStringArray(configurations);

}

}

...

}

public interface DeferredImportSelector extends ImportSelector {

@Nullable

default Class getImportGroup() {

return null;

}

public interface Group {...}

}

public interface ImportSelector {

String[] selectImports(AnnotationMetadata var1);

}

我們先通過一個簡單例子看看@Import注解是如何將bean導入到spring容器的。

1锡足、新建一個bean

public class User {

private Long id;

private String name;

private String password;

private String phone;

...

}

如果想學習Java工程化波丰、高性能及分布式、深入淺出舶得。微服務掰烟、Spring,MyBatis,Netty源碼分析的朋友可以加我的Java高級交流:854630135纫骑,群里有阿里大牛直播講解技術蝎亚,以及Java大型互聯(lián)網技術的視頻免費分享給大家。

2先馆、創(chuàng)建一個ItpscSelector類繼承ImportSelector接口并實現selectImports方法

public class ItpscSelector implements ImportSelector {

public String[] selectImports(AnnotationMetadata importingClassMetadata) {

return new String[]{"com.itpsc.entity.User"};

}

}

3发框、創(chuàng)建ImportConfig類,使用@Configuration煤墙、@Import(ItpscSelector.class)注解梅惯。

@Configuration

@Import(ItpscSelector.class)

public class ImportConfig {

}

4、從容器獲取bean

@RunWith(SpringRunner.class)

@SpringBootTest

public class ImportSelectorTests {

@Test

public void testSelectImport() {

ApplicationContext ctx = new AnnotationConfigApplicationContext(ImportConfig.class);

String[] beanDefinitionNames = ctx.getBeanDefinitionNames();

for (String name : beanDefinitionNames) {

System.out.println(name);

}

}

}

運行結果:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor

org.springframework.context.annotation.internalAutowiredAnnotationProcessor

org.springframework.context.annotation.internalRequiredAnnotationProcessor

org.springframework.context.annotation.internalCommonAnnotationProcessor

org.springframework.context.event.internalEventListenerProcessor

org.springframework.context.event.internalEventListenerFactory

importConfig

com.itpsc.entity.User

很直觀仿野,selectImports方法返回一組bean铣减,@EnableAutoConfiguration注解借助@Import注解將這組bean注入到spring容器中,springboot正式通過這種機制來完成bean的注入的脚作。

回到頂部

總結

我們可以將自動配置的關鍵幾步以及相應的注解總結如下:

1徙歼、@Configuration&與@Bean->基于java代碼的bean配置

2、@Conditional->設置自動配置條件依賴

3鳖枕、@EnableConfigurationProperties與@ConfigurationProperties->讀取配置文件轉換為bean。

4桨螺、@EnableAutoConfiguration宾符、@AutoConfigurationPackage 與@Import->實現bean發(fā)現與加載。

如果想學習Java工程化灭翔、高性能及分布式魏烫、深入淺出。微服務肝箱、Spring哄褒,MyBatis,Netty源碼分析的朋友可以加我的Java高級交流:854630135煌张,群里有阿里大牛直播講解技術呐赡,以及Java大型互聯(lián)網技術的視頻免費分享給大家。

加群直通車:854630135

課堂直通車:點一下就好啦

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末骏融,一起剝皮案震驚了整個濱河市链嘀,隨后出現的幾起案子,更是在濱河造成了極大的恐慌档玻,老刑警劉巖怀泊,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異误趴,居然都是意外死亡霹琼,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來枣申,“玉大人售葡,你說我怎么就攤上這事∨炊” “怎么了天通?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長熄驼。 經常有香客問我像寒,道長,這世上最難降的妖魔是什么瓜贾? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任诺祸,我火速辦了婚禮,結果婚禮上祭芦,老公的妹妹穿的比我還像新娘筷笨。我一直安慰自己,他們只是感情好龟劲,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布胃夏。 她就那樣靜靜地躺著,像睡著了一般昌跌。 火紅的嫁衣襯著肌膚如雪仰禀。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天蚕愤,我揣著相機與錄音答恶,去河邊找鬼。 笑死萍诱,一個胖子當著我的面吹牛悬嗓,可吹牛的內容都是我干的。 我是一名探鬼主播裕坊,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼包竹,長吁一口氣:“原來是場噩夢啊……” “哼伏伯!你這毒婦竟也來了骄崩?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤膨报,失蹤者是張志新(化名)和其女友劉穎静浴,沒想到半個月后堰氓,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡苹享,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年双絮,在試婚紗的時候發(fā)現自己被綠了浴麻。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡囤攀,死狀恐怖软免,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情焚挠,我是刑警寧澤膏萧,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站蝌衔,受9級特大地震影響榛泛,放射性物質發(fā)生泄漏。R本人自食惡果不足惜噩斟,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一曹锨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧剃允,春花似錦沛简、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至牡肉,卻和暖如春撒顿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背荚板。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留吩屹,地道東北人跪另。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像煤搜,于是被迫代替她去往敵國和親免绿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內容

  • SpringMVC原理分析 Spring Boot學習 5擦盾、Hello World探究 1嘲驾、POM文件 1、父項目...
    jack_jerry閱讀 1,284評論 0 1
  • SpringBoot簡介 Spring Boot是由Pivotal團隊提供的全新框架迹卢,其設計目的是用來簡化新Spr...
    誰在烽煙彼岸閱讀 2,276評論 1 1
  • SpringBoot自動化配置的注解開關原理 發(fā)表于2016-11-13|分類于springboot|0 Comm...
    等一夏_81f7閱讀 296評論 0 0
  • 今晚發(fā)生在家里的故事辽故,我想把它記錄下來。 接姐姐回家后腐碱,孩子們在客廳玩耍誊垢。 我在房間里聽到了孩子們仿佛有爭執(zhí),出來...
    EvelynFish閱讀 402評論 0 0
  • 免費的午餐……現在真的有,但我不想吃喂走,包括免費看電影殃饿,免費游樂,免費洗浴芋肠,免費體驗…… 一聽到免費乎芳, 我便反彈式抵...
    活著不易閱讀 401評論 3 5