当前位置: 首页 > news >正文

周口网站建设定制zkweb网站seo诊断分析报告

周口网站建设定制zkweb,网站seo诊断分析报告,米客优品的网站是哪做的,关于政府网站建设的研究报告参考文章: 《Spring事务源码解析之tx:annotation-driven标签解析》 《Spring 源码解析—事务执行》 参考资料: 《Spring AOP源码:开启注解读取》 《Spring AOP源码2:查找增强器》 《Spring AOP源码3:实现代理》 …

参考文章:

《Spring事务源码解析之tx:annotation-driven标签解析》

《Spring 源码解析—事务执行》

参考资料:

《Spring AOP源码:开启注解读取》

《Spring AOP源码2:查找增强器》

《Spring AOP源码3:实现代理》

        写在开头:本文为个人学习笔记,内容比较随意,夹杂个人理解,如有错误,欢迎指正。

前言

        本文我们介绍下Spring中的事务管理的源码,Spring的事务管理主要有申明式事务与编程式事务这两种,如果对这两种方式不了解的朋友可以先看下这篇文章(《Spring事务管理(详解+实例)》)然后再来深入源码分析。

       我们通过如下方式开启事务,由于申明式事务的注入方式式基于代理方式实现的,因此这里也建议先了解下AOP的源码,这部分我前文已经做了完整的介绍,因此下文将不会对AOP的功能做深入的解析,有需要的朋友可以看下我之前AOP的文章。

<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/>
</bean>

目录

前言

一、AnnotationDrivenBeanDefinitionParser

        1、TxNamespaceHandler

        2、AnnotationDrivenBeanDefinitionParser

二、、AopAutoProxyConfigurer

        1、注册自动代理创建器

        1.1、注册InfrastructureAdvisorAutoProxyCreator

         1.2、设置proxy-target-class和expose-proxy属性

        1.3、注册ComponentDefinition

        2、注册了事务管理器等Bean

        2.1、创建AnnotationTransactionAttributeSource

        2.2、创建TransactionInterceptor

        2.3、创建BeanFactoryTransactionAttributeSourceAdvisor

三、代理对象的创建

        1、InfrastructureAdvisorAutoProxyCreator

        2、canapply

        2.1、getMethodMatcher

        2.2、getTransactionAttribute 

        2.3、computeTransactionAttribute

        2.4、findTransactionAttribute

        2.5、parseTransactionAnnotation

补充

        事务仅对 public 方法有效


一、AnnotationDrivenBeanDefinitionParser

        在介绍IOC时我们讲过Spring通过spring.handlers配置文件处理自定义命名空间节点(《Spring IOC:parseCustomElement调用链》),从下面的配置中可以看出TX标签由TxNamespaceHandler类解析。

