SpringBoot——AOP使用

一优幸、Aop關(guān)鍵術(shù)語個人理解

1.1 Joinpoint(連接點)

所謂連接點是指那些被攔截到的點吨拍。在spring中,這些點指的是方法,因為spring只支持方法類型的連接點。(通俗理解:業(yè)務(wù)層接口的所有方法都叫連接點)

1.2 Pointcut(切入點)

所謂切入點是指我們要對哪些Joinpoint進行攔截的定義网杆。 (通俗理解:被增強的業(yè)務(wù)層接口的方法叫切入點)

這樣看來羹饰,連接點不一定是切入點,但切入點一定是連接點跛璧。

1.3 Advice(通知/增強)

所謂通知是指攔截到Joinpoint之后所要做的事情就是通知严里。
通知的類型:前置通知、后置通知追城、異常通知刹碾、最終通知、環(huán)繞通知座柱。

通知的查找方法:找到invoke方法中明確調(diào)用業(yè)務(wù)層那行代碼迷帜,在其之前執(zhí)行的就是前置通知,在其之后執(zhí)行的就是后置通知色洞,在catch中的就是異常通知戏锹,在finally中的就是最終通知。整個的invoke方法執(zhí)行就是環(huán)繞通知火诸。

注意:返回通知和異常通知只能有一個會被執(zhí)行锦针,因為發(fā)生異常執(zhí)行異常通知,然后就不會繼續(xù)向下執(zhí)行置蜀,自然后置通知也就不會被執(zhí)行奈搜,反之亦然。

1.4 Introduction(引介)

引介是一種特殊的通知在不修改類代碼的前提下, Introduction可以在運行期為類動態(tài)地添加一些方法或Field盯荤。

1.5 Target(目標(biāo)對象)

代理的目標(biāo)對象馋吗。(被代理的對象)

1.6 Weaving(織入):

織入是指把增強應(yīng)用到目標(biāo)對象來創(chuàng)建新的代理對象的過程。 spring采用動態(tài)代理織入秋秤,而AspectJ采用編譯期織入和類裝載期織入宏粤。

1.7 Proxy(代理)

一個類被AOP織入增強后,就產(chǎn)生一個結(jié)果代理類灼卢。(代理對象)

1.8 Aspect(切面):

切面是切入點和通知(引介)的結(jié)合绍哎。 (通俗理解:建立切入點方法和通知方法在執(zhí)行調(diào)用的對應(yīng)關(guān)系就是切面)

二、Pointcut表達(dá)式

2.1 Pointcut表達(dá)式類型

標(biāo)準(zhǔn)的AspectJ Aop的pointcut的表達(dá)式類型是很豐富的鞋真,但是Spring Aop只支持其中的9種蛇摸,外加Spring Aop自己擴充的一種一共是10種類型的表達(dá)式,分別如下灿巧。

  • execution:一般用于指定方法的執(zhí)行赶袄,用的最多揽涮。
  • within:指定某些類型的全部方法執(zhí)行,也可用來指定一個包饿肺。
  • this:Spring Aop是基于動態(tài)代理的蒋困,生成的bean也是一個代理對象,this就是這個代理對象敬辣,當(dāng)這個對象可以轉(zhuǎn)換為指定的類型時雪标,對應(yīng)的切入點就是它了,Spring Aop將生效溉跃。
  • target:當(dāng)被代理的對象可以轉(zhuǎn)換為指定的類型時村刨,對應(yīng)的切入點就是它了,Spring Aop將生效撰茎。
  • args:當(dāng)執(zhí)行的方法的參數(shù)是指定類型時生效嵌牺。
  • @target:當(dāng)代理的目標(biāo)對象上擁有指定的注解時生效。
  • @args:當(dāng)執(zhí)行的方法參數(shù)類型上擁有指定的注解時生效龄糊。
  • @within:與@target類似逆粹,看官方文檔和網(wǎng)上的說法都是@within只需要目標(biāo)對象的類或者父類上有指定的注解,則@within會生效炫惩,而@target則是必須是目標(biāo)對象的類上有指定的注解僻弹。而根據(jù)筆者的測試這兩者都是只要目標(biāo)類或父類上有指定的注解即可。
  • @annotation:當(dāng)執(zhí)行的方法上擁有指定的注解時生效他嚷。
  • bean:當(dāng)調(diào)用的方法是指定的bean的方法時生效蹋绽。

