Spring AOP 源碼分析

面向方面編程(AOP)補(bǔ)充了面向?qū)ο缶幊?OOP)璧南,提供了另一種思考程序結(jié)構(gòu)的方式存筏。OOP中模塊化的關(guān)鍵單元是類,而AOP中模塊化的單元是方面浴讯。 方面可以實(shí)現(xiàn)關(guān)注點(diǎn)的模塊化,比如跨越多個(gè)類型和對象的事務(wù)管理蔼啦。(在AOP文獻(xiàn)中榆纽,這種關(guān)注點(diǎn)通常被稱為橫切關(guān)注點(diǎn)。)

簡述Spring AOP實(shí)現(xiàn)

在之前BeanFactory及ApplicationContext的源碼分析中都有提到過,Spring AOP的實(shí)現(xiàn)是通過實(shí)現(xiàn)BeanPostProcessor接口奈籽,在前置或者后置方法中饥侵,通過返回代理對象,完成AOP的實(shí)現(xiàn)衣屏。這里實(shí)現(xiàn)一個(gè)簡單的BeanPostProcessor:

public class LogInterceptor implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof UserService) {
            UserService us = (UserService) bean;
            Object proxyInstance = Proxy.newProxyInstance(LogInterceptor.class.getClassLoader(), new Class[]{UserService.class},
                    (proxy, method, args) -> {
                        System.out.println("LogInterceptor UserService: " + method.getName() + " invoked");
                        return method.invoke(us, args);
                    });

            return proxyInstance;
        }
        return bean;
    }
}

LogInterceptor是一個(gè)簡單的日志攔截器躏升,在bean構(gòu)建過程中,init之后狼忱,就會遍歷所有的BeanPostProcessor的postProcessAfterInitialization方法煮甥,當(dāng)檢測到當(dāng)前bean是UserService實(shí)例時(shí),就會返回一個(gè)代理對象藕赞,代理對象在原對象每個(gè)方法調(diào)用時(shí)都會打印一行日志成肘,這樣一個(gè)簡單的日志攔截器就完成了。

運(yùn)行程序

UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService.getName());
System.out.println(userService.getMoney());
System.out.println(userService.getIntegral());

得到輸出

LogInterceptor UserService: getName invoked
Hello World
LogInterceptor UserService: getMoney invoked
999
LogInterceptor UserService: getIntegral invoked
998

可以看到斧蜕,只要實(shí)現(xiàn)BeanPostProcessor双霍,在實(shí)現(xiàn)接口方法類完成對想要增強(qiáng)bean攔截,然后完成需要的增強(qiáng)處理批销,就可以了洒闸。當(dāng)然上例只是簡單的接口代理,對于有些沒有接口的bean均芽,就需要CGlib完成代理了丘逸。

Spring AOP實(shí)現(xiàn)

當(dāng)然如果每次增強(qiáng)操作都要這樣手動處理就太麻煩了,還要考慮沒有接口的類掀宋,所以Spring內(nèi)部實(shí)現(xiàn)了AOP以簡化開發(fā)深纲。

Spring實(shí)現(xiàn)AOP的方式有兩種,一種為ProxyFactoryBean劲妙,對單個(gè)bean做代理湃鹊;另一種為BeanPostProcessor,根據(jù)規(guī)則镣奋,對一類bean做代理币呵。

ProxyFactoryBean

ProxyFactoryBean是用于創(chuàng)建代理類的通用FactoryBean,通過配置侨颈,可以在構(gòu)建bean的時(shí)候返回代理對象余赢,而代理對象通過對 target 對象進(jìn)行增強(qiáng),這樣在執(zhí)行bean方法的時(shí)候就可以達(dá)到想要的增強(qiáng)后的效果哈垢。示例

spring-aop.xml 文件

<!--攔截器-->
<bean id="debugInterceptor" class="com.zero.demo.advice.DebugInterceptor"/>
<!--target-->
<bean id="mockTask" class="com.zero.demo.aop.MockTask"/>
<bean id="task" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="proxyInterfaces" value="com.zero.demo.aop.ITask"/>
    <property name="target" ref="mockTask"/>
    <property name="interceptorNames">
        <list>
            <value>debugInterceptor</value>
        </list>
    </property>
</bean>

main方法

public static void main(String[] args) {
    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    XmlBeanDefinitionReader configReader = new XmlBeanDefinitionReader(beanFactory);
    configReader.loadBeanDefinitions("classpath:spring-aop.xml");
    ITask task = beanFactory.getBean("task", ITask.class);
    task.execute();
}

可以通過調(diào)試進(jìn)入beanFactory#getBean方法妻柒,查看bean的構(gòu)建,在AbstractBeanFactory#doGetBean方法內(nèi)