http\://www.springframework.org/schema/tx=org.springframework.transaction.config.TxNamespaceHandler

        1、TxNamespaceHandler

        TxNamespaceHandler中的init方法针对不通的属性配置实例化不通的解析对象,这里我们使用了annotation-driven,因此实例化的对象为AnnotationDrivenBeanDefinitionParser。

	@Overridepublic void init() {registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());}

        2、AnnotationDrivenBeanDefinitionParser

        下面就来到了AnnotationDrivenBeanDefinitionParser的parse方法,因为Spring事务管理也是基于创建代理类来实现的,因此和AOP一样首先需要创建代理创建器。

	@Overridepublic BeanDefinition parse(Element element, ParserContext parserContext) {registerTransactionalEventListenerFactory(parserContext);String mode = element.getAttribute("mode");// 判断是否配置了mode属性且为aspectj,满足条件则走aop切面的方式管理事务if ("aspectj".equals(mode)) {registerTransactionAspect(element, parserContext);}else {// 默认方式,配置自动代理创建器AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);}return null;}

二、、AopAutoProxyConfigurer

        创建代理创建器的流程可以拆解成如下几个步骤,我们下面分别对其进行分析。

	private static class AopAutoProxyConfigurer {public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {1、注册自动代理创建器2、注册了事务管理器等Bean			}}

        1、注册自动代理创建器

        configureAutoProxyCreator调用registerAutoProxyCreatorIfNecessary注册自动代理创建器

		public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {// 1、创建代理创建器AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);2、注册了AnnotationTransactionAttributeSource、TransactionInterceptor和BeanFactoryTransactionAttributeSourceAdvisor			}

        registerAutoProxyCreatorIfNecessary分为三个步骤

        (1)注册InfrastructureAdvisorAutoProxyCreator

        (2)设置proxy-target-class和expose-proxy属性

        (3)注册ComponentDefinition

	public static void registerAutoProxyCreatorIfNecessary(ParserContext parserContext, Element sourceElement) {// 向容器中注册InfrastructureAdvisorAutoProxyCreatorBeanDefinition beanDefinition = AopConfigUtils.registerAutoProxyCreatorIfNecessary(parserContext.getRegistry(), parserContext.extractSource(sourceElement));// 设置proxy-target-class和expose-proxy配置useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);// 注册ComponentDefinitionregisterComponentIfNecessary(beanDefinition, parserContext);}

        1.1、注册InfrastructureAdvisorAutoProxyCreator

        很简单的方法,向容器中注册固定的BeanDefinition。

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

         1.2、设置proxy-target-class和expose-proxy属性

        解析标签中的proxy-target-class和expose-proxy属性值。proxy-target-class主要控制是使用Jdk代理还是Cglib代理实现,expose-proxy用于控制是否将生成的代理类的实例防御AopContext中,并且暴露给相关子类使用。

    public static final String PROXY_TARGET_CLASS_ATTRIBUTE = "proxy-target-class";private static final String EXPOSE_PROXY_ATTRIBUTE = "expose-proxy";private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Element sourceElement) {if (sourceElement != null) {boolean proxyTargetClass = Boolean.parseBoolean(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));if (proxyTargetClass) {AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);}boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));if (exposeProxy) {AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);}}}

        1.3、注册ComponentDefinition

	public static final String AUTO_PROXY_CREATOR_BEAN_NAME ="org.springframework.aop.config.internalAutoProxyCreator";private static void registerComponentIfNecessary(BeanDefinition beanDefinition, ParserContext parserContext) {if (beanDefinition != null) {parserContext.registerComponent(new BeanComponentDefinition(beanDefinition, AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME));}}

        2、注册了事务管理器等Bean

	    public static final String TRANSACTION_ADVISOR_BEAN_NAME ="org.springframework.transaction.config.internalTransactionAdvisor";public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);//1、注册自动代理创建器AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);// 判断容器中有无internalTransactionAdvisorString txAdvisorBeanName = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME;if (!parserContext.getRegistry().containsBeanDefinition(txAdvisorBeanName)) {//2、注册了事务管理器等Bean		创建AnnotationTransactionAttributeSource创建TransactionInterceptor创建BeanFactoryTransactionAttributeSourceAdvisor}}}

        2.1、创建AnnotationTransactionAttributeSource

Object eleSource = parserContext.extractSource(element);
// 创建并注册AnnotationTransactionAttributeSource的BeanDefinition
RootBeanDefinition sourceDef = new RootBeanDefinition(
"org.springframework.transaction.annotation.AnnotationTransactionAttributeSource");
sourceDef.setSource(eleSource);
sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);

        2.2、创建TransactionInterceptor

        这一步创建了TransactionInterceptor的BeanDefinition,并将上一步创建的AnnotationTransactionAttributeSource作为transactionAttributeSource属性注入了进去,这个属性后面创建代理类的时候将会用到。

        另外调用了registerTransactionManager创建了事务管理器。

RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);
interceptorDef.setSource(eleSource);
interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registerTransactionManager(element, interceptorDef);
interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);

        registerTransactionManager创建了事务管理器,其实就是添加事务管理器的beanName。

	private static void registerTransactionManager(Element element, BeanDefinition def) {def.getPropertyValues().add("transactionManagerBeanName",TxNamespaceHandler.getTransactionManagerName(element));}

         事务管理器的beanName在不指定transaction-manager属性的时候,会默认寻找id固定名为transactionManager的bean作为事务管理器。

    // TxNamespaceHandler.javastatic final String TRANSACTION_MANAGER_ATTRIBUTE = "transaction-manager";static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager";static String getTransactionManagerName(Element element) {return (element.hasAttribute(TRANSACTION_MANAGER_ATTRIBUTE) ?element.getAttribute(TRANSACTION_MANAGER_ATTRIBUTE) : DEFAULT_TRANSACTION_MANAGER_BEAN_NAME);}

        2.3、创建BeanFactoryTransactionAttributeSourceAdvisor

        创建BeanFactoryTransactionAttributeSourceAdvisor的BeanDefinition,同时将前两个步骤中创建的AnnotationTransactionAttributeSource、TransactionInterceptor作为属性添加到当前Bean中,最后注册到容器中。

RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);
advisorDef.setSource(eleSource);
advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
if (element.hasAttribute("order")) {dvisorDef.getPropertyValues().add("order", element.getAttribute("order"));}
parserContext.getRegistry().registerBeanDefinition(txAdvisorBeanName, advisorDef);

        总结一下,这两部其实主要是注册了InfrastructureAdvisorAutoProxyCreator与BeanFactoryTransactionAttributeSourceAdvisor,可以从名字看出来一个是代理自动创建器,一个是增强器。

三、代理对象的创建

        1、InfrastructureAdvisorAutoProxyCreator

        InfrastructureAdvisorAutoProxyCreator类的继承关系如下图,可以发现其和我们之前介绍AOP时提到的AnnotationAwareAspectJAutoProxyCreator一样继承了AbstractAdvisorAutoProxyCreator。

这里写图片描述

          抽象类AbstractAdvisorAutoProxyCreator继承了BeanPostProcessor接口,因此在Bean实例化的时候会去为其创建代理对象。这部分内容如果不熟悉可以看下我之前的文章(《Spring IOC:bean的生命周期与@Autowire(3)》、《Spring AOP源码2:查找增强器》)

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (bean != null) {// 如果是普通bean,则返回beanName,如果是FactoryBean,则返回加上前缀&的&beanNameObject cacheKey = getCacheKey(bean.getClass(), beanName);// 判断当前bean是否需要被代理,如果需要则进行封装(earlyProxyReferences中缓存的是已经创建好的代理对象)if (!this.earlyProxyReferences.contains(cacheKey)) {return wrapIfNecessary(bean, beanName, cacheKey);}}return bean;
}

        2、canapply

        在创建代理类时,会先查找能够应用在当前bean上的增强器,这部分内容我们前文有过介绍就不赘述了,这里直接从如何判断是否适配的判断方法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;/* 判断当前增强是否可以应用在当前bean上 */return canApply(pca.getPointcut(), targetClass, hasIntroductions);}else {return true;}
}

        这里的advisor就是我们的增强器BeanFactoryTransactionAttributeSourceAdvisor,而pca.getPointcut()就是获取其内部创建的TransactionAttributeSourcePointcut。

	@Overridepublic Pointcut getPointcut() {return this.pointcut;}private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {@Overrideprotected TransactionAttributeSource getTransactionAttributeSource() {return transactionAttributeSource;}};

        该对象重写了getTransactionAttributeSource方法,获取的就是BeanFactoryTransactionAttributeSourceAdvisor在注册时添加的transactionAttributeSource属性,其实现类为AnnotationTransactionAttributeSource。 

        // configureAutoProxyCreator方法RootBeanDefinition sourceDef = new RootBeanDefinition("org.springframework.transaction.annotation.AnnotationTransactionAttributeSource");sourceDef.setSource(eleSource);sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));

        继续往下深入,调用pc.getMethodMatcher()获取方法匹配器,这里的pc就是上文中的TransactionAttributeSourcePointcut类。

    public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {Assert.notNull(pc, "Pointcut must not be null");// 获取当前Advisor的CalssFilter,并且调用其matches()方法判断当前切点表达式是否与目标bean匹配,// 这里ClassFilter指代的切点表达式主要是当前切面类上使用的@Aspect注解中所指代的切点表达式if (!pc.getClassFilter().matches(targetClass)) {return false;}// 判断如果当前Advisor所指代的方法的切点表达式如果是对任意方法都放行,则直接返回MethodMatcher methodMatcher = pc.getMethodMatcher();if (methodMatcher == MethodMatcher.TRUE) {// No need to iterate the methods if we're matching any method anyway...return true;}IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;if (methodMatcher instanceof IntroductionAwareMethodMatcher) {introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;}// 获取目标类的所有接口Set<Class<?>> classes = new LinkedHashSet<Class<?>>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));classes.add(targetClass);// 遍历目标类上的接口方法for (Class<?> clazz : classes) {Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);for (Method method : methods) {// 使用matches判断能否作用于该方法上if ((introductionAwareMethodMatcher != null &&introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||methodMatcher.matches(method, targetClass)) {return true;}}}return false;}

        2.1、getMethodMatcher

        TransactionAttributeSourcePointcut的getMethodMatcher方法是在它的父类StaticMethodMatcherPointcut中实现的。

public final MethodMatcher getMethodMatcher() {return this;
}

        该方法直接返回了this,也就是下面methodMatcher.matches方法就是调用TransactionAttributeSourcePointcut的matches方法。getTransactionAttributeSource即我们之前说过的AnnotationTransactionAttributeSource,getTransactionAttribute则是调用的其父类AbstractFallbackTransactionAttributeSource的方法。

public boolean matches(Method method, Class<?> targetClass) {if (TransactionalProxy.class.isAssignableFrom(targetClass)) {return false;}TransactionAttributeSource tas = getTransactionAttributeSource();return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}

        2.2、getTransactionAttribute 

         getTransactionAttribute中判断有无缓存,如果缓存不存在则调用computeTransactionAttribute方法查找事务属性,获取后进行缓存。

public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass) {if (method.getDeclaringClass() == Object.class) {return null;}// 先查询缓存Object cacheKey = getCacheKey(method, targetClass);Object cached = this.attributeCache.get(cacheKey);if (cached != null) {if (cached == NULL_TRANSACTION_ATTRIBUTE) {return null;}else {return (TransactionAttribute) cached;}}else {// 获取事务属性 TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);// 事务属性不存在if (txAttr == null) {this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);}else {String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);if (txAttr instanceof DefaultTransactionAttribute) {((DefaultTransactionAttribute) txAttr).setDescriptor(methodIdentification);}if (logger.isDebugEnabled()) {logger.debug("Adding transactional method '" + methodIdentification + "' with attribute: " + txAttr);}// 将解析好的事务属性存入缓存this.attributeCache.put(cacheKey, txAttr);}return txAttr;}
}

        2.3、computeTransactionAttribute

         computeTransactionAttribute方法会依次从方法上和类上查找事务申明,使用的是findTransactionAttribute方法。

        这里注意下第一行的内容,只对public的方法可继续往下走,这就解释了为什么事务只在public方法上生效。

