SpringAOP源码解析过程

废话不多说,先上干货:spring的AOP是依赖AnnotationAwareAspectJAutoProxyCreator这个类生成的,这个类继承了Aware、BeanPostProcessor、InstantiationAwareBeanPostProcessor,主要是在bean实例化前调用InstantiationAwareBeanPostProcessor的前置方法生成AnnotationAwareAspectJAutoProxyCreator,然后在bean的初始化前调用Aware的setBeanFactory设置工厂对象,最后在bean的初始化完成后调用BeanPostProcessor后置处理器的后置方法完成代理对象的创建,记得这里就可以应付面试了,后面写得烂不用看了
spring的AOP基于JDK的动态代理和cglib实现,默认代理对象是某个接口的实现就会使用JDK动态代理,否则使用cglib,使用cglib时要注意,代理类不能是final类型,否则无法进行代理

AOP示例

 @Aspect
 @Component
 public class SystemLogAspect {
 
     //注入Service用于把日志保存数据库  
     @Resource
     private SystemLogService systemLogService;  
     
     private  static  final Logger logger = LoggerFactory.getLogger(SystemLogAspect. class);  
     
     //Controller层切点  
     @Pointcut("execution (* com.gcx.controller..*.*(..))")  
     public  void controllerAspect() {  
     }  
     
     /** 
      * 前置通知 用于拦截Controller层记录用户的操作 
      * 
      * @param joinPoint 切点 
      */ 
     @Before("controllerAspect()")
     public void doBefore(JoinPoint joinPoint) {
         System.out.println("==========执行controller前置通知===============");
         if(logger.isInfoEnabled()){
             logger.info("before " + joinPoint);
         }
     }    
     
     //配置controller环绕通知,使用在方法aspect()上注册的切入点
       @Around("controllerAspect()")
       public void around(JoinPoint joinPoint){
           System.out.println("==========开始执行controller环绕通知===============");
           long start = System.currentTimeMillis();
           try {
               ((ProceedingJoinPoint) joinPoint).proceed();
               long end = System.currentTimeMillis();
               if(logger.isInfoEnabled()){
                   logger.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms!");
               }
               System.out.println("==========结束执行controller环绕通知===============");
           } catch (Throwable e) {
               long end = System.currentTimeMillis();
               if(logger.isInfoEnabled()){
                   logger.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage());
               }
           }
       }
     
     /** 
      * 后置通知 用于拦截Controller层记录用户的操作 
      * 
      * @param joinPoint 切点 
      */  
     @After("controllerAspect()")  
     public  void after(JoinPoint joinPoint) {  
         User user = new User();
         user.setId(1);
         user.setName("张三");
         String ip = "127.0.0.1";
          try {  
             String targetName = joinPoint.getTarget().getClass().getName();  
             String methodName = joinPoint.getSignature().getName();  
             Object[] arguments = joinPoint.getArgs();  
             Class targetClass = Class.forName(targetName);  
             Method[] methods = targetClass.getMethods();
             String operationType = "";
             String operationName = "";
              for (Method method : methods) {  
                  if (method.getName().equals(methodName)) {  
                     Class[] clazzs = method.getParameterTypes();  
                      if (clazzs.length == arguments.length) {  
                          operationType = method.getAnnotation(Log.class).operationType();
                          operationName = method.getAnnotation(Log.class).operationName();
                          break;  
                     }  
                 }  
             }
             //*========控制台输出=========*//  
             System.out.println("=====controller后置通知开始=====");  
             System.out.println("请求方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")+"."+operationType);  
             System.out.println("方法描述:" + operationName);  
             System.out.println("请求人:" + user.getName());  
             System.out.println("请求IP:" + ip);  
             //*========数据库日志=========*//  
             SystemLog log = new SystemLog();  
             log.setId(UUID.randomUUID().toString());
             log.setDescription(operationName);  
             log.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")+"."+operationType);  
             log.setLogType((long)0);  
             log.setRequestIp(ip);  
             log.setExceptioncode( null);  
             log.setExceptionDetail( null);  
             log.setParams( null);  
             log.setCreateBy(user.getName());  
             log.setCreateDate(new Date());  
             //保存数据库  
             systemLogService.insert(log);  
             System.out.println("=====controller后置通知结束=====");  
         }  catch (Exception e) {  
             //记录本地异常日志  
             logger.error("==后置通知异常==");  
             logger.error("异常信息:{}", e.getMessage());  
         }  
     } 
     
     //配置后置返回通知,使用在方法aspect()上注册的切入点
       @AfterReturning("controllerAspect()")
       public void afterReturn(JoinPoint joinPoint){
           System.out.println("=====执行controller后置返回通知=====");  
               if(logger.isInfoEnabled()){
                   logger.info("afterReturn " + joinPoint);
               }
       }
     
     /** 
      * 异常通知 用于拦截记录异常日志 
      * 
      * @param joinPoint 
      * @param e 
      */  
      @AfterThrowing(pointcut = "controllerAspect()", throwing="e")  
      public  void doAfterThrowing(JoinPoint joinPoint, Throwable e) {  
         //获取用户请求方法的参数并序列化为JSON格式字符串  
         User user = new User();
         user.setId(1);
         user.setName("张三");
         String ip = "127.0.0.1";
         
         String params = "";  
          if (joinPoint.getArgs() !=  null && joinPoint.getArgs().length > 0) {  
              for ( int i = 0; i < joinPoint.getArgs().length; i++) {  
                 params += JsonUtil.getJsonStr(joinPoint.getArgs()[i]) + ";";  
             }  
         }  
          try {  
              
              String targetName = joinPoint.getTarget().getClass().getName();  
              String methodName = joinPoint.getSignature().getName();  
              Object[] arguments = joinPoint.getArgs();  
              Class targetClass = Class.forName(targetName);  
              Method[] methods = targetClass.getMethods();
              String operationType = "";
              String operationName = "";
               for (Method method : methods) {  
                   if (method.getName().equals(methodName)) {  
                      Class[] clazzs = method.getParameterTypes();  
                       if (clazzs.length == arguments.length) {  
                           operationType = method.getAnnotation(Log.class).operationType();
                           operationName = method.getAnnotation(Log.class).operationName();
                           break;  
                      }  
                  }  
              }
              /*========控制台输出=========*/  
             System.out.println("=====异常通知开始=====");  
             System.out.println("异常代码:" + e.getClass().getName());  
             System.out.println("异常信息:" + e.getMessage());  
             System.out.println("异常方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")+"."+operationType);  
             System.out.println("方法描述:" + operationName);  
             System.out.println("请求人:" + user.getName());  
             System.out.println("请求IP:" + ip);  
             System.out.println("请求参数:" + params);  
                /*==========数据库日志=========*/  
             SystemLog log = new SystemLog();
             log.setId(UUID.randomUUID().toString());
             log.setDescription(operationName);  
             log.setExceptioncode(e.getClass().getName());  
             log.setLogType((long)1);  
             log.setExceptionDetail(e.getMessage());  
             log.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));  
             log.setParams(params);  
             log.setCreateBy(user.getName());  
             log.setCreateDate(new Date());  
             log.setRequestIp(ip);  
             //保存数据库  
             systemLogService.insert(log);  
             System.out.println("=====异常通知结束=====");  
         }  catch (Exception ex) {  
             //记录本地异常日志  
             logger.error("==异常通知异常==");  
             logger.error("异常信息:{}", ex.getMessage());  
         }  
          /*==========记录本地异常日志==========*/  
         logger.error("异常方法:{}异常代码:{}异常信息:{}参数:{}", joinPoint.getTarget().getClass().getName() + joinPoint.getSignature().getName(), e.getClass().getName(), e.getMessage(), params);  
     }  
 }

