Spring中AOP源碼深入解析

有關AOP相關概念以及Spring AOP相關概念和Spring AOP的使用不再重復昌跌。關于AOP在Spring中的地位仰禀,不用說相信我們都知道,也都會用蚕愤,但是對于更深入的東西答恶,還未接觸過,這里就對Spring AOP的相關源碼進行說明一下萍诱,看看到底Spring中AOP是怎么實現(xiàn)的悬嗓。

有關AOP的概念和Spring AOP相關配置,請參考其他兩篇文章:AOP概念砂沛,原理烫扼,應用介紹Spring中AOP的配置從1.0到5.0的演進

另外,本文使用的源碼是Spring1.1.1版本的碍庵,之所以使用這么老的版本映企,是覺得相對來說簡單一些,并且無關的東西更少静浴,這樣更容易去理解堰氓。對于后續(xù)版本新增功能可以在此基礎上進行對比,理解的效果會更好苹享。

示例程序

首先我們還是先使用一個實例來看一下怎么使用双絮,再從實例中一步一步跟進到源碼中浴麻。

先定義業(yè)務接口和實現(xiàn):

LoginService:

package me.cxis.spring.aop;

/**
 * Created by cheng.xi on 2017-03-29 12:02.
 */
public interface LoginService {
    String login(String userName);
}

LoginServiceImpl:

package me.cxis.spring.aop;

/**
 * Created by cheng.xi on 2017-03-29 10:36.
 */
public class LoginServiceImpl implements LoginService {

    public String login(String userName){
        System.out.println("正在登錄");
        return "success";
    }
}

接著是三個通知類:

//這里只是在登錄方法調用之前打印一句話
public class LogBeforeLogin implements MethodBeforeAdvice {
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("有人要登錄了。囤攀。软免。");
    }
}

package me.cxis.spring.aop.proxyfactory;

import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

/**
 * Created by cheng.xi on 2017-03-29 10:56.
 */
public class LogAfterLogin implements AfterReturningAdvice {

    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("有人已經登錄了。焚挠。膏萧。");
    }
}

package me.cxis.spring.aop.proxyfactory;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

/**
 * Created by cheng.xi on 2017-03-30 23:36.
 */
public class LogAroundLogin implements MethodInterceptor {
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("有人要登錄。蝌衔。榛泛。");
        Object result = invocation.proceed();
        System.out.println("登錄完了");
        return result;
    }
}

測試方法:

package me.cxis.spring.aop.proxyfactory;

import org.springframework.aop.framework.ProxyFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Created by cheng.xi on 2017-03-29 10:34.
 */
public class Main {


    public static void main(String[] args) {
        ProxyFactory proxyFactory = new ProxyFactory();//創(chuàng)建代理工廠
        proxyFactory.setTarget(new LoginServiceImpl());//設置目標對象
        proxyFactory.addAdvice(new LogBeforeLogin());//前置增強
        proxyFactory.addAdvice(new LogAfterLogin());//后置增強
        //proxyFactory.addAdvice(new LogAroundLogin());//環(huán)繞增強

        LoginService loginService = (LoginService) proxyFactory.getProxy();//從代理工廠中獲取代理
        loginService.login("x");
    }
}

關于實例中要說明的:我們看到在使用的時候,直接獲取的是一個代理噩斟,不是要使用的實現(xiàn)類曹锨,這也很好懂,之前文章都說過AOP其實就是代理模式剃允,在編譯期或者運行期沛简,給我們原來的代碼增加一些功能,變成一個代理硅急。當我們調用的時候覆享,實際就是調用的代理類。

源碼解析

對于源碼的解析营袜,我們這里使用的是代碼的方式撒顿,沒有選擇xml配置文件的方式。關于xml配置的方式荚板,后面再講解凤壁。

創(chuàng)建AOP代理

首先我們要明白,Spring中實現(xiàn)AOP跪另,就是生成一個代理拧抖,然后在使用的時候調用代理。

創(chuàng)建代理工廠

代碼中首先創(chuàng)建一個代理工廠實例ProxyFactory proxyFactory = new ProxyFactory();代理工廠的作用就是使用編程的方式創(chuàng)建AOP代理免绿。ProxyFactory繼承自AdvisedSupport唧席,AdvicedSupport是AOP代理的配置管理器。

