徹徹底底解決Spring中@EnableAsync、@Async異步調用的使用、原理及源碼分析

前言:

基于Spring框架的業(yè)務系統(tǒng)中由于一些業(yè)務場景的要求,我們經(jīng)常使用異步方法的方式來提高系統(tǒng)的處理性能,Spring框架為我們提供了默認的線程池,當然我們也可以對線程池進行自定義,本篇文章基于spring-context:5.1.6與大家進行探討,建議一邊看源碼一遍看我的文章。

寫這篇文章出于兩個原因,一是前一段時間和工作的小伙伴做一個新的項目,在項目中我們自然的使用到了異步方法,而且通過之前的經(jīng)驗也對線程池的參數(shù)進行了自定義設置,在應用上生產(chǎn)之前小伙伴對Spring異步的原理以及線程池參數(shù)調優(yōu)等方面與我進行了討論,這讓我又有了一次溫習異步的想法, 二是本人也沒有在網(wǎng)上搜集到比較滿意的Spring異步源碼解析的文章。

一、@EnabelAsync與@Asyn注解的使用

1.@EnabelAsync注解的使用。如不指定自定義異步線程池直接使用@EnableAsync即可使用,若自定義線程池可以使用下面的方法進行自定義,這種方法我認為可讀性比較好,當然你可以利用@Bean("taskExecutor")申明一個Executor類型的實例,至于為什么可以生效后面的文章會進行介紹。

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor threadPool = new ThreadPoolTaskExecutor();
        threadPool.setCorePoolSize(1);
        threadPool.setMaxPoolSize(1);
        threadPool.setWaitForTasksToCompleteOnShutdown(true);
        threadPool.setAwaitTerminationSeconds(60 * 15);
        threadPool.setThreadNamePrefix("MyAsync-");
        threadPool.initialize();
        return threadPool;
    }

}

2.@Asyn注解的使用。在下方代碼中在AsynMethodInvocation類中對使用@Asyn注解的異步方法進行了調用,再使用上其實很簡單,只要在想要實現(xiàn)異步的方法上使用@Asyn注解即可,值得注意的是調用方法與目標方法不能在一個類中,在一個類中異步不會生效,這與代理模式有一定的關系。

@RestController
public class AsynMethodInvocation {
    @Autowired
    AsynMethod asynMethod;
    @RequestMapping(value = "/test")
    public void doService() {
        System.out.println("調用線程:"+Thread.currentThread().getName());
        asynMethod.doService();
    }
}

@Service
public class AsynMethod {
    @Async
    public void doService() {
        System.out.println("異步線程:" + Thread.currentThread().getName());
    }
}

3.異步方法調用的驗證。我們可以看到上面的代碼執(zhí)行結果如下,異步方法在一個新的線程中完成。

調用線程:http-nio-8080-exec-2
2019-05-15 17:19:51.120  INFO 23368 --- [nio-8080-exec-2] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService
異步線程:ThreadPoolTaskExecutor-1

二、自動配置@EnableAsync的代理選擇

@EnableAsync自動配置類關系圖

1.從@EnableAsync注解入手,可以的得到@EnableAsync自動配置類之間的關系圖,在@EnableAsync中除了一些屬性以外(屬性通過看源碼的注釋應該很清楚)還有一個注解@Import(AsyncConfigurationSelector.class),項目啟動時通過讀取注解可以將該類引入,AsyncConfigurationSelector主要是用來選擇代理方式是由JDK還是AspectJ實現(xiàn)代理,默認使用JDK的代理。


@Import(AsyncConfigurationSelector.class)

public @interface EnableAsync {

Classannotation()default Annotation.class;

//當AdviceModemode為PROXY時,選擇代理是基于接口實現(xiàn)還是cglib實現(xiàn)

boolean proxyTargetClass()default false;

//代理方式是由JDK實現(xiàn)還是AspectJ實現(xiàn)

AdviceModemode()default AdviceMode.PROXY;

int order()default Ordered.LOWEST_PRECEDENCE;

}