Pointcut定義時,還可以使用&&筋蓖、||卸耘、! 這三個運算。進行邏輯運算扭勉。可以把各種條件組合起來使用苛聘。

2.2 Pointcut表達(dá)式使用示例

2.2.1 execution

execution是使用的最多的一種Pointcut表達(dá)式涂炎,表示某個方法的執(zhí)行,其標(biāo)準(zhǔn)語法如下设哗。

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
  • 修飾符匹配(modifier-pattern?)
  • 返回值匹配(ret-type-pattern)可以為*表示任何返回值,全路徑的類名等
  • 類路徑匹配(declaring-type-pattern?)
  • 方法名匹配(name-pattern)可以指定方法名 或者 代表所有, set 代表以set開頭的所有方法
  • 參數(shù)匹配((param-pattern))可以指定具體的參數(shù)類型唱捣,多個參數(shù)間用“,”隔開,各個參數(shù)也可以用“”來表示匹配任意類型的參數(shù)网梢,如(String)表示匹配一個String參數(shù)的方法震缭;(,String) 表示匹配有兩個參數(shù)的方法,第一個參數(shù)可以是任意類型战虏,而第二個參數(shù)是String類型拣宰;可以用(…)表示零個或多個任意參數(shù)
  • 異常類型匹配(throws-pattern?)
  • 其中后面跟著“?”的是可選項
下面看幾個例子
//表示匹配所有方法  
1)execution(* *(..))  

//表示匹配com.fsx.run.UserService中所有的公有方法  
2)execution(public * com.fsx.run.UserService.*(..))  

//表示匹配com.fsx.run包及其子包下的所有方法
3)execution(* com.fsx.run..*.*(..))  

Pointcut定義時党涕,還可以使用&&、||巡社、! 這三個運算膛堤。進行邏輯運算

// 簽名:消息發(fā)送切面
@Pointcut("execution(* com.fsx.run.MessageSender.*(..))")
private void logSender(){}

// 簽名:消息接收切面
@Pointcut("execution(* com.fsx.run.MessageReceiver.*(..))")
private void logReceiver(){}

// 只有滿足發(fā)送  或者  接收  這個切面都會切進去
@Pointcut("logSender() || logReceiver()")
private void logMessage(){}

這個例子中,logMessage()將匹配任何MessageSender和MessageReceiver中的任何方法晌该。

當(dāng)我們的切面很多的時候肥荔,我們可以把所有的切面放到單獨的一個類去,進行統(tǒng)一管理朝群,比如下面:

//集中管理所有的切入點表達(dá)式
public class Pointcuts {

@Pointcut("execution(* *Message(..))")
public void logMessage(){}

@Pointcut("execution(* *Attachment(..))")
public void logAttachment(){}

@Pointcut("execution(* *Service.*(..))")
public void auth(){}
}

這樣別的使用時燕耿,采用全類名+方法名的方式

@Before("com.fsx.run.Pointcuts.logMessage()")
public void before(JoinPoint joinPoint) {
    System.out.println("Logging before " + joinPoint.getSignature().getName());
}

2.2.2 within

within是用來指定類型的,指定類型中的所有方法將被攔截姜胖。

// AService下面所有外部調(diào)用方法誉帅,都會攔截。備注:只能是AService的方法谭期,子類不會攔截的
@Pointcut("within(com.fsx.run.service.AService)")
public void pointCut() {
}

所以此處需要注意:上面寫的是AService接口堵第,是達(dá)不到攔截效果的,只能寫實現(xiàn)類:

//此處只能寫實現(xiàn)類
@Pointcut("within(com.fsx.run.service.impl.AServiceImpl)")
public void pointCut() {
}

匹配包以及子包內(nèi)的所有類:

@Pointcut("within(com.fsx.run.service..*)")
public void pointCut() {
}

2.2.3 this

Spring Aop是基于代理的隧出,this就表示代理對象踏志。this類型的Pointcut表達(dá)式的語法是this(type),當(dāng)生成的代理對象可以轉(zhuǎn)換為type指定的類型時則表示匹配胀瞪≌胗啵基于JDK接口的代理和基于CGLIB的代理生成的代理對象是不一樣的。(注意和上面within的區(qū)別)

// 這樣子凄诞,就可以攔截到AService所有的子類的所有外部調(diào)用方法
@Pointcut("this(com.fsx.run.service.AService*)")
public void pointCut() {
}

2.2.4 target

Spring Aop是基于代理的圆雁,target則表示被代理的目標(biāo)對象。當(dāng)被代理的目標(biāo)對象可以被轉(zhuǎn)換為指定的類型時則表示匹配帆谍。

注意:和上面不一樣伪朽,這里是target,因此如果要切入汛蝙,只能寫實現(xiàn)類了

@Pointcut("target(com.fsx.run.service.impl.AServiceImpl)")
public void pointCut() {
}

2.2.5 args

args用來匹配方法參數(shù)的烈涮。

  • 1、“args()”匹配任何不帶參數(shù)的方法窖剑。
  • 2坚洽、“args(java.lang.String)”匹配任何只帶一個參數(shù),而且這個參數(shù)的類型是String的方法。
  • 3、“args(…)”帶任意參數(shù)的方法剪侮。
  • 4跷究、“args(java.lang.String,…)”匹配帶任意個參數(shù)跳昼,但是第一個參數(shù)的類型是String的方法般甲。
  • 5、“args(…,java.lang.String)”匹配帶任意個參數(shù)庐舟,但是最后一個參數(shù)的類型是String的方法欣除。
@Pointcut("args()")
public void pointCut() {
}

這個匹配的范圍非常廣,所以一般和別的表達(dá)式結(jié)合起來使用

2.2.6 @target

@target匹配當(dāng)被代理的目標(biāo)對象對應(yīng)的類型及其父類型上擁有指定的注解時挪略。

//能夠切入類上(非方法上)標(biāo)準(zhǔn)了MyAnno注解的所有外部調(diào)用方法
@Pointcut("@target(com.fsx.run.anno.MyAnno)")
public void pointCut() {
}

2.2.7 @args

@args匹配被調(diào)用的方法上含有參數(shù)历帚,且對應(yīng)的參數(shù)類型上擁有指定的注解的情況。

例如:

// 匹配**方法參數(shù)類型上**擁有MyAnno注解的方法調(diào)用杠娱。如我們有一個方法add(MyParam param)接收一個MyParam類型的參數(shù)挽牢,而MyParam這個類是擁有注解MyAnno的,則它可以被Pointcut表達(dá)式匹配上
@Pointcut("@args(com.fsx.run.anno.MyAnno)")
public void pointCut() {
}

2.2.8 @within:

@within用于匹配被代理的目標(biāo)對象對應(yīng)的類型或其父類型擁有指定的注解的情況摊求,但只有在調(diào)用擁有指定注解的類上的方法時才匹配禽拔。

“@within(com.fsx.run.anno.MyAnno)”匹配被調(diào)用的方法聲明的類上擁有MyAnno注解的情況。比如有一個ClassA上使用了注解MyAnno標(biāo)注室叉,并且定義了一個方法a()睹栖,那么在調(diào)用ClassA.a()方法時將匹配該Pointcut;如果有一個ClassB上沒有MyAnno注解茧痕,但是它繼承自ClassA野来,同時它上面定義了一個方法b(),那么在調(diào)用ClassB().b()方法時不會匹配該Pointcut踪旷,但是在調(diào)用ClassB().a()時將匹配該方法調(diào)用曼氛,因為a()是定義在父類型ClassA上的,且ClassA上使用了MyAnno注解令野。但是如果子類ClassB覆寫了父類ClassA的a()方法舀患,則調(diào)用ClassB.a()方法時也不匹配該Pointcut。