if (mbd.isSingleton()) {
  // 構(gòu)建bean温赔,實(shí)際構(gòu)建的是ProxyFactoryBean bean
   sharedInstance = getSingleton(beanName, () -> {
      try {
         return createBean(beanName, mbd, args);
      }
      catch (BeansException ex) {
         destroySingleton(beanName);
         throw ex;
      }
   });
  // 根據(jù)是否是FactoryBean蛤奢,構(gòu)建proxy對象
   bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

對于FactoryBean鬼癣,最后容器中存放的是FactoryBean#getObject方法獲得的對象,在getObjectForBeanInstance方法內(nèi)會根據(jù) sharedInstance 是否為FactoryBean啤贩,然后決定是否調(diào)用getObject方法獲取代理的對象待秃。

protected Object getObjectForBeanInstance(
      Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

   if (BeanFactoryUtils.isFactoryDereference(name)) {
      if (beanInstance instanceof NullBean) {
         return beanInstance;
      }
      if (!(beanInstance instanceof FactoryBean)) {
         throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
      }
   }
   if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
      return beanInstance;
   }

  // 到這里說明一定是一個(gè)FactoryBean
   Object object = null;
   if (mbd == null) {
      object = getCachedObjectForFactoryBean(beanName);
   }
   if (object == null) {
      // 從工廠返回bean實(shí)例。
      FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
      // Caches object obtained from FactoryBean if it is a singleton.
      if (mbd == null && containsBeanDefinition(beanName)) {
         mbd = getMergedLocalBeanDefinition(beanName);
      }
      boolean synthetic = (mbd != null && mbd.isSynthetic());
      object = getObjectFromFactoryBean(factory, beanName, !synthetic);
   }
   return object;
}

getObjectFromFactoryBean方法主要是創(chuàng)建bean以及創(chuàng)建之后的處理痹屹,創(chuàng)建bean的方法為doGetObjectFromFactoryBean章郁,這是從給定的FactoryBean獲取要公開的對象的方法,核心邏輯就是factory.getObject()志衍,也就是ProxyFactoryBeangetObject方法

public Object getObject() throws BeansException {
   // 創(chuàng)建advisor(interceptor)鏈暖庄。根據(jù)interceptorNames,從BeanFactory取出相應(yīng)的advisor(interceptor)
   initializeAdvisorChain();
   if (isSingleton()) {
      return getSingletonInstance();
   }
   else {
      if (this.targetName == null) {
         logger.warn(...);
      }
      return newPrototypeInstance();
   }
}

先創(chuàng)建advisor(interceptor)鏈楼肪。根據(jù)interceptorNames培廓,從BeanFactory取出相應(yīng)的advisor(interceptor),然后getSingletonInstance方法主要是調(diào)用createAopProxy方法創(chuàng)建AopProxy春叫,然后調(diào)用返回的aopProxy的getProxy方法肩钠,獲取最后的proxy對象。createAopProxy方法的調(diào)用DefaultAopProxyFactory#createAopProxy方法暂殖,返回AopProxy對象

//DefaultAopProxyFactory#createAopProxy
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
  // 判斷使用ObjenesisCglibAopProxy還是JdkDynamicAopProxy
   if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
      Class<?> targetClass = config.getTargetClass();
      if (targetClass == null) {
         throw new AopConfigException(...);
      }
      if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
         return new JdkDynamicAopProxy(config);
      }
      return new ObjenesisCglibAopProxy(config);
   }
   else {
      return new JdkDynamicAopProxy(config);
   }
}

根據(jù)條件判斷是返回ObjenesisCglibAopProxy還是JdkDynamicAopProxy价匠,

// JdkDynamicAopProxy
public Object getProxy(@Nullable ClassLoader classLoader) {
   ... log ..
   Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
   findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
   return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

newProxyInstance方法InvocationHandler參數(shù)傳的是this,也就是說呛每,方法調(diào)用會調(diào)用自身的invoke方法

// JdkDynamicAopProxy#invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   MethodInvocation invocation;
   Object oldProxy = null;
   boolean setProxyContext = false;

   TargetSource targetSource = this.advised.targetSource;
   Object target = null;

   try {
      ... equal踩窖、hashCode 等等

      Object retVal;

      if (this.advised.exposeProxy) {
         // Make invocation available if necessary.
         oldProxy = AopContext.setCurrentProxy(proxy);
         setProxyContext = true;
      }
     
      target = targetSource.getTarget();
      Class<?> targetClass = (target != null ? target.getClass() : null);

      // 獲取此方法的攔截鏈。之前初始化過
      List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

      if (chain.isEmpty()) {
         Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
         retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
      }
      else {
         // 創(chuàng)建一個(gè)方法調(diào)用
         invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
         retVal = invocation.proceed();
      }

      // Massage return value if necessary.
      Class<?> returnType = method.getReturnType();
      if (retVal != null && retVal == target &&
            returnType != Object.class && returnType.isInstance(proxy) &&
            !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
         retVal = proxy;
      }
      else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
         throw new AopInvocationException(...);
      }
      return retVal;
   }
   finally {
      if (target != null && !targetSource.isStatic()) {
         // Must have come from TargetSource.
         targetSource.releaseTarget(target);
      }
      if (setProxyContext) {
         // Restore old proxy.
         AopContext.setCurrentProxy(oldProxy);
      }
   }
}