設置目標對象

然后是設置要代理的目標對象proxyFactory.setTarget(new LoginServiceImpl());嘲驾,看下setTarget方法:

public void setTarget(Object target) {
    //先根據給定的目標實現(xiàn)類淌哟,創(chuàng)建一個單例的TargetSource
    //然后設置TargetSource
    setTargetSource(new SingletonTargetSource(target));
}

TargetSource

TargetSource用來獲取當前的Target,也就是TargetSource中會保存著我們的的實現(xiàn)類辽故。

public interface TargetSource {

    //返回目標類的類型
    Class getTargetClass();
    
    //查看TargetSource是否是static的
    //靜態(tài)的TargetSource每次都返回同一個Target
    boolean isStatic();
    
    //獲取目標類的實例
    Object getTarget() throws Exception;
    
    //釋放目標類
    void releaseTarget(Object target) throws Exception;

}

SingletonTargetSource

TargetSource的默認實現(xiàn)徒仓,是一個單例的TargetSource,isStatic方法直接返回true誊垢。

public final class SingletonTargetSource implements TargetSource, Serializable {

    //用來保存目標類   
    private final Object target;
    //構造方法
    public SingletonTargetSource(Object target) {
        this.target = target;
    }
    //直接返回目標類的類型
    public Class getTargetClass() {
        return target.getClass();
    }
    //返回目標類
    public Object getTarget() {
        return this.target;
    }
    //釋放目標類掉弛,這里啥也沒做
    public void releaseTarget(Object o) {
        // Nothing to do
    }
    //直接返回true
    public boolean isStatic() {
        return true;
    }

    //equals方法
    public boolean equals(Object other) {
        //相等症见,返回true
        if (this == other) {
            return true;
        }
        //不是SingletonTargetSource類型的返回false
        if (!(other instanceof SingletonTargetSource)) {
            return false;
        }
        SingletonTargetSource otherTargetSource = (SingletonTargetSource) other;
        //判斷目標類是否相等
        return ObjectUtils.nullSafeEquals(this.target, otherTargetSource.target);
    }
    
    //toString方法
    public String toString() {
        return "SingletonTargetSource: target=(" + target + ")";
    }
}

上面是有關TargetSource和SingletonTargetSource的說明,接著往下一步就是設置目標類setTargetSource方法殃饿,在AdvisedSupport類中:

public void setTargetSource(TargetSource targetSource) {
    if (isActive() && getOptimize()) {
        throw new AopConfigException("Can't change target with an optimized CGLIB proxy: it has its own target");
    }
    //么有做什么處理谋作,只是將我們構建的TargetSource緩存起來
    this.targetSource = targetSource;
}

添加通知

上面設置了要代理的目標類之后,接著是添加通知壁晒,也就是添加增強類瓷们,proxyFactory.addAdvice()方法是添加增強類的方法。我們在例子中是這么使用的:

proxyFactory.addAdvice(new LogBeforeLogin());//前置增強
proxyFactory.addAdvice(new LogAfterLogin());//后置增強
//proxyFactory.addAdvice(new LogAroundLogin());//環(huán)繞增強

addAdvice方法的參數是一個Advice類型的類秒咐,也就是通知或者叫增強,可以去我們的增強類中查看碘裕,我們都繼承了各種Advice携取,比如MethodBeforeAdviceAfterReturningAdvice帮孔,MethodInterceptor雷滋,這里先講一下有關通知Advice的代碼,然后再繼續(xù)說明addAdvice方法文兢。

Advice接口

Advice不屬于Spring晤斩,是AOP聯(lián)盟定義的接口。Advice接口并沒有定義任何方法姆坚,是一個空的接口澳泵,用來做標記,實現(xiàn)了此接口的的類是一個通知類兼呵。Advice有幾個子接口:

  • BeforeAdvice兔辅,前置增強,意思是在我們的目標類之前調用的增強击喂。這個接口也沒有定義任何方法维苔。
  • AfterReturningAdvice,方法正常返回前的增強懂昂,該增強可以看到方法的返回值介时,但是不能更改返回值,該接口有一個方法afterReturning
  • ThrowsAdvice凌彬,拋出異常時候的增強沸柔,也是一個標志接口,沒有定義任何方法饿序。
  • Interceptor勉失,攔截器,也沒有定義任何方法原探,表示一個通用的攔截器乱凿。不屬于Spring顽素,是AOP聯(lián)盟定義的接口
  • DynamicIntroductionAdvice,動態(tài)引介增強徒蟆,有一個方法implementsInterface胁出。