2.從關系圖可以看出通過抽象類與實現(xiàn)類的方式(敲黑板:Java基礎知識不經(jīng)常使用容易忘)實現(xiàn)了selectImports方法,決定是返回ProxyAsyncConfiguration還是AspectJAsyncConfiguration。

public abstract class AdviceModeImportSelector<A extends Annotation> implements ImportSelector {
    @Override
    public final String[] selectImports(AnnotationMetadata importingClassMetadata) {
        Class<?> annType = GenericTypeResolver.resolveTypeArgument(getClass(), AdviceModeImportSelector.class);
        Assert.state(annType != null, "Unresolvable type argument for AdviceModeImportSelector");
        //獲得注解類讀取其中的配置,就是上圖代碼中的注解的屬性
        AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
        if (attributes == null) {
            throw new IllegalArgumentException(String.format(
                    "@%s is not present on importing class '%s' as expected",
                    annType.getSimpleName(), importingClassMetadata.getClassName()));
        }

        AdviceMode adviceMode = attributes.getEnum(getAdviceModeAttributeName());
        //該方法由AsyncConfigurationSelector類實現(xiàn),參數(shù)為注解的AdviceModemode屬性
        String[] imports = selectImports(adviceMode);
        if (imports == null) {
            throw new IllegalArgumentException("Unknown AdviceMode: " + adviceMode);
        }
        return imports;
    }
}

public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
    private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
            "org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";
    @Override
    @Nullable
    public String[] selectImports(AdviceMode adviceMode) {
        switch (adviceMode) {
            case PROXY:
                return new String[] {ProxyAsyncConfiguration.class.getName()};
            case ASPECTJ:
                return new String[] {ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME};
            default:
                return null;
        }
    }
}

3.截止到現(xiàn)在即可實現(xiàn)ProxyAsyncConfiguration配置類的引入,這項工作其實是在org.springframework.context.annotation.ConfigurationClassParser#processImports中完成,從而實現(xiàn)ProxyAsyncConfiguration的自動配置

三、ProxyAsyncConfiguration的自動配置

在注解中定義了兩種代理的方式后,有不同的自動配置類進行加載,我們這里只對JDK的代理方式進行說明,主要是理解一下整個原理與流程,有興趣的同學也可以去了解AspectJ的具體做法,下面我們來看一下ProxyAsyncConfiguration。


ProxyAsyncConfiguration自動配置類關系圖

1.ProxyAsyncConfiguration 類主要就是通過自動配置,讀取注解中的相關屬性,從而實例化AsyncAnnotationBeanPostProcessor,代碼中的注釋設置bbp對象的過程(敲黑板:Spring代碼的名字其實很有講究,通過名字就可以大概知道對象的作用,如看到BeanPostProcessor大家應該就會想到Spring中的Bean生命周期中的后置處理器,如不熟悉請自行補課,其實我也有似乎有一些忘記了)。

@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {
    @Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
        Assert.notNull(this.enableAsync, "@EnableAsync annotation metadata was not injected");
        //整個方法就是在Spring上下文中實例化一個AsyncAnnotationBeanPostProcessor,這個Bean主要是實現(xiàn)方法或者類上使用@Async注解從而達到異步的效果
        AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
        //設置執(zhí)行器與異常執(zhí)行器
        bpp.configure(this.executor, this.exceptionHandler);
        //注解annotation是否指定了自定義的注解,如果沒有指定默認@Async和 @javax.ejb.Asynchronous注解生效,若自定義需要加入到bbp中
        Class<? extends Annotation> customAsyncAnnotation = this.enableAsync.getClass("annotation");
        if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) {
            bpp.setAsyncAnnotationType(customAsyncAnnotation);
        }
        //注解ProxyTargetClass、setOrder屬性的設置  
        bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));
        bpp.setOrder(this.enableAsync.<Integer>getNumber("order"));
        return bpp;
    }
}

