Spring AOP(六)细究 JdkDynamicAopProxy 类

本文来分析 Spring 中 JdkDynamicAopProxy 代理对象的生成 getProxy() 方法和拦截增强 invoke 方法的相关处理逻辑。

上文我们研究过了 PointcutAdviceAdvisor)接口以及它们在 Spring AOP 代理中的作用,并利用几个 Demo 演示了其相关关系。

为了方便阅读,把上文中的 ProxyFactoryDemo 类代码再次贴出来。

public class ProxyFactoryDemo {

    public static void main(String[] args) {
        // 1. 构造目标对象
        Cat catTarget = new Cat();

        // 2. 通过目标对象,构造 ProxyFactory 对象
        ProxyFactory factory = new ProxyFactory(catTarget);

        // 添加一个方法拦截器
        factory.addAdvice(new MyMethodInterceptor());

        // 3. 根据目标对象生成代理对象
        Object proxy = factory.getProxy();

        Animal cat = (Animal) proxy;
        cat.eat();
    }
}

源码分析

首先,我们从 ProxyFactory 类的 getProxy() 方法开始分析。

public Object getProxy() {
  return createAopProxy().getProxy();
}

createAopProxy()ProxyFactory 父类 ProxyCreatorSupport 中的方法的主要是根据当前的 config 信息判断是生成 JDK 动态代理还是 CGLIB 动态代理

protected final synchronized AopProxy createAopProxy() {
  if (!this.active) {
    activate();
  }
  // 1. 获取 AopProxyFactory 工厂,用来创建 AopProxy 对象
  // 注意 createAopProxy 的参数 this,this 就是在 ProxyFactoryDemo 中定义的 ProxyFactory 对象。
  return getAopProxyFactory().createAopProxy(this);
}

/*
 *  子类构造函数中如果不显示调用父类的构造函数,则会自动调用父类的无参构造函数
 *  所以,ProxyCreatorSupport() 方法会在 ProxyFactory factory = new ProxyFactory(catTarget); 调用时被调用
 */
public ProxyCreatorSupport() {
  this.aopProxyFactory = new DefaultAopProxyFactory();
}

public AopProxyFactory getAopProxyFactory() {
  // DefaultAopProxyFactory 对象
  return this.aopProxyFactory;
}

下面我们继续来看 DefaultAopProxyFactory 类中的 createAopProxy 方法。

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
  // config.isOptimize() 这个属性没有研究过
  // 满足下面三个条件的任意一个
  // 1. 如果 optimize 属性为 ture 
  // 2. 或者 proxyTargetClass 属性为 ture
  // 3. 被代理类没有可代理的接口
  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.");
    }
    // 如果被代理类的类型是接口,或者被代理类是 JDK 动态代理生成的代理类,则使用 JDK 动态代理
    if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
      return new JdkDynamicAopProxy(config);
    }
    
    // 使用 CGLIB 动态代理
    return new ObjenesisCglibAopProxy(config);
  }
  else {
    //  使用 JDK 动态代理
    return new JdkDynamicAopProxy(config);
  }
}

ObjenesisCglibAopProxy 类在下一篇文章讲解,先来看 JdkDynamicAopProxy 类的 getProxy() 方法。

public Object getProxy() {
  return getProxy(ClassUtils.getDefaultClassLoader());
}

public Object getProxy(@Nullable ClassLoader classLoader) {
  if (logger.isTraceEnabled()) {
    logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
  }
  // 得到所有需要代理的接口
  Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
  // 查找被代理的接口中是否包含 equals 和 hashCode 方法,如果有,则在代理类中进行相应的标记,
  // 从而在 invoke 方法中直接调用相关的方法,避免递归调用导致栈溢出
  findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
  
  // 这里就是我们熟悉的 Proxy.newProxyInstance 方法了,用来生成 JDK 动态代理对象。
  return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
  // advised 就是在 ProxyFactoryDemo 中定义的 ProxyFactory 对象。
  this.advised = config;
}

AopProxyUtils 类中的的 completeProxiedInterfaces() 方法需要讲解下。

/**
 * 返回要为给定 AOP 配置代理的完整接口集。
 *   始终添加 SpringProxy 接口
 *   如果 opaque 标记为 false,则始终添加 Advised 接口
 * @param advised 代理配置
 * @param decoratingProxy 为 ture 则添加 DecoratingProxy 接口
 */