MethodBeforeAdvice

MethodBeforeAdvice接口,是BeforeAdvice的子接口段审,表示在方法前調用的增強全蝶,方法前置增強不能阻止方法的調用,但是能拋異常來使目標方法不繼續(xù)執(zhí)行寺枉。

public interface MethodBeforeAdvice extends BeforeAdvice {
    
    //在給定的方法調用前抑淫,調用該方法
    //參數method是被代理的方法
    //參數args是被代理方法的參數
    //參數target是方法調用的目標,可能為null
    void before(Method m, Object[] args, Object target) throws Throwable;
}
MethodInterceptor

MethodInterceptor不屬于Spring姥闪,是AOP聯(lián)盟定義的接口始苇,是Interceptor的子接口,我們通常叫做環(huán)繞增強筐喳。

public interface MethodInterceptor extends Interceptor {
    
    //在目標方法調用前后做一些事情
    //返回的是invocation.proceed()方法的返回值
    Object invoke(MethodInvocation invocation) throws Throwable;
}

參數MethodInvocation是一個方法調用的連接點催式,接下來先看看MethodInvocation相關的代碼。

Joinpoint連接點

JointPoint接口避归,是一個通用的運行時連接點荣月,運行時連接點是在一個靜態(tài)連接點發(fā)生的事件。

public interface Joinpoint {

   //開始調用攔截器鏈中的下一個攔截器
   Object proceed() throws Throwable;

   //
   Object getThis();

   //
   AccessibleObject getStaticPart();   

}
Invocation接口

Invocation接口是Joinpoint的子接口梳毙,表示程序的調用哺窄,一個Invocation就是一個連接點,可以被攔截器攔截顿天。

public interface Invocation extends Joinpoint {
   
   //獲取參數
   Object[] getArguments();

}
MethodInvocation接口

MethodInvocation接口是Invocation的子接口堂氯,用來描述一個方法的調用。

public interface MethodInvocation extends Invocation
{

    //獲取被調用的方法
    Method getMethod();

}

另外還有一個ConstructorInvocation接口牌废,也是Invocation的子接口咽白,描述的是構造器的調用。

上面介紹完了Advice的相關定義鸟缕,接著看往代理工廠中添加增強的addAdvice方法晶框,addAdvice方法在AdvisedSupport類中:

public void addAdvice(Advice advice) throws AopConfigException {
    //advisors是Advice列表,是一個LinkedList
    //如果被添加進來的是一個Interceptor懂从,會先被包裝成一個Advice
    //添加之前現(xiàn)獲取advisor的大小授段,當做添加的Advice的位置
    int pos = (this.advisors != null) ? this.advisors.size() : 0;
    //添加Advice
    addAdvice(pos, advice);
}

接著看addAdvice(pos, advice)方法:

public void addAdvice(int pos, Advice advice) throws AopConfigException {
    //只能處理實現(xiàn)了AOP聯(lián)盟的接口的攔截器
    if (advice instanceof Interceptor && !(advice instanceof MethodInterceptor)) {
        throw new AopConfigException(getClass().getName() + " only handles AOP Alliance MethodInterceptors");
    }
    //IntroductionInfo接口類型,表示引介信息
    if (advice instanceof IntroductionInfo) {
        //不需要IntroductionAdvisor
        addAdvisor(pos, new DefaultIntroductionAdvisor(advice, (IntroductionInfo) advice));
    }
    //動態(tài)引介增強的處理
    else if (advice instanceof DynamicIntroductionAdvice) {
        //需要IntroductionAdvisor
        throw new AopConfigException("DynamicIntroductionAdvice may only be added as part of IntroductionAdvisor");
    }
    else {
        //添加增強器番甩,需要先把我們的增強包裝成增強器侵贵,然后添加
        addAdvisor(pos, new DefaultPointcutAdvisor(advice));
    }
}

我們看到添加增強的時候,實際調用添加增強器這個方法缘薛,首先需要把我們的Advice包裝成一個PointCutAdvisor窍育,然后在添加增強器卡睦。這里先了解一下有關PointCutAdvisor的相關信息。