2.在ProxyAsyncConfiguration中我們可以看到它繼承了抽象類AbstractAsyncConfiguration,它是一個基礎的配置類提供了異步的一些公共功能,可以通過實現(xiàn)AsyncConfigurer接口或者繼承AsyncConfigurerSupport類來實現(xiàn)自定義異步線程池執(zhí)行器與異常執(zhí)行器,如果自定義了則會設置到bpp對象中,若沒有自定義Spring會找實現(xiàn)TaskExecutor接口或bean的名字為taskExecutor的執(zhí)行器。

@Configuration
public abstract class AbstractAsyncConfiguration implements ImportAware {
    @Override
    public void setImportMetadata(AnnotationMetadata importMetadata) {
        //獲得@EnableAsync注解及其屬性
        this.enableAsync = AnnotationAttributes.fromMap(
                importMetadata.getAnnotationAttributes(EnableAsync.class.getName(), false));
        if (this.enableAsync == null) {
            throw new IllegalArgumentException(
                    "@EnableAsync is not present on importing class " + importMetadata.getClassName());
        }
    }
    @Autowired(required = false)
    void setConfigurers(Collection<AsyncConfigurer> configurers) {
        if (CollectionUtils.isEmpty(configurers)) {
            return;
        }
        if (configurers.size() > 1) {
            throw new IllegalStateException("Only one AsyncConfigurer may exist");
        }
        AsyncConfigurer configurer = configurers.iterator().next();
        //設置自定義執(zhí)行器與異常執(zhí)行器,若沒有自定義則不會執(zhí)行setConfigurers方法
        this.executor = configurer::getAsyncExecutor;
        this.exceptionHandler = configurer::getAsyncUncaughtExceptionHandler;
    }
}

四、AsyncAnnotationBeanPostProcessor 初始化

在第二節(jié)我們主要講解了如何初始化AsyncAnnotationBeanPostProcessor對象,但是大家可能對這個對象是干什么的在什么時候使用存在一定的疑慮,這節(jié)我們就重點來說一下Spring是如何生成代理類,首先我們看一下AsyncAnnotationBeanPostProcessor類的關系圖,看起來的確很可怕(從前的我看到這種圖幾乎也會放棄查看源碼的奢望),但是我們我們不用過多的擔心,主要看下一下藍色箭頭的類。


AsyncAnnotationBeanPostProcessor類關系圖

1.,由于AsyncAnnotationBeanPostProcessor類在父類AbstractBeanFactoryAwareAdvisingPostProcessor中實現(xiàn)了BeanFactoryAware接口,所以在初始化后,會執(zhí)行setBeanFactory方法,在該方法中,實例化了AsyncAnnotationAdvisor。

public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor {
    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        super.setBeanFactory(beanFactory);
        //實例化AsyncAnnotationAdvisor對象,初始化異步切面,該對象通過注解實現(xiàn)AOP的通知,使其在使用注解時能夠觸發(fā)異步方法的執(zhí)行
        AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);
        if (this.asyncAnnotationType != null) {
            advisor.setAsyncAnnotationType(this.asyncAnnotationType);
        }
        advisor.setBeanFactory(beanFactory);
        this.advisor = advisor;
    }
}

2.AbstractAdvisingBeanPostProcessor類中實現(xiàn)了BeanPostProcessor方法,在AsyncAnnotationBeanPostProcessor類初始化完后,會執(zhí)行postProcessAfterInitialization方法,在該方法中會生成目標類的代理類,從而實現(xiàn)在調用該方法時能后達到異步的效果。