static Class<?>[] completeProxiedInterfaces(AdvisedSupport advised, boolean decoratingProxy) {
  // 1. 获取到所有需要被代理的接口
  Class<?>[] specifiedInterfaces = advised.getProxiedInterfaces();
 
  // 如果被代理的接口集合中没有 SpringProxy 接口,则需要添加 SpringProxy 接口到被代理集合集合中
  boolean addSpringProxy = !advised.isInterfaceProxied(SpringProxy.class);
  
  // 如果 opaque 为 false(默认是 false),且被代理的接口集合中没有 Advised 接口,则需要添加 Advised 接口到被代理集合集合中
  boolean addAdvised = !advised.isOpaque() && !advised.isInterfaceProxied(Advised.class);
  
  // decoratingProxy 为 ture,且被代理的接口集合中没有 DecoratingProxy 接口,则需要添加 DecoratingProxy 接口到被代理集合集合中
  boolean addDecoratingProxy = (decoratingProxy && !advised.isInterfaceProxied(DecoratingProxy.class));
  
  // 省略添加逻辑...
  
  return proxiedInterfaces;
}

由此,我们就知道了 JdkDynamicAopProxy 代理类的生成逻辑。

然后我们再来看 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 {
    if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
      // 不代理 equals 方法,通过 JdkDynamicAopProxy 中定义的 equals 方法进行比较
      return equals(args[0]);
    }
    else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
      // 不代理 hashCode 方法,调用 JdkDynamicAopProxy 中定义的 hashCode 方法
      return hashCode();
    }
    else if (method.getDeclaringClass() == DecoratingProxy.class) {
      // DecoratingProxy 接口中只有 getDecoratedClass 方法,通过下面的方法实现其逻辑
      return AopProxyUtils.ultimateTargetClass(this.advised);
    }
    else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
        method.getDeclaringClass().isAssignableFrom(Advised.class)) {
      // opaque 为 false 才可能代理 Advised 接口
      // 如果 method 是在 Advised 中声明的,则直接把 method 转移到 advised 对象上调用。
      // 这样就可以通过代理对象直接操作代理的配置,比如新增 Advisor。
      return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
    }

    Object retVal;

    if (this.advised.exposeProxy) {
      // 如果需要在拦截器暴露 proxy 对象,则把 proxy 对象添加到 ThreadLocal 中
      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()) {
      // 如果拦截器集合为空,说明当前 method 不需要被增强,则通过反射直接调用目标对象上的方法
      Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
      retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
    }
    else {
      // 创建 ReflectiveMethodInvocation,用来管理方法拦截器责任链
      // 类似于 【Spring AOP (四) 多重代理和责任链模式】 中的 MyMethodInvocationImpl
      invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
      // 通过 invocation.proceed 驱动方法拦截器责任链的运行,并获取到返回值。
      retVal = invocation.proceed();
    }

    // 返回 this,需要替换为 proxy 对象
    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(
          "Null return value from advice does not match primitive return type for: " + method);
    }
    return retVal;
  }
  finally {
    if (setProxyContext) {
      // 把第一次调用 setCurrentProxy 返回的对象,重新设置到 ThreadLocal 中
      AopContext.setCurrentProxy(oldProxy);
    }
  }
}

我们接下来分析一下在 invoke 方法中值得关注的地方。

advisedgetInterceptorsAndDynamicInterceptionAdvice 方法,此方法定义在 AdvisedSupport 类中。

AdvisorChainFactory advisorChainFactory = new DefaultAdvisorChainFactory();

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
  // 1. 定义 method 的 cacheKey,用来缓存结果
  MethodCacheKey cacheKey = new MethodCacheKey(method);
  
  // 2. 从缓存中获取 method 的拦截器列表
  List<Object> cached = this.methodCache.get(cacheKey);
  if (cached == null) {
    // 3. 通过 DefaultAdvisorChainFactory的 getInterceptorsAndDynamicInterceptionAdvice 获取 method 的拦截器列表
    cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
        this, method, targetClass);
    // 4. 保存 method 的拦截器列表到缓存中
    this.methodCache.put(cacheKey, cached);
  }
  return cached;
}

getInterceptorsAndDynamicInterceptionAdvice 方法中涉及 DefaultAdvisorChainFactory 类中的相关代码清单如下所示。