Advisor接口

Advisor漱抓,增強器表锻,它持有一個增強Advice,還持有一個過濾器乞娄,來決定Advice可以用在哪里瞬逊。

public interface Advisor {
    
    //判斷Advice是不是每個實例中都有
    boolean isPerInstance();
    
    //返回持有的Advice
    Advice getAdvice();

}

PointcutAdvisor

是一個持有Pointcut切點的增強器,PointcutAdvisor現(xiàn)在就會持有一個Advice和一個Pointcut仪或。

public interface PointcutAdvisor extends Advisor {

    //獲取Pointcut
    Pointcut getPointcut();

}

Pointcut接口

切入點确镊,定義了哪些連接點需要被織入橫切邏輯∪芷洌可以

public interface Pointcut {
    //類過濾器骚腥,可以知道哪些類需要攔截
    ClassFilter getClassFilter();
    //方法匹配器,可以知道哪些方法需要攔截
    MethodMatcher getMethodMatcher();
    
    // could add getFieldMatcher() without breaking most existing code
    Pointcut TRUE = TruePointcut.INSTANCE; 

}

ClassFilter接口

public interface ClassFilter {
    
    //判斷給定的類是不是要攔截
    boolean matches(Class clazz);

    ClassFilter TRUE = TrueClassFilter.INSTANCE;

}

MethodMatcher接口

public interface MethodMatcher {
    
    / 靜態(tài)方法匹配
    boolean matches(Method m, Class targetClass);
    
    //是否是運行時動態(tài)匹配
    boolean isRuntime();
    
    //運行是動態(tài)匹配
    boolean matches(Method m, Class targetClass, Object[] args);
    
    MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;

}

看完相關的定義之后瓶逃,接著看方法new DefaultPointcutAdvisor(advice),將Advice包裝成一個DefaultPointcutAdvisor廓块。其實就是將advice和默認的Pointcut包裝進DefaultPointcutAdvisor厢绝。

DefaultPointcutAdvisor是Advisor的最常用的一個實現(xiàn),可以使用任意類型的Pointcut和Advice带猴,但是不能使用Introduction昔汉。

構造完成了DefaultPointcutAdvisor只有,接著就是添加增強器方法addAdvisor:

public void addAdvisor(int pos, Advisor advisor) throws AopConfigException {
    //引介增強器處理
    if (advisor instanceof IntroductionAdvisor) {
        addAdvisor(pos, (IntroductionAdvisor) advisor);
    }
    else {
        //其他的增強器處理
        addAdvisorInternal(pos, advisor);
    }
}

首先看下非引介增強器的添加方法addAdvisorInternal:

private void addAdvisorInternal(int pos, Advisor advice) throws AopConfigException {
    if (isFrozen()) {
        throw new AopConfigException("Cannot add advisor: config is frozen");
    }
    //把Advice添加到LinkedList中指定位置
    this.advisors.add(pos, advice);
    //同時更新一下Advisors數組
    updateAdvisorArray();
    //通知監(jiān)聽器
    adviceChanged();
}

然后看下關于引介增強器的添加addAdvisor拴清,我們知道引介就是對目標類增加新的接口靶病,所以引介增強,也就是對接口的處理:

public void addAdvisor(int pos, IntroductionAdvisor advisor) throws AopConfigException {
    //對接口進行校驗
    advisor.validateInterfaces();

    // 遍歷要添加的接口口予,添加
    for (int i = 0; i < advisor.getInterfaces().length; i++) {
        //就是添加到interfaces集合中娄周,interfaces是一個HashSet
        addInterface(advisor.getInterfaces()[i]);
    }
    //然后添加到advisors中
    addAdvisorInternal(pos, advisor);
}

對于添加增強的步驟,就是把我們的增強器添加進代理工廠中沪停,保存在一個LinkedList中煤辨,順序是添加進來的順序。

獲取代理

到目前為止木张,我們看到的都還是在組裝代理工廠众辨,并沒有看到代理的生成,接下來proxyFactory.getProxy()這一步就是獲取代理的過程舷礼,我們繼續(xù)看ProxyFactory的getProxy方法:

public Object getProxy() {
    //創(chuàng)建一個AOP代理
    AopProxy proxy = createAopProxy();
    //返回代理
    return proxy.getProxy();
}

我們知道一般創(chuàng)建代理會有兩種方式鹃彻,一種是JDK動態(tài)代理,另外一種是CGLIB動態(tài)代理妻献,而這里的創(chuàng)建AOP代理就是生成這兩種代理中的一種蛛株。先看createAopProxy()方法团赁,在AdvisedSupport類中:

protected synchronized AopProxy createAopProxy() {
    if (!this.isActive) {
        activate();
    }
    //獲取AOP代理工廠腰鬼,然后創(chuàng)建代理
    return getAopProxyFactory().createAopProxy(this);
}

獲取代理工廠這一步角撞,這里就是默認獲取一個DefaultAopProxyFactory實例诚纸,然后調用createAopProxy創(chuàng)建AOP代理:

public AopProxy createAopProxy(AdvisedSupport advisedSupport) throws AopConfigException {
    //對于指定了使用CGLIB方式孝赫,或者代理的是類臊泌,或者代理的不是接口匠楚,就使用CGLIB的方式來創(chuàng)建代理
    boolean useCglib = advisedSupport.getOptimize() || advisedSupport.getProxyTargetClass() || advisedSupport.getProxiedInterfaces().length == 0;
    if (useCglib) {
        return CglibProxyFactory.createCglibProxy(advisedSupport);
    }
    else {
        //使用JDK動態(tài)代理來創(chuàng)建代理
        return new JdkDynamicAopProxy(advisedSupport);
    }
}

獲取完AOP代理之后返回画机,然后就是調用getProxy方法獲取代理盛卡,這里分為CGLIB的獲取方式和JDK動態(tài)代理的獲取方式兩種矗钟。

JDK動態(tài)代理方式獲取代理

JDK動態(tài)代理方式獲取代理唆香,實現(xiàn)在JdkDynamicAopProxy中:

public Object getProxy() {
    return getProxy(Thread.currentThread().getContextClassLoader());
}
public Object getProxy(ClassLoader cl) {
    //JDK動態(tài)代理只能代理接口類型,先獲取接口
    //就是從AdvisedSupport中獲取保存在interfaces中的接口
    Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advisedSupport);
    //使用Java的反射機制創(chuàng)建一個代理實例
    return Proxy.newProxyInstance(cl, proxiedInterfaces, this);
}

關于JDK反射創(chuàng)建代理之類的吨艇,這里不做解析躬它。

CGLIB方式獲取代理

CGLIB獲取方式,實現(xiàn)在Cglib2AopProxy中:

public Object getProxy() {
    //使用CGLIB的方式來獲取东涡,CGLIB這里不做解析
    return getProxy(Thread.currentThread().getContextClassLoader());
}

使用代理

上面獲取代理之后冯吓,就剩最后一步,使用疮跑,當我們調用業(yè)務方法的時候组贺,實際上是調用代理中的方法,對于CGLIB生成的代理祖娘,調用的是DynamicAdvisedInterceptor的intercept方法失尖;JDK動態(tài)代理生成的代理是調用invoke方法。

JDK動態(tài)代理