當(dāng)攔截鏈不為空晨横,則創(chuàng)建一個(gè)ReflectiveMethodInvocation洋腮,調(diào)用其proceed方法,proceed方法的調(diào)用會遞歸調(diào)用颓遏,直到所有的MethodInterceptor調(diào)用完

public Object proceed() throws Throwable {
   // We start with an index of -1 and increment early.
   if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
      return invokeJoinpoint();
   }

  // currentInterceptorIndex索引遞增徐矩,每次調(diào)用將獲取下一個(gè)MethodInterceptor
   Object interceptorOrInterceptionAdvice =
         this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
  // 動態(tài)方法匹配
   if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
            InterceptorAndDynamicMethodMatcher dm =
            (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
      if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
         return dm.interceptor.invoke(this);
      }
      else {
        // 遞歸調(diào)用
         return proceed();
      }
   }
   else {
     // methodInterceptor傳參為this,只要methodInterceptor內(nèi)調(diào)用proceed方法叁幢,將遞歸調(diào)用
      return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
   }
}

這樣Spring AOP攔截鏈就調(diào)用完畢。

BeanPostProcessor

Spring更為常見的AOP使用是BeanPostProcessor坪稽,使用BeanPostProcessor可以通過名稱或者正則表達(dá)式曼玩,更加便捷的對一類bean進(jìn)行AOP操作。下面是簡單xml配置

<bean id="debugInterceptor" class="com.zero.demo.advice.DebugInterceptor"/>
<bean id="logInterceptor" class="com.zero.demo.advice.LogInterceptor"/>
<bean id="task" class="com.zero.demo.aop.MockTask"/>

<bean id="processor" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
    <property name="beanNames" value="task,*Service,*Dao"/>
    <property name="interceptorNames">
        <list>
            <value>debugInterceptor</value>
            <value>logInterceptor</value>
        </list>
    </property>
</bean>

示例是使用BeanNameAutoProxyCreator窒百,根據(jù)名稱自動匹配黍判,名稱可以用通配符。main函數(shù)如下

public static void main(String[] args) {
    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    XmlBeanDefinitionReader configReader = new XmlBeanDefinitionReader(beanFactory);
    configReader.loadBeanDefinitions("classpath:spring-aop.xml");
    // 因?yàn)槭褂玫氖莃eanFactory篙梢,所以需要手動注冊BeanPostProcessor顷帖,ApplicationContex會自動注冊
    BeanPostProcessor processor = beanFactory.getBean("processor", BeanPostProcessor.class);
    beanFactory.addBeanPostProcessor(processor);

    ITask task = beanFactory.getBean("mockTask", ITask.class);
    task.execute();
}

這里使用的BeanPostProcessor是BeanNameAutoProxyCreatorBeanNameAutoProxyCreator實(shí)現(xiàn)了SmartInstantiationAwareBeanPostProcessor接口

SmartInstantiationAwareBeanPostProcessor的繼承關(guān)系及方法

image

SmartInstantiationAwareBeanPostProcessor及繼承的接口都會影響bean的構(gòu)建,所以實(shí)際上只要看看BeanNameAutoProxyCreatorSmartInstantiationAwareBeanPostProcessor接口的實(shí)現(xiàn)即可贬墩。

其中榴嗅,直接可以返回代理bean的方法有

  1. postProcessBeforeInstantiation,在bean構(gòu)建開始之前攔截
  2. postProcessBeforeInitialization陶舞,在bean init之前攔截
  3. postProcessAfterInitialization嗽测,在bean init之后攔截

其他的方法主要是bean的類型,構(gòu)造函數(shù)以及屬性注入等肿孵。

postProcessBeforeInstantiation方法

public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
   Object cacheKey = getCacheKey(beanClass, beanName);

   if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
      if (this.advisedBeans.containsKey(cacheKey)) {
         return null;
      }
      if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
         this.advisedBeans.put(cacheKey, Boolean.FALSE);
         return null;
      }
   }

  // 如果我們有一個(gè)定制的TargetSource唠粥,在這里創(chuàng)建代理。
   TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
   if (targetSource != null) {
      if (StringUtils.hasLength(beanName)) {
         this.targetSourcedBeans.add(beanName);
      }
      Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
     // 創(chuàng)建proxy
      Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
      this.proxyTypes.put(cacheKey, proxy.getClass());
      return proxy;
   }

   return null;
}