// 省略了方法中关于 Introduction 增强的处理逻辑
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
    Advised config, Method method, @Nullable Class<?> targetClass) {

  // // 获取一个全局唯一的 AdvisorAdapterRegistry  对象
  AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
  // 这里的 config,就是最开始定义的 ProxyFactory 对象
  // 获取到所有的 Advisor
  Advisor[] advisors = config.getAdvisors();
  List<Object> interceptorList = new ArrayList<>(advisors.length);
  Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());

  for (Advisor advisor : advisors) {
    // 如果 advisor 是 PointcutAdvisor 类型
    if (advisor instanceof PointcutAdvisor) {
      PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
      // 如果 preFiltered 为 ture,或者目标对象类型满足 ClassFilter 的条件,则进行下面的处理
      if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
        MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
        boolean match;
          // 进行方法的静态匹配
          match = mm.matches(method, actualClass);
        if (match) {
          // 匹配则把 advisor 中的 Advice(MethodInterceptor 是 Advice 的子接口) 添加方法拦截器列表
          MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
          if (mm.isRuntime()) {
            // 如果需要进行方法的动态匹配,则转换为 InterceptorAndDynamicMethodMatcher 类型,在 invoke 方法调用时进行动态匹配
            for (MethodInterceptor interceptor : interceptors) {
              interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
            }
          }
          else {
            interceptorList.addAll(Arrays.asList(interceptors));
          }
        }
      }
    }
    // 不是 PointcutAdvisor 说明没有 Pointcut,则匹配所有方法
    else {
      Interceptor[] interceptors = registry.getInterceptors(advisor);
      interceptorList.addAll(Arrays.asList(interceptors));
    }
  }

  return interceptorList;
}

getInterceptorsAndDynamicInterceptionAdvice 方法中一个容易忽略的地方就是 AdvisorAdapterRegistry 接口和其 getInterceptors 方法。

GlobalAdvisorAdapterRegistry 类的代码清单如下所示。

public final class GlobalAdvisorAdapterRegistry {

    private GlobalAdvisorAdapterRegistry() {
    }


    /**
     * 保存单个实例,以便我们给请求它的类使用。
     */
    private static AdvisorAdapterRegistry instance = new DefaultAdvisorAdapterRegistry();

    public static AdvisorAdapterRegistry getInstance() {
        return instance;
    }

    static void reset() {
        instance = new DefaultAdvisorAdapterRegistry();
    }

}

DefaultAdvisorAdapterRegistry 类的代码清单如下所示。

public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {

    private final List<AdvisorAdapter> adapters = new ArrayList<>(3);

    /**
     * 创建一个新的 DefaultAdvisorAdapterRegistry,注册默认的 AdvisorAdapter。
     */
    public DefaultAdvisorAdapterRegistry() {
        registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
        registerAdvisorAdapter(new AfterReturningAdviceAdapter());
        registerAdvisorAdapter(new ThrowsAdviceAdapter());
    }

    /**
     * 返回给定 advice 的包装类 advisor
     *     默认至少支持如下 Advice:
     *        org.aopalliance.intercept.MethodInterceptor
     *        org.springframework.aop.MethodBeforeAdvice
     *        org.springframework.aop.AfterReturningAdvice
     *        org.springframework.aop.ThrowsAdvice
     */
    @Override
    public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
        // 如果 adviceObject 已经是 Advisor 实例,直接返回
        if (adviceObject instanceof Advisor) {
            return (Advisor) adviceObject;
        }

        // 如果 adviceObject 不是 Advice 实例,直接抛出异常
        if (!(adviceObject instanceof Advice)) {
            throw new UnknownAdviceTypeException(adviceObject);
        }
        Advice advice = (Advice) adviceObject;
        if (advice instanceof MethodInterceptor) {
            // MethodInterceptor 不需要 wrap
            return new DefaultPointcutAdvisor(advice);
        }
        for (AdvisorAdapter adapter : this.adapters) {
            // 检查 adapter 是否理解此 advice 对象? 即使用 Advisor 调用 getInterceptors 方法作为参数是否有效?
            if (adapter.supportsAdvice(advice)) {
                return new DefaultPointcutAdvisor(advice);
            }
        }

        // 如果上面的条件都不满足,直接抛出异常
        throw new UnknownAdviceTypeException(advice);
    }

    @Override
    public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
        List<MethodInterceptor> interceptors = new ArrayList<>(3);
        Advice advice = advisor.getAdvice();

        // 1. 如果 advice 是 MethodInterceptor 实例,直接添加到拦截器列表
        if (advice instanceof MethodInterceptor) {
            interceptors.add((MethodInterceptor) advice);
        }

        // 2. 如果存在 adapter可以把 advisor 转换为 MethodInterceptor,也需要添加到拦截器列表
        for (AdvisorAdapter adapter : this.adapters) {
            if (adapter.supportsAdvice(advice)) {
                interceptors.add(adapter.getInterceptor(advisor));
            }
        }

        // 拦截器列表为空,则抛出异常
        if (interceptors.isEmpty()) {
            throw new UnknownAdviceTypeException(advisor.getAdvice());
        }
        return interceptors.toArray(new MethodInterceptor[0]);
    }

    @Override
    public void registerAdvisorAdapter(AdvisorAdapter adapter) {
        this.adapters.add(adapter);
    }

}