2.2.9 @annotation:使用得也比較多

@annotation用于匹配方法上擁有指定注解的情況气破。

// 可以匹配所有方法上標(biāo)有此注解的方法
@Pointcut("@annotation(com.fsx.run.anno.MyAnno)")
public void pointCut() {
}

我們還可以這么寫聊浅,非常方便的獲取到方法上面的注解

@Before("@annotation(myAnno)")
public void doBefore(JoinPoint joinPoint, MyAnno myAnno) {
    System.out.println(myAnno); //@com.fsx.run.anno.MyAnno()
    System.out.println("AOP Before Advice...");
}

2.2.10 bean

這是Spring增加的一種方法,spring獨有现使,bean用于匹配當(dāng)調(diào)用的是指定的Spring的某個bean的方法時低匙。

  • 1、“bean(abc)”匹配Spring Bean容器中id或name為abc的bean的方法調(diào)用朴下。
  • 2努咐、“bean(user*)”匹配所有id或name為以user開頭的bean的方法調(diào)用苦蒿。
// 這個就能切入到AServiceImpl類的素有的外部調(diào)用的方法里
@Pointcut("bean(AServiceImpl)")
public void pointCut() {
}

2.3 類型匹配語法

  • *:匹配任何數(shù)量字符殴胧;
  • ...:匹配任何數(shù)量字符的重復(fù),如在類型模式中匹配任何數(shù)量子包;而在方法參數(shù)模式中匹配任何數(shù)量參數(shù)团滥。
    +:匹配指定類型的子類型竿屹;僅能作為后綴放在類型模式后邊。
java.lang.String    匹配String類型灸姊; 
java.*.String       匹配java包下的任何“一級子包”下的String類型拱燃; 如匹配java.lang.String,但不匹配java.lang.ss.String 
java..*             匹配java包及任何子包下的任何類型力惯。如匹配java.lang.String碗誉、java.lang.annotation.Annotation 
java.lang.*ing      匹配任何java.lang包下的以ing結(jié)尾的類型;
java.lang.Number+   匹配java.lang包下的任何Number的子類型父晶; 如匹配java.lang.Integer哮缺,也匹配java.math.BigInteger 

2.4 表達(dá)式的組合

表達(dá)式的組合其實就是對應(yīng)的表達(dá)式的邏輯運算,與甲喝、或尝苇、非〔号郑可以通過它們把多個表達(dá)式組合在一起糠溜。

  • 1、“bean(userService) && args()”匹配id或name為userService的bean的所有無參方法直撤。
  • 2非竿、“bean(userService) || @annotation(MyAnnotation)”匹配id或name為userService的bean的方法調(diào)用,或者是方法上使用了MyAnnotation注解的方法調(diào)用谊惭。
  • 3汽馋、“bean(userService) && !args()”匹配id或name為userService的bean的所有有參方法調(diào)用。

三使用案例

3.1 引入aop相關(guān)的依賴

<!--aop相關(guān)的依賴引入-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

3.2 環(huán)繞通知使用

@Component
@Aspect
@Slf4j
public class FeignCallDetailAspect {

    // 定義切點Pointcut
    @Pointcut("execution(* com.yibo.order.service..api.open.*.*(..))")
    public void excludeService() {

    }

    @Around("excludeService()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {

        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        HttpServletRequest request = sra.getRequest();

        String url = request.getRequestURL().toString();
        String method = request.getMethod();
        String uri = request.getRequestURI();
        String queryString = request.getQueryString();

        //log.info("請求開始, 各個參數(shù), url: {}, method: {}, uri: {}, params: {}", url, method, uri, queryString);
        Object[] args = joinPoint.getArgs();
        String params = "";
        //獲取請求參數(shù)集合并進行遍歷拼接
        if(args.length>0){
            if("POST".equals(method)){
                Object object = args[0];
                //請求參數(shù)
                Map<String, Object> map = getKeyAndValue(object);

            }else if("GET".equals(method)){
                params = queryString;
            }
        }


        // result的值就是被攔截方法的返回值
        Object result = joinPoint.proceed();

        log.info("請求結(jié)束圈盔,controller的返回值是 " + JSON.toJSONString(result));
        return result;
    }

