zoukankan      html  css  js  c++  java
  • spring事物(一),@EnableTransactionManagement @Transactional 启动解析

    原文连接:https://www.cnblogs.com/leaveast/p/11765503.html,侵删

    1.事物的声明阶段

      @EnableTransactionManagement,是我们开启注解事物的第一步,我们来看下这个类为我们干了什么

    复制代码
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(TransactionManagementConfigurationSelector.class)
    public @interface EnableTransactionManagement {
    
    </span><span style="color: #0000ff;">boolean</span> proxyTargetClass() <span style="color: #0000ff;">default</span> <span style="color: #0000ff;">false</span><span style="color: #000000;">;
    
    AdviceMode mode() </span><span style="color: #0000ff;">default</span><span style="color: #000000;"> AdviceMode.PROXY;
    
    </span><span style="color: #0000ff;">int</span> order() <span style="color: #0000ff;">default</span><span style="color: #000000;"> Ordered.LOWEST_PRECEDENCE;  
    

    }

    复制代码

      我们主要看 TransactionManagementConfigurationSelector 干了件什么事情。它主要往spring 容器中导入了 AutoProxyRegistrar , ProxyTransactionManagementConfiguration两个对象。

    复制代码
    public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
        @Override
        protected String[] selectImports(AdviceMode adviceMode) {
            switch (adviceMode) {
                case PROXY:
                    return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};
                case ASPECTJ:
                    return new String[] {TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME};
                default:
                    return null;
            }
        }
    

    }

    复制代码

    ProxyTransactionManagementConfiguration的作用,我们可以看到此类是一个配置类,主要为spring容器中导入了3个bean。这三个bean分别的作用,我们下来详说

    复制代码
    @Configuration
    public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
    
    @Bean(name </span>=<span style="color: #000000;"> TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
        BeanFactoryTransactionAttributeSourceAdvisor advisor </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> BeanFactoryTransactionAttributeSourceAdvisor();
        advisor.setTransactionAttributeSource(transactionAttributeSource());
        advisor.setAdvice(transactionInterceptor());
        advisor.setOrder(</span><span style="color: #0000ff;">this</span>.enableTx.&lt;Integer&gt;getNumber("order"<span style="color: #000000;">));
        </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> advisor;
    }
    
    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> TransactionAttributeSource transactionAttributeSource() {
        </span><span style="color: #0000ff;">return</span> <span style="color: #0000ff;">new</span><span style="color: #000000;"> AnnotationTransactionAttributeSource();
    }
    
    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> TransactionInterceptor transactionInterceptor() {
        TransactionInterceptor interceptor </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> TransactionInterceptor();
        interceptor.setTransactionAttributeSource(transactionAttributeSource());
        </span><span style="color: #0000ff;">if</span> (<span style="color: #0000ff;">this</span>.txManager != <span style="color: #0000ff;">null</span><span style="color: #000000;">) {
            interceptor.setTransactionManager(</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.txManager);
        }
        </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> interceptor;
    }
    

    }

    复制代码

    AutoProxyRegistrar 这个类,实现了ImportBeanDefinitionRegistrar接口,主要是为容器中注入了 InfrastructureAdvisorAutoProxyCreator 这个bean。

    复制代码
    public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    
    </span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">final</span> Log logger =<span style="color: #000000;"> LogFactory.getLog(getClass());</span><span style="color: #000000;">
    @Override
    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        </span><span style="color: #0000ff;">boolean</span> candidateFound = <span style="color: #0000ff;">false</span><span style="color: #000000;">;
        Set</span>&lt;String&gt; annoTypes =<span style="color: #000000;"> importingClassMetadata.getAnnotationTypes();
        </span><span style="color: #0000ff;">for</span><span style="color: #000000;"> (String annoType : annoTypes) {
            AnnotationAttributes candidate </span>=<span style="color: #000000;"> AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType);
            </span><span style="color: #0000ff;">if</span> (candidate == <span style="color: #0000ff;">null</span><span style="color: #000000;">) {
                </span><span style="color: #0000ff;">continue</span><span style="color: #000000;">;
            }
            Object mode </span>= candidate.get("mode"<span style="color: #000000;">);
            Object proxyTargetClass </span>= candidate.get("proxyTargetClass"<span style="color: #000000;">);
            </span><span style="color: #0000ff;">if</span> (mode != <span style="color: #0000ff;">null</span> &amp;&amp; proxyTargetClass != <span style="color: #0000ff;">null</span> &amp;&amp; AdviceMode.<span style="color: #0000ff;">class</span> == mode.getClass() &amp;&amp;<span style="color: #000000;">
                    Boolean.</span><span style="color: #0000ff;">class</span> ==<span style="color: #000000;"> proxyTargetClass.getClass()) {
                candidateFound </span>= <span style="color: #0000ff;">true</span><span style="color: #000000;">;
                </span><span style="color: #0000ff;">if</span> (mode ==<span style="color: #000000;"> AdviceMode.PROXY) {
                    AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
                    </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> ((Boolean) proxyTargetClass) {
                        AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
                        </span><span style="color: #0000ff;">return</span><span style="color: #000000;">;
                    }
                }
            }
        }</span><span style="color: #000000;">
    }
    

      

      public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
        return registerAutoProxyCreatorIfNecessary(registry, null);
    }

      public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
        return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
    }

    
    }
    复制代码

    我们首先来看一下这个类的继承关系图。

    这个类首先是beanpostprocessor的实现类,他会对所有的bean做一次后置增强处理,我们在AbstractAutoProxyCreator中的 postProcessAfterInitialization 方法中可以看到,他会根据规则去对bean包装从而创造满足条件的代理。

    复制代码
    /**
         * Create a proxy with the configured interceptors if the bean is
         * identified as one to proxy by the subclass.
         * @see #getAdvicesAndAdvisorsForBean
         */
        @Override
        public Object postProcessAfterInitialization(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;
        }
    复制代码

    我们接下来进入 wrapIfNecessary 方法

    复制代码
        // 如果需要的话,包装给定的bean,也就是说它是否有资格代理。
        protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
            if (beanName != null && 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;
            }
    
        </span><span style="color: #008000;">//</span><span style="color: #008000;"> Create proxy if we have advice.</span>
        Object[] specificInterceptors = <span style="color: #ff0000;">getAdvicesAndAdvisorsForBean</span>(bean.getClass(), beanName, <span style="color: #0000ff;">null</span><span style="color: #000000;">);
        </span><span style="color: #0000ff;">if</span> (specificInterceptors !=<span style="color: #000000;"> DO_NOT_PROXY) {
            </span><span style="color: #0000ff;">this</span><span style="color: #000000;">.advisedBeans.put(cacheKey, Boolean.TRUE);
            Object proxy </span>=<span style="color: #000000;"> createProxy(
                    bean.getClass(), beanName, specificInterceptors, </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> SingletonTargetSource(bean));
            </span><span style="color: #0000ff;">this</span><span style="color: #000000;">.proxyTypes.put(cacheKey, proxy.getClass());
            </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> proxy;
        }
    
        </span><span style="color: #0000ff;">this</span><span style="color: #000000;">.advisedBeans.put(cacheKey, Boolean.FALSE);
        </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> bean;
    }</span></pre>
    
    复制代码

    我们着重关注一下 getAdvicesAndAdvisorsForBean 方法

    复制代码
        @Override
        protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
            List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
            if (advisors.isEmpty()) {
                return DO_NOT_PROXY;
            }
            return advisors.toArray();
        }
    复制代码

    这个方法没什么说的,主要表达的意思是为bean找到合格的增强器。

    复制代码
    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; }
    复制代码

    在 findCandidateAdvisors 方法中,我们可以看到我们上文中注入的三个bean中的其中一个 BeanFactoryTransactionAttributeSourceAdvisor,这个bean可以理解为我们的事物增强器。

    复制代码
        public List<Advisor> findAdvisorBeans() {
            // Determine list of advisor bean names, if not cached already.
            String[] advisorNames = null;
            synchronized (this) {
                advisorNames = this.cachedAdvisorBeanNames;
                if (advisorNames == null) {
                    // 找到上文注入的 BeanFactoryTransactionAttributeSourceAdvisor
                    advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                            this.beanFactory, Advisor.class, true, false);
                    this.cachedAdvisorBeanNames = advisorNames;
                }
            }
            if (advisorNames.length == 0) {
                return new LinkedList<Advisor>();
            }return advisors;
        }
    复制代码

    接下来我们看 findAdvisorsThatCanApply 方法,这个方法主要实现了,候选的增强器是否可以对当前bean使用。

    复制代码
        public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
            if (candidateAdvisors.isEmpty()) {
                return candidateAdvisors;
            }
            List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
            for (Advisor candidate : candidateAdvisors) {
                if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
                    eligibleAdvisors.add(candidate);
                }
            }
            boolean hasIntroductions = !eligibleAdvisors.isEmpty();
            for (Advisor candidate : candidateAdvisors) {
                if (candidate instanceof IntroductionAdvisor) {
                    // already processed
                    continue;
                }
                if (canApply(candidate, clazz, hasIntroductions)) {
                    eligibleAdvisors.add(candidate);
                }
            }
            return eligibleAdvisors;
        }
    复制代码

    我们进入方法走到canApply处,继续往下跟。

    复制代码
        public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
            if (advisor instanceof IntroductionAdvisor) {
                return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
            }
            else if (advisor instanceof PointcutAdvisor) {
                PointcutAdvisor pca = (PointcutAdvisor) advisor;
                return canApply(pca.getPointcut(), targetClass, hasIntroductions);
            }
            else {
                // It doesn't have a pointcut so we assume it applies.
                return true;
            }
        }
    复制代码

    先判断类型后做一次转化,再次进入canApply方法

    复制代码
    public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
            Assert.notNull(pc, "Pointcut must not be null");
            if (!pc.getClassFilter().matches(targetClass)) {
                return false;
            }
    
        MethodMatcher methodMatcher </span>=<span style="color: #000000;"> pc.getMethodMatcher();
        </span><span style="color: #0000ff;">if</span> (methodMatcher ==<span style="color: #000000;"> MethodMatcher.TRUE) {
            </span><span style="color: #008000;">//</span><span style="color: #008000;"> No need to iterate the methods if we're matching any method anyway...</span>
            <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">true</span><span style="color: #000000;">;
        }
    
        IntroductionAwareMethodMatcher introductionAwareMethodMatcher </span>= <span style="color: #0000ff;">null</span><span style="color: #000000;">;
        </span><span style="color: #0000ff;">if</span> (methodMatcher <span style="color: #0000ff;">instanceof</span><span style="color: #000000;"> IntroductionAwareMethodMatcher) {
            introductionAwareMethodMatcher </span>=<span style="color: #000000;"> (IntroductionAwareMethodMatcher) methodMatcher;
        }
    
        Set</span>&lt;Class&lt;?&gt;&gt; classes = <span style="color: #0000ff;">new</span> LinkedHashSet&lt;Class&lt;?&gt;&gt;<span style="color: #000000;">(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
        classes.add(targetClass);
        </span><span style="color: #0000ff;">for</span> (Class&lt;?&gt;<span style="color: #000000;"> clazz : classes) {
            Method[] methods </span>=<span style="color: #000000;"> ReflectionUtils.getAllDeclaredMethods(clazz);
            </span><span style="color: #0000ff;">for</span><span style="color: #000000;"> (Method method : methods) {
                </span><span style="color: #0000ff;">if</span> ((introductionAwareMethodMatcher != <span style="color: #0000ff;">null</span> &amp;&amp;<span style="color: #000000;">
                        introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) </span>||<span style="color: #000000;"><span style="color: #ff0000;">
                        methodMatcher.matches(method, targetClass)</span>) {
                    </span><span style="color: #0000ff;">return</span> <span style="color: #0000ff;">true</span><span style="color: #000000;">;
                }
            }
        }
    
        </span><span style="color: #0000ff;">return</span> <span style="color: #0000ff;">false</span><span style="color: #000000;">;
    }</span></pre>
    
    复制代码

    进入matches方法,下来的代码我会合并的连贯一些

    复制代码
    @Override
        public boolean matches(Method method, Class<?> targetClass) {
            if (targetClass != null && TransactionalProxy.class.isAssignableFrom(targetClass)) {
                return false;
            }
            TransactionAttributeSource tas = getTransactionAttributeSource();
            return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
        }
    // 进入AbstractFallbackTransactionAttributeSource.getTransactionAttribute()
      TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);
      TransactionAttribute txAttr = findTransactionAttribute(specificMethod);

    复制代码

    进入解析的流程

    复制代码
    public class SpringTransactionAnnotationParser implements TransactionAnnotationParser, Serializable {
    
    @Override
    </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
        <span style="color: #ff0000;">AnnotationAttributes attributes </span></span><span style="color: #ff0000;">= AnnotatedElementUtils.getMergedAnnotationAttributes(ae, Transactional.class);
        </span><span style="color: #0000ff;">if</span> (attributes != <span style="color: #0000ff;">null</span><span style="color: #000000;">) {
            </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> parseTransactionAnnotation(attributes);
        }
        </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> {
            </span><span style="color: #0000ff;">return</span> <span style="color: #0000ff;">null</span><span style="color: #000000;">;
        }
    }
    
    </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> TransactionAttribute parseTransactionAnnotation(Transactional ann) {
        </span><span style="color: #0000ff;">return</span> parseTransactionAnnotation(AnnotationUtils.getAnnotationAttributes(ann, <span style="color: #0000ff;">false</span>, <span style="color: #0000ff;">false</span><span style="color: #000000;">));
    }
    
    </span><span style="color: #0000ff;">protected</span><span style="color: #000000;"> TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
        RuleBasedTransactionAttribute rbta </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> RuleBasedTransactionAttribute();
        Propagation propagation </span>= attributes.getEnum(<span style="color: #ff0000;">"propagation"</span><span style="color: #000000;">);
        rbta.setPropagationBehavior(<span style="color: #ff0000;">propagation.value()</span>);
        Isolation isolation </span>= attributes.getEnum(<span style="color: #ff0000;">"isolation"</span><span style="color: #000000;">);
        rbta.setIsolationLevel(<span style="color: #ff0000;">isolation.value()</span>);
        rbta.setTimeout(attributes.getNumber(</span>"timeout"<span style="color: #000000;">).intValue());
        rbta.setReadOnly(attributes.getBoolean(</span>"readOnly"<span style="color: #000000;">));
        rbta.setQualifier(attributes.getString(</span>"value"<span style="color: #000000;">));
        ArrayList</span>&lt;RollbackRuleAttribute&gt; rollBackRules = <span style="color: #0000ff;">new</span> ArrayList&lt;RollbackRuleAttribute&gt;<span style="color: #000000;">();
        Class</span>&lt;?&gt;[] rbf = attributes.getClassArray("rollbackFor"<span style="color: #000000;">);
        </span><span style="color: #0000ff;">for</span> (Class&lt;?&gt;<span style="color: #000000;"> rbRule : rbf) {
            RollbackRuleAttribute rule </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> RollbackRuleAttribute(rbRule);
            rollBackRules.add(rule);
        }
        String[] rbfc </span>= attributes.getStringArray("rollbackForClassName"<span style="color: #000000;">);
        </span><span style="color: #0000ff;">for</span><span style="color: #000000;"> (String rbRule : rbfc) {
            RollbackRuleAttribute rule </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> RollbackRuleAttribute(rbRule);
            rollBackRules.add(rule);
        }
        Class</span>&lt;?&gt;[] nrbf = attributes.getClassArray("noRollbackFor"<span style="color: #000000;">);
        </span><span style="color: #0000ff;">for</span> (Class&lt;?&gt;<span style="color: #000000;"> rbRule : nrbf) {
            NoRollbackRuleAttribute rule </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> NoRollbackRuleAttribute(rbRule);
            rollBackRules.add(rule);
        }
        String[] nrbfc </span>= attributes.getStringArray("noRollbackForClassName"<span style="color: #000000;">);
        </span><span style="color: #0000ff;">for</span><span style="color: #000000;"> (String rbRule : nrbfc) {
            NoRollbackRuleAttribute rule </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> NoRollbackRuleAttribute(rbRule);
            rollBackRules.add(rule);
        }
        rbta.getRollbackRules().addAll(rollBackRules);
        </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> rbta;
    }</span><span style="color: #000000;">
    

    }

    复制代码

    此方法会先拿到标记为 Transcantional 注解的方法,然后遍历属性。最后返回attr,如果解析到的attr不为空,则会将此增强器对待增强的bean做增强处理。

    复制代码
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
            if (beanName != null && 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;
            }
    
        </span><span style="color: #008000;">//</span><span style="color: #008000;"> Create proxy if we have advice.</span>
       <span style="color: #ff0000;"> 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;
        }
    
        </span><span style="color: #0000ff;">this</span><span style="color: #000000;">.advisedBeans.put(cacheKey, Boolean.FALSE);
        </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> bean;
    }</span></pre>
    
    复制代码

    我们回到 wrapIfNecessary方法,如果返回的advisor不为空,我们会为他生成代理对象。 

    Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));

    这里就跟我们的使用AOP创建代理无缝融合,在找到当前bean确定要进行增强处理和增强的advisor后,使用动态代理的方式创建代理对象。

     

  • 相关阅读:
    LinkageSel无限级联动下拉菜单
    纯CSS+HTML自定义checkbox效果[转]
    jquery1.9+,jquery1.10+ 为什么不支持live方法了?
    电脑按键混乱,好像被锁定了Alt键
    docker 清理无用的卷
    dotnet core linux 接入支付宝H5支付,提示:System.PlatformNotSupportedException","Message":"'CspParameters' requires Windows Cryptographic API (CAPI), which is not available on this platform.
    【每天学一点Linux】centos7 docker 启动cpu100% 飙升居高不下 无法关机 无法杀死进程
    【每天学一点Linux】centos7修改selinux导致无法开机 Failed to load SELinux policy. Freezing
    webapi HttpGet标签
    强制结束虚拟机 centos home 卷丢失导致无法挂载进入 emergency mode 紧急模式
  • 原文地址:https://www.cnblogs.com/cosmos-wong/p/12862289.html
Copyright © 2011-2022 走看看