public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSupport implements BeanPostProcessor {
    private final Map<Class<?>, Boolean> eligibleBeans = new ConcurrentHashMap<>(256);

    public Object postProcessAfterInitialization(Object bean, String beanName) {
        if (this.advisor == null || bean instanceof AopInfrastructureBean) {
            // Ignore AOP infrastructure such as scoped proxies.
            return bean;
        }
        //第一次進入到這個方法時bean是注解了異步的那個原始類,因為那時候還沒有生成代理類bean instanceof Advised為false,所以該端代碼不會執(zhí)行。還有可能進入到這個方法是生成代理類后實例化CglibAopProxy時會進入到這個類,bean為該類的代理類,這不做過多的介紹,感興趣的可以去查看一下Spring Aop的源碼部分
        if (bean instanceof Advised) {
            Advised advised = (Advised) bean;
            if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
                // Add our local Advisor to the existing proxy's Advisor chain...
                if (this.beforeExistingAdvisors) {
                    advised.addAdvisor(0, this.advisor);
                }
                else {
                    advised.addAdvisor(this.advisor);
                }
                return bean;
            }
        }
        //由于在初始化AsyncAnnotationBeanPostProcessor,該方法并沒有調用本類中的,而是調用了下方類的isEligible方法,主要判斷改類是否為原始類,而不是該類的代理類,如果是原始類則進入方法。
        if (isEligible(bean, beanName)) {
            //創(chuàng)建目標類的代理工廠,在AbstractBeanFactoryAwareAdvisingPostProcessor中重寫了該方法
            ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
            //判斷代理使用的是JDK代理還是使用的CGLIB代理,默認false,使用JDK創(chuàng)建代理
            if (!proxyFactory.isProxyTargetClass()) {
                //檢查bean是接口還是類,如果是接口默認使用JDK創(chuàng)建代理,如果為類則修改為使用CGLIB來創(chuàng)建代理
                evaluateProxyInterfaces(bean.getClass(), proxyFactory);
            }
            //代理工廠應用自定義的Advisor(AsynAnnotationAdvisor)
            proxyFactory.addAdvisor(this.advisor);
            customizeProxyFactory(proxyFactory);
            //返回該類的代理類
            return proxyFactory.getProxy(getProxyClassLoader());
        }
        // No proxy needed.
        return bean;
    }
}

public abstract class AbstractBeanFactoryAwareAdvisingPostProcessor extends AbstractAdvisingBeanPostProcessor implements BeanFactoryAware {
@Override
    protected boolean isEligible(Object bean, String beanName) {
        return (!AutoProxyUtils.isOriginalInstance(beanName, bean.getClass()) &&
                super.isEligible(bean, beanName));
    }
}

3.在這里簡單的介紹一下是proxyFactory是如何生成代理的,創(chuàng)建Aop代理的過程是在DefaultAopProxyFactory中完成,根據(jù)Config不同的屬性條件,來決定不同類型的AOP代理(JDK動態(tài)代理、CGLIB代理),然后根據(jù)創(chuàng)建的代理getProxy方法生成代理類,如CglibAopProxy#getProxy。

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
    @Override
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: " +
                        "Either an interface or a target is required for proxy creation.");
            }
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }
}

總結:到這里我們應該對@EnableAsync自動配置有一個比較清晰的認識,回顧一下這部分的內容主要從兩方面來入手。首先,@EnableAsync注解-->配置選擇-->ProxyAsyncConfiguration自動配置-->初始化AsyncAnnotationBeanPostProcessor對象。其次,AsyncAnnotationBeanPostProcessor自定義Advisor-->AbstractAdvisingBeanPostProcessor實現(xiàn)代理類的生成。

五、@Asyn注解實現(xiàn)異步的過程

在之前的部分我們一筆帶過了AsyncAnnotationAdvisor對象的初始化是因為這部分與Spring Aop有很大關系,所以這沒有做重點的介紹(其實這部分我也沒有去關注),但是它的類的關系圖中我們可以注意到AsyncAnnotationAdvisor類中有一個buildAdvice方法,生成了AnnotationAsyncExecutionInterceptor對象,它的父類AsyncExecutionInterceptor重寫了AsyncExecutionInterceptor接口的invoke方法,通過委托實現(xiàn)@Async異步方法的調用。


AsyncAnnotationAdvisor類關系圖

當我們使用@Async注解在方法或者類上面時,在進行方法調用時,代理類會進行攔截,如CglibAopProxy.DynamicAdvisedInterceptor的intercept方法,在這里我們不會仔細的去分析這個方法的源碼,大家只要知道在該方法中retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed()這行代碼的proceed方法時會被
AsyncExecutionInterceptor攔截器進行攔截,在該攔截器中完成了異步方法的調用。