@Aspect就是所谓的切面也叫做Advisor的织入,@Pointcut就是切点

AOP源码分析

涉及到几个关键的类AspectJAutoProxyBeanDefinitionParser,AnnotationAwareAspectJAutoProxyCreator,AbstractAutoProxyCreator,BeanPostProcessor。其中AnnotationAwareAspectJAutoProxyCreator是AOP的核心,AspectJAutoProxyBeanDefinitionParser主要是对AnnotationAwareAspectJAutoProxyCreator完成注册,AnnotationAwareAspectJAutoProxyCreator负责将获取到的aop的bean解析成出各种属性信息最后生成代理

  • AspectJAutoProxyBeanDefinitionParser的parse方法中调用registerAspectJAnnotationAutoProxyCreatorIfNecessary来向容器注册AnnotationAwareAspectJAutoProxyCreator,其中传入的element参数携带了AOP的xml节点资源,用于后面spring加载AnnotationAwareAspectJAutoProxyCreator生成代理代理使用
@Override
    @Nullable
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
        extendBeanDefinition(element, parserContext);
        return null;
    }
//AopNamespaceUtils类的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
            ParserContext parserContext, Element sourceElement) {
        BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
                parserContext.getRegistry(), parserContext.extractSource(sourceElement));
        useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
        registerComponentIfNecessary(beanDefinition, parserContext);
    }