protected TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {return null;}Class<?> userClass = ClassUtils.getUserClass(targetClass);// method为接口中的方法,specificMethod为具体类中的方法Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);// 查看方法中是否存在事务声明 TransactionAttribute txAttr = findTransactionAttribute(specificMethod);if (txAttr != null) {return txAttr;}// 查看方法所在类中是否存在事务声明 txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {return txAttr;}if (specificMethod != method) {// 查看接口方法中是否存在事务声明txAttr = findTransactionAttribute(method);if (txAttr != null) {return txAttr;}// 查看接口类中是否存在事务声明txAttr = findTransactionAttribute(method.getDeclaringClass());if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {return txAttr;}}return null;
}

        2.4、findTransactionAttribute

        findTransactionAttribute方法遍历所有的事务注解解析器,判断哪一个能用来进行解析,这里默认使用的是Spring的事务注解解析器SpringTransactionAnnotationParser。

protected TransactionAttribute findTransactionAttribute(Method method) {return determineTransactionAttribute(method);
}protected TransactionAttribute findTransactionAttribute(Class<?> clazz) {return determineTransactionAttribute(clazz);
}protected TransactionAttribute determineTransactionAttribute(AnnotatedElement ae) {if (ae.getAnnotations().length > 0) {// 遍历事务注解解析器for (TransactionAnnotationParser annotationParser : this.annotationParsers) {// 解析事务注解TransactionAttribute attr = annotationParser.parseTransactionAnnotation(ae);if (attr != null) {return attr;}}}return null;
}

        this.annotationParsers是在AnnotationTransactionAttributeSource类初始化的时候初始化的

public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {this.publicMethodsOnly = publicMethodsOnly;this.annotationParsers = new LinkedHashSet<TransactionAnnotationParser>(2);// 添加Spring事务注解解析器this.annotationParsers.add(new SpringTransactionAnnotationParser());// 添加JTA事务注解解析器if (jta12Present) { this.annotationParsers.add(new JtaTransactionAnnotationParser());}// 添加EJB事务注解解析器if (ejb3Present) {this.annotationParsers.add(new Ejb3TransactionAnnotationParser());}
}

        2.5、parseTransactionAnnotation

        annotationParser.parseTransactionAnnotation调用了SpringTransactionAnnotationParser的parseTransactionAnnotation方法,内部又调用了parseTransactionAnnotation。

public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ae, Transactional.class);if (attributes != null) {// 解析事务注解属性return parseTransactionAnnotation(attributes);}else {return null;}
}

         parseTransactionAnnotation方法则解析了解析注解@Transactional注解中声明的各种属性。

protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();Propagation propagation = attributes.getEnum("propagation");rbta.setPropagationBehavior(propagation.value());Isolation isolation = attributes.getEnum("isolation");rbta.setIsolationLevel(isolation.value());rbta.setTimeout(attributes.getNumber("timeout").intValue());rbta.setReadOnly(attributes.getBoolean("readOnly"));rbta.setQualifier(attributes.getString("value"));ArrayList<RollbackRuleAttribute> rollBackRules = new ArrayList<RollbackRuleAttribute>();Class<?>[] rbf = attributes.getClassArray("rollbackFor");for (Class<?> rbRule : rbf) {RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule);rollBackRules.add(rule);}String[] rbfc = attributes.getStringArray("rollbackForClassName");for (String rbRule : rbfc) {RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule);rollBackRules.add(rule);}Class<?>[] nrbf = attributes.getClassArray("noRollbackFor");for (Class<?> rbRule : nrbf) {NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule);rollBackRules.add(rule);}String[] nrbfc = attributes.getStringArray("noRollbackForClassName");for (String rbRule : nrbfc) {NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule);rollBackRules.add(rule);}rbta.getRollbackRules().addAll(rollBackRules);return rbta;
}

        到这里,我们就找除了适配于当前Bean的增强,结合之前AOP的内容,整个步骤结束后会生成该Bean的代理类,在方法执行时调用的事务的生效正是由代理类来完成的,具体的事务控制我们将在下文讲解。

补充

        事务仅对 public 方法有效

        在本文内容的computeTransactionAttribute方法中,第一行在读取注解上的属性方法中,会优先判断方法是否是 public,如果不是 public,就不会读取事务配置信息。

        因此@Transactional注解只能在public方法上生效。AbstractFallbackTransactionAttributeSource#computeTransactionAttribute

http://www.dinnco.com/news/52436.html

相关文章:

  • 电子商务网站建设收益举例天津seo诊断
  • 有什么教做甜品的网站如何优化关键词的方法
  • 衢州网站建设网络营销培训班
  • 深圳自适应网站开发营销网络推广方式有哪些
  • 网站循环滚动图片z怎么做国际网络销售平台有哪些
  • 教做幼儿菜谱菜的网站百度集团公司简介
  • 境外社交网站上做推广做外贸网站的公司
  • flash网站建设个人简介网站排名查询站长之家
  • saas平台是什么意思优化网站性能监测
  • 深圳微交易网站开发官方正版清理优化工具
  • 电子商务网站建设基础项目实训报告怎么去做网络推广
  • 做大型网站建设网站 软件
  • 职友集 一家做职业点评的网站最好用的免费建站
  • 广州开发网站哪家专业百度推广登录手机版
  • 用邮箱找回智慧团建密码seo网站seo
  • 随州市住房和城乡建设委员会网站市场调研方法有哪几种
  • 招聘网站代做网站关键词优化工具
  • 做网站的价格 外贸seo运营推广
  • 站内推广的几种方式病毒式营销案例
  • 中国建设银行手机银行下载官方网站营销网站建设选择
  • 网站建设手机端郑州seo推广外包
  • wordpress.商品武汉seo首页
  • 网站建设收费标准营销型网站内容
  • 朝鲜族做的电影网站百度推广热线电话
  • 个人工作室网页设计模板优化搜索关键词
  • 郑州专业做网站多少钱深圳做网站公司
  • 商机互联公司做网站怎么样谷歌seo优化公司
  • 国内外做的比较好的家装网站网站安全检测工具
  • 谁有手机网站店铺推广软文案例
  • 返佣网站都是自己做的黄页引流推广