org.springframework.aop.interceptor.AsyncExecutionInterceptor#invoke
public Object invoke(final MethodInvocation invocation) throws Throwable {
        //該方法的參數(shù)為異步要調用的方法
        Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
        //獲得方法的詳細信息,如參數(shù)、返回值等
        Method specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass);
        final Method userDeclaredMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
        //確定異步執(zhí)行器,在spring上下文中找到實現(xiàn)TaskExecutor的實例
        AsyncTaskExecutor executor = determineAsyncExecutor(userDeclaredMethod);
        if (executor == null) {
            throw new IllegalStateException(
                    "No executor specified and no default executor set on AsyncExecutionInterceptor either");
        }
        //建立一個線程,用于執(zhí)行目標異步方法,該段代碼只是線程的生命,而沒有直接的調用
        Callable<Object> task = () -> {
            try {
                Object result = invocation.proceed();
                if (result instanceof Future) {
                    return ((Future<?>) result).get();
                }
            }
            catch (ExecutionException ex) {
                handleError(ex.getCause(), userDeclaredMethod, invocation.getArguments());
            }
            catch (Throwable ex) {
                handleError(ex, userDeclaredMethod, invocation.getArguments());
            }
            return null;
        };
        //結合目標方法、異步執(zhí)行器對目標方法進行調用
        return doSubmit(task, executor, invocation.getMethod().getReturnType());
    }

在上述的代碼中determineAsyncExecutor方法用于確定異步執(zhí)行器,在執(zhí)行defaultExecutor.get()方法時,最終會執(zhí)行AsyncExecutionAspectSupport的getDefaultExecutor方法,該方法會查找實現(xiàn)TaskExecutor接口的實例作為執(zhí)行器,在該文中沒有特別實現(xiàn)TaskExecutor接口,故默認的執(zhí)行器為ThreadPoolTaskExecutor。

org.springframework.aop.interceptor.AsyncExecutionAspectSupport#determineAsyncExecutor
protected AsyncTaskExecutor determineAsyncExecutor(Method method) {
        //executors的類型為Map<Method, AsyncTaskExecutor>,用于緩存方法的執(zhí)行器,第一次加載時executor為null,調用過一次后對應關系緩存在Map中
        AsyncTaskExecutor executor = this.executors.get(method);
        if (executor == null) {
            Executor targetExecutor;
            String qualifier = getExecutorQualifier(method);
            if (StringUtils.hasLength(qualifier)) {
                targetExecutor = findQualifiedExecutor(this.beanFactory, qualifier);
            }
            else {
                //指定執(zhí)行器
                targetExecutor = this.defaultExecutor.get();
            }
            if (targetExecutor == null) {
                return null;
            }
            executor = (targetExecutor instanceof AsyncListenableTaskExecutor ?
                    (AsyncListenableTaskExecutor) targetExecutor : new TaskExecutorAdapter(targetExecutor));
            this.executors.put(method, executor);
        }
        return executor;
    }

在invoke方法的最后一行是submit方法,最后的執(zhí)行要落在線程池的執(zhí)行,spring線程池的實現(xiàn)這里不做過多的介紹,大家只要知道在這里利用其他線程實現(xiàn)了異步方法的調用即可。

org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor#submit
public <T> Future<T> submit(Callable<T> task) {
        ExecutorService executor = getThreadPoolExecutor();
        try {
            return executor.submit(task);
        }
        catch (RejectedExecutionException ex) {
            throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
        }
    }

總結:在上一部分我們了解了@EnableAsync注解,這一部分我們重點對@Asyn注解在調用時是如何實現(xiàn)異步的進行了分析,主要是兩個方面。一是在方法調用是對其進行攔截,然后指定方法執(zhí)行的執(zhí)行器;二是在方法的調用過程中應用了線程池,由線程池執(zhí)行task從而達到異步的作用。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,316評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,481評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,241評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,939評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,697評論 6 409
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,182評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,247評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,406評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,933評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,772評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,973評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,516評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,638評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,866評論 1 285
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,644評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,953評論 2 373