我们继续来看 AdvisorAdapter 的相关实现类中的 getInterceptor 方法。

class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {

    @Override
    public boolean supportsAdvice(Advice advice) {
        return (advice instanceof MethodBeforeAdvice);
    }

    @Override
    public MethodInterceptor getInterceptor(Advisor advisor) {
        MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
        // 返回 MethodInterceptor 接口实现类 MethodBeforeAdviceInterceptor
        return new MethodBeforeAdviceInterceptor(advice);
    }
}

class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {

    @Override
    public boolean supportsAdvice(Advice advice) {
        return (advice instanceof AfterReturningAdvice);
    }

    @Override
    public MethodInterceptor getInterceptor(Advisor advisor) {
        AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();
        // 返回 MethodInterceptor 接口实现类 AfterReturningAdviceInterceptor
        return new AfterReturningAdviceInterceptor(advice);
    }

}

class ThrowsAdviceAdapter implements AdvisorAdapter, Serializable {

    @Override
    public boolean supportsAdvice(Advice advice) {
        return (advice instanceof ThrowsAdvice);
    }

    @Override
    public MethodInterceptor getInterceptor(Advisor advisor) {
        // 返回 MethodInterceptor 接口实现类 ThrowsAdviceInterceptor
        return new ThrowsAdviceInterceptor(advisor.getAdvice());
    }

}

最后,我们需要分析一下 ReflectiveMethodInvocation 类中 proceed 的逻辑,proceed 方法驱动责任链模式的运行。

/**
 * 当前拦截器列表索引,表示当前已经运行到第几个 MethodInterceptor
 */
private int currentInterceptorIndex = -1;

public Object proceed() throws Throwable {
  //  如果拦截器列表已经执行完毕,则调用目标对象的目标方法
  if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
    return invokeJoinpoint();
  }

  // 获取责任链中的下一个 MethodInterceptor,用来对目标方法做增强处理
  Object interceptorOrInterceptionAdvice =
      this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
  if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
    // 如果 interceptorOrInterceptionAdvice 是 InterceptorAndDynamicMethodMatcher 类型,说明需要对目标方法进行动态匹配
    InterceptorAndDynamicMethodMatcher dm =
        (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
    Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
    
    // 方法动态匹配成功,才对目标方法做增强处理
    if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
      return dm.interceptor.invoke(this);
    }
    else {
      // 方法动态匹配失败,则驱动责任链向前运行
      return proceed();
    }
  }
  else {
    // 如果目标方法不需要动态匹配,则直接调用 MethodInterceptor 的 invoke 方法,对目标方法做增强处理。
    return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
  }
}

至此我们可以得知,Spring AOP 代理 是由如下两个核心接口实现的。

  • MethodInterceptor 接口中的 invoke 方法对目标方法做代理(增强)处理。

  • MethodInvocation 接口中的 proceed() 方法通过责任链模式对目标方法做多重代理。在其实现类 ReflectiveMethodInvocation 中实现此逻辑。

Spring 中的 JDK AOP 代理类 JdkDynamicAopProxy 到这里就基本说完了,下一篇我们讲解 CGLIB AOP 代理类 ObjenesisCglibAopProxy 类。

(正文完)

扩展阅读

喜欢本文的朋友们,欢迎关注微信公众号【程序员小课堂】,阅读更多精彩内容!


程序员小课堂.jpg
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,194评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,058评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,780评论 0 346
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,388评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,430评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,764评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,907评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,679评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,122评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,459评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,605评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,270评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,867评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,734评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,961评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,297评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,472评论 2 348

推荐阅读更多精彩内容