    /**
     * 獲取接口參數(shù)
     * @param obj
     * @return
     */
    public static Map<String, Object> getKeyAndValue(Object obj) {
        Map<String, Object> map = new HashMap<>();
        // 得到類對象
        Class userCla = (Class) obj.getClass();
        /* 得到類中的所有屬性集合 */
        Field[] fs = userCla.getDeclaredFields();
        for (int i = 0; i < fs.length; i++) {
            Field f = fs[i];
            f.setAccessible(true); // 設(shè)置些屬性是可以訪問的
            Object val = new Object();
            try {
                val = f.get(obj);
                // 得到此屬性的值
                map.put(f.getName(), val);// 設(shè)置鍵值
            } catch (IllegalArgumentException | IllegalAccessException e) {
                e.printStackTrace();
            }

        }
        return map;
    }

}

3.3 內(nèi)網(wǎng)調(diào)用接口自動賦值app security的AOP方案

/**
 * app授權(quán)配置組件
 *
 **/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@Component
public class AppSecurityConfigComponent {

    /**
     應(yīng)用id
     */
    @Value("${xundu.app-id}")
    private String appId;

    /**
     版本號
     */
    @Value("${xundu.app.version}")
    private String version;


    /**
     密鑰
     */
    @Value("${xundu.app.security-key}")
    private String securityKey;

    public void setSecurityProperties(SecurityBaseForm securityForm) {
        securityForm.setVersion(this.version);
        securityForm.setAppId(this.appId);
        securityForm.setSecurityKey(this.securityKey);
    }

}
/**
 * 解決調(diào)用內(nèi)網(wǎng)接口自動賦值app security的AOP方案
 *
 */
@Component
@Aspect
public class SecurityBaseFormAspect {

    @Resource
    private AppSecurityConfigComponent securityKeyConfigComponent;

    /**
     * 切面的聲明
     * 影響到的方法:
     * 1: 被{@link org.springframework.cloud.openfeign.FeignClient}標(biāo)記的類
     * 2: 參數(shù)為{@link SecurityBaseForm}的所有方法
     */
    @Pointcut("@within(org.springframework.cloud.openfeign.FeignClient) && args(xundu.app.api.validate.SecurityBaseForm)")
    public void pointCut() {
    }

    /**
     * 定義Advice
     * 前置處理豹芯, 對請求的參數(shù)進行賦值:將配置的App配置賦值到SecurityBaseForm中
     *
     * @param joinPoint 連接點對象
     */
    @Before("pointCut()")
    public void handle(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        Arrays.stream(args).filter(arg -> arg instanceof SecurityBaseForm)
                .forEach(arg -> securityKeyConfigComponent
                        .setSecurityProperties((SecurityBaseForm) arg));
    }
}

四、理解AOP

4.1 什么是AOP

AOP(Aspect Oriented Programming)驱敲,面向切面思想铁蹈,是Spring的三大核心思想之一(兩外兩個:IOC-控制反轉(zhuǎn)、DI-依賴注入)众眨。

那么AOP為何那么重要呢握牧?在我們的程序中,經(jīng)常存在一些系統(tǒng)性的需求娩梨,比如權(quán)限校驗沿腰、日志記錄、統(tǒng)計等狈定,這些代碼會散落穿插在各個業(yè)務(wù)邏輯中颂龙,非常冗余且不利于維護习蓬。例如下面這個示意圖:

有多少業(yè)務(wù)操作,就要寫多少重復(fù)的校驗和日志記錄代碼措嵌,這顯然是無法接受的躲叼。當(dāng)然,用面向?qū)ο蟮乃枷肫蟪玻覀兛梢园堰@些重復(fù)的代碼抽離出來枫慷,寫成公共方法,就是下面這樣:


這樣浪规,代碼冗余和可維護性的問題得到了解決或听,但每個業(yè)務(wù)方法中依然要依次手動調(diào)用這些公共方法,也是略顯繁瑣笋婿。有沒有更好的方式呢神帅?有的,那就是AOP萌抵,AOP將權(quán)限校驗找御、日志記錄等非業(yè)務(wù)代碼完全提取出來,與業(yè)務(wù)代碼分離绍填,并尋找節(jié)點切入業(yè)務(wù)代碼中:


4.2 AOP體系與概念

簡單地去理解霎桅,其實AOP要做三類事:

  • 在哪里切入,也就是權(quán)限校驗等非業(yè)務(wù)操作在哪些業(yè)務(wù)代碼中執(zhí)行讨永。
  • 在什么時候切入滔驶,是業(yè)務(wù)代碼執(zhí)行前還是執(zhí)行后。
  • 切入后做什么事卿闹,比如做權(quán)限校驗揭糕、日志記錄等。

因此锻霎,AOP的體系可以梳理為下圖:


一些概念詳解:

  • Pointcut:切點著角,決定處理如權(quán)限校驗、日志記錄等在何處切入業(yè)務(wù)代碼中(即織入切面)旋恼。切點分為execution方式和annotation方式吏口。前者可以用路徑表達(dá)式指定哪些類織入切面,后者可以指定被哪些注解修飾的代碼織入切面冰更。

  • Advice:處理产徊,包括處理時機和處理內(nèi)容。處理內(nèi)容就是要做什么事蜀细,比如校驗權(quán)限和記錄日志舟铜。處理時機就是在什么時機執(zhí)行處理內(nèi)容,分為前置處理(即業(yè)務(wù)代碼執(zhí)行前)奠衔、后置處理(業(yè)務(wù)代碼執(zhí)行后)等谆刨。

  • Aspect:切面奕谭,即Pointcut和Advice。

  • Joint point:連接點痴荐,是程序執(zhí)行的一個點。例如官册,一個方法的執(zhí)行或者一個異常的處理生兆。在 Spring AOP 中,一個連接點總是代表一個方法執(zhí)行膝宁。

  • Weaving:織入鸦难,就是通過動態(tài)代理,在目標(biāo)對象方法中執(zhí)行處理內(nèi)容的過程员淫。

參考:
https://blog.csdn.net/weixin_44830331/article/details/119169791

https://www.codeleading.com/article/718121000/

https://www.cnblogs.com/satire/p/14874827.html

https://blog.csdn.net/weixin_46228112/article/details/123930413

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末合蔽,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子介返,更是在濱河造成了極大的恐慌拴事,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件圣蝎,死亡現(xiàn)場離奇詭異刃宵,居然都是意外死亡,警方通過查閱死者的電腦和手機徘公,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進店門牲证,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人关面,你說我怎么就攤上這事坦袍。” “怎么了等太?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵捂齐,是天一觀的道長。 經(jīng)常有香客問我缩抡,道長辛燥,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任缝其,我火速辦了婚禮挎塌,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘内边。我一直安慰自己榴都,他們只是感情好漠其,可當(dāng)我...
    茶點故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布春瞬。 她就那樣靜靜地躺著,像睡著了一般萄涯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天,我揣著相機與錄音,去河邊找鬼片效。 笑死召调,一個胖子當(dāng)著我的面吹牛只嚣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播盛杰,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼逗嫡!你這毒婦竟也來了性穿?” 一聲冷哼從身側(cè)響起吗坚,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤呆万,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年旁理,在試婚紗的時候發(fā)現(xiàn)自己被綠了芋哭。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,625評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出句各,到底是詐尸還是另有隱情吸占,我是刑警寧澤,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布凿宾,位于F島的核電站矾屯,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏初厚。R本人自食惡果不足惜件蚕,卻給世界環(huán)境...
    茶點故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望产禾。 院中可真熱鬧骤坐,春花似錦、人聲如沸下愈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽势似。三九已至拌夏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間履因,已是汗流浹背障簿。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留栅迄,地道東北人站故。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親西篓。 傳聞我的和親對象是個殘疾皇子愈腾,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,492評論 2 348

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