看下JDK動態(tài)代理的方式渐苏,對于方法的調用掀潮,實際上調用的是代理類的invoke方法,在JdkDynamicAopProxy中:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    MethodInvocation invocation = null;
    Object oldProxy = null;
    boolean setProxyContext = false;
    //代理的目標對象
    TargetSource targetSource = advisedSupport.targetSource;
    Class targetClass = null;
    Object target = null;       

    try {
        //equals方法
        if (method.getDeclaringClass() == Object.class && "equals".equals(method.getName())) {
            return equals(args[0]) ? Boolean.TRUE : Boolean.FALSE;
        }
        else if (Advised.class == method.getDeclaringClass()) {
            //琼富?仪吧??
            return AopProxyUtils.invokeJoinpointUsingReflection(this.advisedSupport, method, args);
        }

        Object retVal = null;

        //代理目標對象
        target = targetSource.getTarget();
        if (target != null) {
            targetClass = target.getClass();
        }
        //公黑?邑商??
        if (this.advisedSupport.exposeProxy) {
            // Make invocation available if necessary
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }

        //獲取配置的通知Advicelian
        List chain = this.advisedSupport.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
                this.advisedSupport, proxy, method, targetClass);

        //沒有配置通知
        if (chain.isEmpty()) {
            //直接調用目標對象的方法
            retVal = AopProxyUtils.invokeJoinpointUsingReflection(target, method, args);
        }
        else {
            //配置了通知凡蚜,創(chuàng)建一個MethodInvocation
            invocation = new ReflectiveMethodInvocation(proxy, target,
                                method, args, targetClass, chain);

            //執(zhí)行通知鏈人断,沿著通知器鏈調用所有的通知
            retVal = invocation.proceed();
        }

        //返回值
        if (retVal != null && retVal == target) {
            //返回值為自己
            retVal = proxy;
        }
        //返回
        return retVal;
    }
    finally {
        if (target != null && !targetSource.isStatic()) {
            targetSource.releaseTarget(target);
        }

        if (setProxyContext) {
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}

CGLIB動態(tài)代理

CGLIB的是調用DynamicAdvisedInterceptor的intercept方法對目標對象進行處理,具體暫先不解析朝蜘。

使用ProxyFactoryBean創(chuàng)建AOP代理

ProxyFactoryBean對Pointcut和Advice提供了完全的控制恶迈,還包括應用的順序。ProxyFactoryBean的getObject方法會返回一個AOP代理,包裝了目標對象暇仲。

Spring在初始化的過程中步做,createBean的時候,如果是FactoryBean的話奈附,會調用((BeanFactoryAware)bean).setBeanFactory(this);

public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
    this.beanFactory = beanFactory;
    //創(chuàng)建通知器鏈
    this.createAdvisorChain();
    if(this.singleton) {
        //刷新目標對象
        this.targetSource = this.freshTargetSource();
        //獲取單例實例
        this.getSingletonInstance();
        this.addListener(this);
    }

}

看下獲取單例實例的方法:

private Object getSingletonInstance() {
    if(this.singletonInstance == null) {
        this.singletonInstance = this.createAopProxy().getProxy();
    }

    return this.singletonInstance;
}

createAopProxy方法在AdvisedSupport類中全度,下面創(chuàng)建的流程跟上面解析的都一樣了。

到這里AOP的一個流程的源碼算是走完了斥滤,這只是其中一小部分将鸵,還有很多的沒有涉及到,包括AOP標簽的解析佑颇,CGLIB生成代理以及調用代理等等顶掉。其中有些還沒明白的已經畫上了問號,慢慢的在研究下挑胸。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末痒筒,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子茬贵,更是在濱河造成了極大的恐慌簿透,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件解藻,死亡現(xiàn)場離奇詭異萎战,居然都是意外死亡,警方通過查閱死者的電腦和手機舆逃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來戳粒,“玉大人路狮,你說我怎么就攤上這事∥翟迹” “怎么了奄妨?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長苹祟。 經常有香客問我砸抛,道長,這世上最難降的妖魔是什么树枫? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任直焙,我火速辦了婚禮,結果婚禮上砂轻,老公的妹妹穿的比我還像新娘奔誓。我一直安慰自己,他們只是感情好搔涝,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布厨喂。 她就那樣靜靜地躺著和措,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蜕煌。 梳的紋絲不亂的頭發(fā)上派阱,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機與錄音斜纪,去河邊找鬼贫母。 笑死,一個胖子當著我的面吹牛傀广,可吹牛的內容都是我干的颁独。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼伪冰,長吁一口氣:“原來是場噩夢啊……” “哼誓酒!你這毒婦竟也來了?” 一聲冷哼從身側響起贮聂,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤靠柑,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后吓懈,有當地人在樹林里發(fā)現(xiàn)了一具尸體歼冰,經...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年耻警,在試婚紗的時候發(fā)現(xiàn)自己被綠了隔嫡。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡甘穿,死狀恐怖腮恩,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情温兼,我是刑警寧澤秸滴,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站募判,受9級特大地震影響荡含,放射性物質發(fā)生泄漏。R本人自食惡果不足惜届垫,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一释液、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧敦腔,春花似錦均澳、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽糟袁。三九已至,卻和暖如春躺盛,著一層夾襖步出監(jiān)牢的瞬間项戴,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工槽惫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留周叮,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓界斜,卻偏偏與公主長得像仿耽,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子各薇,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內容