//AopConfigUtils类的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法
@Nullable
    public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
            BeanDefinitionRegistry registry, @Nullable Object source) {

        return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
    }

@Nullable
    private static BeanDefinition registerOrEscalateApcAsRequired(
            Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {

        Assert.notNull(registry, "BeanDefinitionRegistry must not be null");

        if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
            BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
            if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
                int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
                int requiredPriority = findPriorityForClass(cls);
                if (currentPriority < requiredPriority) {
                    apcDefinition.setBeanClassName(cls.getName());
                }
            }
            return null;
        }
//cls就是AnnotationAwareAspectJAutoProxyCreator.class,将和element节点资源一起封装为beanDefinition 注册入容器中
        RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
        beanDefinition.setSource(source);
        beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
        beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
//注册到容器内
        registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
        return beanDefinition;
    }
接下来是解析AnnotationAwareAspectJAutoProxyCreator创建AOP的过程

先看看AnnotationAwareAspectJAutoProxyCreator的继承图

AnnotationAwareAspectJAutoProxyCreator继承了BeanPostProcessor,AOP的创建是在spring加载AnnotationAwareAspectJAutoProxyCreator这个bean的时候,在这个bean实例化后调用其父类BeanPostProcessor的postProcessAfterInitialization方法,这个方法已经由父类AbstractAutoProxyCreator实现了

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

AnnotationAwareAspectJAutoProxyCreator可以根据@Pointcut自动代理匹配的bean。AnnotationAwareAspectJAutoProxyCreator是实现了BeanPostProcessor,当spring初实例化完这个bean后会调用后置处理器的后置处理方法postProcessAfterInitialization,这个方法是其实是调用的AnnotationAwareAspectJAutoProxyCreator的父类AbstractAutoProxyCreator中的实现,其中关键的方法是wrapIfNecessary,wrapIfNecessary主要是获取切面的增强方法,加载标注有aop注解的bean,并将这些bean的增强方法包装成Advisor,Advisor中包装了增强方法的各类信息、增强器和注解上信息和通配符等,例如:" @AfterReturning("controllerAspect()")、execution (* com.gcx.controller...(..))",根据注解生成和初始化增强器,每一个增强方法都会包装成一个Advisor,然后调用createProxy方法根据注解的通配符信息创建生成相应的代理,根据bean的类型选择jdk的动态代理还GCLIB

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;
        }

        // 获取切面所有的增强方法,并将其相关信息封装为Advisor类
        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;
    }

AbstractAdvisorAutoProxyCreator类的getAdvicesAndAdvisorsForBean方法主要是获取aop注解的bean的增强方法,生成Advisor包装类,包装增强器和AOP注解上的通配符信息等

@Override
    @Nullable
    protected Object[] getAdvicesAndAdvisorsForBean(
            Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
//查找aop的注解的bean,获取增强方法的消息,封装成Advisor
        List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
        if (advisors.isEmpty()) {
            return DO_NOT_PROXY;
        }
        return advisors.toArray();
    }