也就是xml文件中配置了customTargetSourceCreators屬性停做,則會在bean的構(gòu)建之前攔截晤愧。createProxy是核心創(chuàng)建代理的方法,后面詳細(xì)分析蛉腌。

postProcessBeforeInitialization方法直接返回傳入的bean官份,無其他邏輯

postProcessAfterInitialization方法

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
   if (bean != null) {
      Object cacheKey = getCacheKey(bean.getClass(), beanName);
      if (!this.earlyProxyReferences.contains(cacheKey)) {
         return wrapIfNecessary(bean, beanName, cacheKey);
      }
   }
   return bean;
}

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
  if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
    return bean;
  }
  if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
    return bean;
  }
  if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
  }

  // 如果有advice,創(chuàng)建代理眉抬。
  Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
  if (specificInterceptors != DO_NOT_PROXY) {
    this.advisedBeans.put(cacheKey, Boolean.TRUE);
    Object proxy = createProxy(
      bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
    this.proxyTypes.put(cacheKey, proxy.getClass());
    return proxy;
  }

  this.advisedBeans.put(cacheKey, Boolean.FALSE);
  return bean;
}

postProcessAfterInitialization方法如果有advice贯吓,則創(chuàng)建代理,創(chuàng)建代理的邏輯和postProcessBeforeInstantiation方法類似蜀变,下面重點(diǎn)看一下createProxy方法

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
      @Nullable Object[] specificInterceptors, TargetSource targetSource) {

   if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
      AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
   }

   ProxyFactory proxyFactory = new ProxyFactory();
   proxyFactory.copyFrom(this);

   if (!proxyFactory.isProxyTargetClass()) {
      if (shouldProxyTargetClass(beanClass, beanName)) {
         proxyFactory.setProxyTargetClass(true);
      }
      else {
         evaluateProxyInterfaces(beanClass, proxyFactory);
      }
   }

   Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
   proxyFactory.addAdvisors(advisors);
   proxyFactory.setTargetSource(targetSource);
   customizeProxyFactory(proxyFactory);

   proxyFactory.setFrozen(this.freezeProxy);
   if (advisorsPreFiltered()) {
      proxyFactory.setPreFiltered(true);
   }

   return proxyFactory.getProxy(getProxyClassLoader());
}

可以看到核心邏輯有3處

  1. 創(chuàng)建ProxyFactory:ProxyFactory proxyFactory = new ProxyFactory();
  2. 獲得advisors:Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
  3. 構(gòu)建代理:return proxyFactory.getProxy(getProxyClassLoader());

ProxyFactorygetProxy方法其實(shí)和前面介紹的ProxyFactoryBean類似悄谐,ProxyFactoryProxyFactoryBean共同繼承ProxyCreatorSupport,實(shí)現(xiàn)代理的邏輯都來自于超類ProxyCreatorSupport库北。

Spring 事務(wù)

稍帶提一句爬舰,Spring 事物的攔截處理主要由TransactionInterceptor實(shí)現(xiàn)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末寒瓦,一起剝皮案震驚了整個(gè)濱河市情屹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌杂腰,老刑警劉巖垃你,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異喂很,居然都是意外死亡惜颇,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門少辣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來凌摄,“玉大人,你說我怎么就攤上這事漓帅∠强鳎” “怎么了痴怨?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長器予。 經(jīng)常有香客問我浪藻,道長,這世上最難降的妖魔是什么劣摇? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任珠移,我火速辦了婚禮,結(jié)果婚禮上末融,老公的妹妹穿的比我還像新娘钧惧。我一直安慰自己,他們只是感情好勾习,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布充岛。 她就那樣靜靜地躺著蹄咖,像睡著了一般丹鸿。 火紅的嫁衣襯著肌膚如雪壕翩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天艺栈,我揣著相機(jī)與錄音英岭,去河邊找鬼。 笑死湿右,一個(gè)胖子當(dāng)著我的面吹牛诅妹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播毅人,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼吭狡,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了丈莺?” 一聲冷哼從身側(cè)響起划煮,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎缔俄,沒想到半個(gè)月后弛秋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡俐载,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了贼急。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片空闲。...
    茶點(diǎn)故事閱讀 38,064評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖跌榔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情担平,我是刑警寧澤暂论,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響循榆,放射性物質(zhì)發(fā)生泄漏秧饮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望逆巍。 院中可真熱鬧锐极,春花似錦灵再、人聲如沸翎迁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽戚嗅。三九已至懦胞,卻和暖如春躏尉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背教藻。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留哮肚,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓涣楷,卻偏偏與公主長得像绽乔,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子睦授,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評論 2 345

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