查找aop的注解的bean,获取增强方法的消息,封装成Advisor,主要的过程就是从容器中获取所有的beanName,然后遍历,将有aop注解的类提取出来进行处理,然后获取这些bean的增强方法信息,然后将其封装到Advisor中

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
        List<Advisor> candidateAdvisors = findCandidateAdvisors();
        List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
        extendAdvisors(eligibleAdvisors);
        if (!eligibleAdvisors.isEmpty()) {
            eligibleAdvisors = sortAdvisors(eligibleAdvisors);
        }
        return eligibleAdvisors;
    }
    protected List<Advisor> findCandidateAdvisors() {
        Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
        return this.advisorRetrievalHelper.findAdvisorBeans();
    }

public List<Advisor> findAdvisorBeans() {
        String[] advisorNames = this.cachedAdvisorBeanNames;
        if (advisorNames == null) {
            advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                    this.beanFactory, Advisor.class, true, false);
            this.cachedAdvisorBeanNames = advisorNames;
        }
        if (advisorNames.length == 0) {
            return new ArrayList<>();
        }

        List<Advisor> advisors = new ArrayList<>();
        for (String name : advisorNames) {
            if (isEligibleBean(name)) {
                if (this.beanFactory.isCurrentlyInCreation(name)) {
                    ..........................
                }
                else {
                    try {
                        advisors.add(this.beanFactory.getBean(name, Advisor.class));
                    }
                ...........................
                }
            }
        }
        return advisors;
    }

获取切面所有的增强方法,并将其相关信息封装为Advisor类后调用AbstractAutoProxyCreator类的createProxy方法,使用proxyFactory.getProxy创建代理。对于代理类的创建及处理,spring将其委托给了ProxyFactory,buildAdvisors方法将前面封装的一大堆Advisor进一步封装成config参数并加入ProxyFactory中同时将拦截器一并封装为Advisor也加入到ProxyFactory中,这些Advisor作为创建代理的信息依据

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 (Proxy.isProxyClass(beanClass)) {
                for (Class<?> ifc : beanClass.getInterfaces()) {
                    proxyFactory.addInterface(ifc);
                }
            }
        }
        else {
            if (shouldProxyTargetClass(beanClass, beanName)) {
                proxyFactory.setProxyTargetClass(true);
            }
            else {
                evaluateProxyInterfaces(beanClass, proxyFactory);
            }
        }

        Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
//将所有封装好的advisors 置入ProxyFactory中
        proxyFactory.addAdvisors(advisors);
        proxyFactory.setTargetSource(targetSource);
        customizeProxyFactory(proxyFactory);

        proxyFactory.setFrozen(this.freezeProxy);
        if (advisorsPreFiltered()) {
            proxyFactory.setPreFiltered(true);
        }
        ClassLoader classLoader = getProxyClassLoader();
        if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
            classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
        }
        return proxyFactory.getProxy(classLoader);
    }

public Object getProxy(@Nullable ClassLoader classLoader) {
        return createAopProxy().getProxy(classLoader);
    }

protected final synchronized AopProxy createAopProxy() {
        if (!this.active) {
            activate();
        }
        return getAopProxyFactory().createAopProxy(this);
    }

在DefaultAopProxyFactory类中判断需要代理的bean是否有接口,有接口的则使用jdk的动态代理,否则使用cglib创建代理,创建代理是根据之前封装的消息进行创建的

//在DefaultAopProxyFactory类中判断需要代理的bean是否有接口,有接口的则使用jdk的动态代理,否则使用cglib创建代理
@Override
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (!NativeDetector.inNativeImage() &&
                (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);
        }
    }


public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
        Assert.notNull(config, "AdvisedSupport must not be null");
        if (config.getAdvisorCount() == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
            throw new AopConfigException("No advisors and no TargetSource specified");
        }
        this.advised = config;
        this.proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
        findDefinedEqualsAndHashCodeMethods(this.proxiedInterfaces);
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容