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

手机咋建网站vs2012怎么做网站

手机咋建网站,vs2012怎么做网站,区块链开发平台,网站框架是什么目录 前言阅读准备阅读指引阅读建议 课程内容一、生成BeanDefinition1.1 简单回顾*1.2 概念回顾1.3 核心方法讲解 二、方法讲解2.1 ClassPathBeanDefinitionScanner#scan2.2 ClassPathBeanDefinitionScanner#doScan2.3 ClassPathScanningCandidateComponentProvider#findCandid… 目录 前言阅读准备阅读指引阅读建议 课程内容一、生成BeanDefinition1.1 简单回顾*1.2 概念回顾1.3 核心方法讲解 二、方法讲解2.1 ClassPathBeanDefinitionScanner#scan2.2 ClassPathBeanDefinitionScanner#doScan2.3 ClassPathScanningCandidateComponentProvider#findCandidateComponents2.4 ClassPathScanningCandidateComponentProvider#scanCandidateComponents2.5 ClassPathScanningCandidateComponentProvider#isCandidateComponent2.6 ClassPathScanningCandidateComponentProvider#isCandidateComponent2.7 ClassPathBeanDefinitionScanner#postProcessBeanDefinition2.8 AnnotationConfigUtils.processCommonDefinitionAnnotations2.9 ClassPathBeanDefinitionScanner#checkCandidate2.10 DefaultListableBeanFactory#registerBeanDefinition 三、扫描逻辑流程图四、合并BeanDefinition 学习总结 前言 阅读准备 由于Spring源码分析是一个前后联系比较强的过程而且这边分析也是按照代码顺序讲解的所以不了解前置知识的情况下大概率没办法看懂当前的内容。所以特别推荐看看我前面的文章自上而下次序 Spring底层核心原理解析——引导篇【学习难度★★☆☆☆】手写简易Spring容器过程分析——引导篇【学习难度★★☆☆☆】Spring之底层架构核心概念解析【学习难度★★★☆☆重要程度★★★★★】Bean的生命周期流程图【学习难度☆☆☆☆☆重要程度★★★★★】 PS特别是《Bean的生命周期流程图》帮大家【开天眼】先了解下流程。毕竟【通过业务了解代码远比通过代码了解业务简单的多】 PS特别是《Bean的生命周期流程图》帮大家【开天眼】先了解下流程。毕竟【通过业务了解代码远比通过代码了解业务简单的多】 PS特别是《Bean的生命周期流程图》帮大家【开天眼】先了解下流程。毕竟【通过业务了解代码远比通过代码了解业务简单的多】 阅读指引 Spring最重要的功能就是帮助程序员创建对象也就是IOC而启动Spring就是为创建Bean对象做准备所以我们先明白Spring到底是怎么去创建Bean的也就是先弄明白Bean的生命周期。 Bean的生命周期就是指在Spring中一个Bean是如何生成的如何销毁的。 本节课的内容将会以下面这段代码为入口讲解 public class MyApplicationTest {public static void main(String[] args) {AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(org.tuling.spring);Object newUser context.getBean(newUser);System.out.println(newUser);} }主要用到的是如下Spring容器的构造方法 /*** 创建一个新的AnnotationConfigApplicationContext扫描给定包中的组件为这些组件注册bean定义并自动刷新上下文。* 参数:* baseppackages——要扫描组件类的包*/ public AnnotationConfigApplicationContext(String... basePackages) {this();scan(basePackages);refresh(); }阅读建议 看源码切记纠结细枝末节不然很容易陷进去。正常来说看主要流程就好了遇到不懂的多看看类注释或者方法注释。Spring这种优秀源码注释真的非常到位如果你是idea用户多用F11的书签功能。 Ctrl F11 选中文件 / 文件夹使用助记符设定 / 取消书签 必备Shift F11 弹出书签显示层 必备Ctrl 1,2,3…9 定位到对应数值的书签位置 必备 课程内容 一、生成BeanDefinition 1.1 简单回顾 如果大家看过我前面的文章应该会知道BeanDefiniton的生成是在生产Bean之前的【扫描】阶段。如下图所示 不过咱也说过这只是简单实现而已事实上Spring实现这个扫描过程涉及到了3个核心类10个核心方法 *1.2 概念回顾 在这个【扫描】过程中涉及到了一些Spring底层设计的概念我在上一个笔记里面有大概介绍过不记得的同学记得回去翻一翻。 主要涉及的概念有 BeanDefinition设计图纸BeanDefinition表示Bean定义BeanDefinition中存在很多属性用来描述一个Bean的特征ClassPathBeanDefinitionScanner图纸注册器用于注册图纸BeanFacotoryBean工厂生产Bean。不过实际上这里用到DefaultListableBeanFactory这个类的BeanDefinitionRegistry接口的注册能力 文章链接 《【Spring专题】Spring之底层架构核心概念解析》 1.3 核心方法讲解 我们在前面提到过整个扫描流程会涉及到【3个核心类10个核心方法】。下面我们将会按照调用次序依次讲解各个方法。 二、方法讲解 整个扫描流程的入口是下面代码中的scan()方法而最终调用的是ClassPathBeanDefinitionScanner.scan() /*** 创建一个新的AnnotationConfigApplicationContext扫描给定包中的组件为这些组件注册bean定义并自动刷新上下文。* 参数:* baseppackages——要扫描组件类的包*/ public AnnotationConfigApplicationContext(String... basePackages) {this();scan(basePackages);refresh(); }public void scan(String... basePackages) {Assert.notEmpty(basePackages, At least one base package must be specified);StartupStep scanPackages this.getApplicationStartup().start(spring.context.base-packages.scan).tag(packages, () - Arrays.toString(basePackages));this.scanner.scan(basePackages);scanPackages.end(); }2.1 ClassPathBeanDefinitionScanner#scan 全路径org.springframework.context.annotation.ClassPathBeanDefinitionScanner#scan 方法注释在指定的基本包中执行扫描 源码如下 public int scan(String... basePackages) {int beanCountAtScanStart this.registry.getBeanDefinitionCount();doScan(basePackages);// Register annotation config processors, if necessary.if (this.includeAnnotationConfig) {AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);}return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);} 但其实真正干活的还不是这里而是里面的doScan() 2.2 ClassPathBeanDefinitionScanner#doScan 方法调用链由 2.1中的scan()调用进来 全路径org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan 方法注释在指定的基包中执行扫描返回已注册的bean定义。此方法不注册注释配置处理程序而是将此工作留给调用者。 源码如下 protected SetBeanDefinitionHolder doScan(String... basePackages) {Assert.notEmpty(basePackages, At least one base package must be specified);SetBeanDefinitionHolder beanDefinitions new LinkedHashSet();for (String basePackage : basePackages) {// 寻找候选的BeanDefinitionSetBeanDefinition candidates findCandidateComponents(basePackage);for (BeanDefinition candidate : candidates) {ScopeMetadata scopeMetadata this.scopeMetadataResolver.resolveScopeMetadata(candidate);candidate.setScope(scopeMetadata.getScopeName());String beanName this.beanNameGenerator.generateBeanName(candidate, this.registry);// 判断是否要设置BeanDefinition属性if (candidate instanceof AbstractBeanDefinition) {postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);}// 判断是否要设置通用的注解BeanDefiniton属性if (candidate instanceof AnnotatedBeanDefinition) {AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);}// 判断是否注册BeanDefinitionif (checkCandidate(beanName, candidate)) {BeanDefinitionHolder definitionHolder new BeanDefinitionHolder(candidate, beanName);definitionHolder AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);beanDefinitions.add(definitionHolder);registerBeanDefinition(definitionHolder, this.registry);}}}return beanDefinitions;}方法解读这个代码看似很多其实整体来说比较简单分为4个步骤 先扫描classpath下所有的class寻找候选的BeanDefinition。至于什么是候选的后面会讲解遍历找到的候选BeanDefinition判断是否需要设置BeanDefinition的一些属性。比如默认属性等判断是否要设置通用的注解BeanDefiniton属性判断是否要注册BeanDefinition毕竟可能出现重复的BeanDefinition 2.3 ClassPathScanningCandidateComponentProvider#findCandidateComponents 方法调用链由 2.2中的doScan()调用进来 全路径org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#findCandidateComponents 方法注释扫描类路径上的候选Component。 源码如下 public SetBeanDefinition findCandidateComponents(String basePackage) {if (this.componentsIndex ! null indexSupportsIncludeFilters()) {return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);}else {return scanCandidateComponents(basePackage);}} 方法解读这里有一个if-else但通常都是进入else。if里面的情况我们在业务开发中基本不会用到这是一个为了加快扫描包的策略通过使用索引思想。怎么实现呢就是在resource下新建一个spring.components配置文件K-V格式。如下 org.tuling.spring.bean.User org.springframework.stereotype.Component通过直接告诉Spring哪些是候选的Component免除了扫描所有class文件筛选的过程从而提升了效率。 2.4 ClassPathScanningCandidateComponentProvider#scanCandidateComponents 方法调用链由 2.3中的findCandidateComponents()调用进来 全路径org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandidateComponents 方法注释扫描类路径上的候选Component。 源码如下 private Setorg.springframework.beans.factory.config.BeanDefinition scanCandidateComponents(String basePackage) {SetBeanDefinition candidates new LinkedHashSet();try {String packageSearchPath ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX resolveBasePackage(basePackage) / this.resourcePattern;Resource[] resources getResourcePatternResolver().getResources(packageSearchPath);boolean traceEnabled logger.isTraceEnabled();boolean debugEnabled logger.isDebugEnabled();for (Resource resource : resources) {if (traceEnabled) {logger.trace(Scanning resource);}try {// 读取类元信息MetadataReader metadataReader getMetadataReaderFactory().getMetadataReader(resource);// 第一层判断是否为候选组件判断核心为是否需要过滤if (isCandidateComponent(metadataReader)) {ScannedGenericBeanDefinition sbd new ScannedGenericBeanDefinition(metadataReader);sbd.setSource(resource);// 第二层判断是否为候选组件判断核心为是否独立、抽象类等if (isCandidateComponent(sbd)) {if (debugEnabled) {logger.debug(Identified candidate component class: resource);}candidates.add(sbd);}else {if (debugEnabled) {logger.debug(Ignored because not a concrete top-level class: resource);}}}else {if (traceEnabled) {logger.trace(Ignored because not matching any filter: resource);}}}catch (FileNotFoundException ex) {if (traceEnabled) {logger.trace(Ignored non-readable resource : ex.getMessage());}}catch (Throwable ex) {throw new BeanDefinitionStoreException(Failed to read candidate component class: resource, ex);}}}catch (IOException ex) {throw new BeanDefinitionStoreException(I/O failure during classpath scanning, ex);}return candidates;}方法解读代码看着很长实际上很简单。主要分为4个步骤 读取类元信息从磁盘中读取 首先通过ResourcePatternResolver获得指定包路径下的所有.class文件Spring源码中将此文件包装成了Resource对象遍历每个Resource对象利用MetadataReaderFactory解析Resource对象得到MetadataReader在Spring源码中MetadataReaderFactory具体的实现类为CachingMetadataReaderFactoryMetadataReader的具体实现类为SimpleMetadataReader 判断是否为候选组件判断核心为是否需要过滤第一个isCandidateComponent 利用MetadataReader进行excludeFilters和includeFilters以及条件注解Conditional的筛选条件注解并不能理解某个类上是否存在Conditional注解如果存在则调用注解中所指定的类的match方法进行匹配匹配成功则通过筛选匹配失败则pass掉。筛选通过后基于metadataReader生成ScannedGenericBeanDefinition 判断是否为候选组件判断核心为是否独立、抽象类等。第二个isCandidateComponent注意他跟上面的isCandidateComponent不是同一个东西属于方法重载如果筛选通过那么就表示扫描到了一个Bean将ScannedGenericBeanDefinition加入结果集 MetadataReader表示类的元数据读取器主要包含了一个AnnotationMetadata功能有 获取类的名字、获取父类的名字获取所实现的所有接口名获取所有内部类的名字判断是不是抽象类判断是不是接口判断是不是一个注解获取拥有某个注解的方法集合获取类上添加的所有注解信息获取类上添加的所有注解类型集合 2.5 ClassPathScanningCandidateComponentProvider#isCandidateComponent 方法调用链由 2.4中的scanCandidateComponents()调用进来 全路径org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#isCandidateComponent 方法注释确定给定的类是否不匹配任何排除筛选器而匹配至少一个包含筛选器。 源码如下 protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {for (TypeFilter tf : this.excludeFilters) {if (tf.match(metadataReader, getMetadataReaderFactory())) {return false;}}for (TypeFilter tf : this.includeFilters) {if (tf.match(metadataReader, getMetadataReaderFactory())) {return isConditionMatch(metadataReader);}}return false;}方法解读这里只是校验当前类是否在过滤器排除选项以内。我估计很多朋友想到的都是ComponentScan注解里面的includeFilter跟excludeFilter属性。只能说不完全正确。 因为在我们启动容器创建ClassPathBeanDefinitionScanner也会添加默认的过滤策略。如下所示 /*** 注册Component的默认过滤器。* 这将隐式注册所有带有Component元注释的注释包括Repository、Service和Controller构造型注释。* 还支持Java EE 6的javax.annotation.ManagedBean和JSR-330的javax.inject.Named注释(如果可用)。*/ protected void registerDefaultFilters() {this.includeFilters.add(new AnnotationTypeFilter(Component.class));ClassLoader cl ClassPathScanningCandidateComponentProvider.class.getClassLoader();try {this.includeFilters.add(new AnnotationTypeFilter(((Class? extends Annotation) ClassUtils.forName(javax.annotation.ManagedBean, cl)), false));logger.trace(JSR-250 javax.annotation.ManagedBean found and supported for component scanning);}catch (ClassNotFoundException ex) {// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.}try {this.includeFilters.add(new AnnotationTypeFilter(((Class? extends Annotation) ClassUtils.forName(javax.inject.Named, cl)), false));logger.trace(JSR-330 javax.inject.Named annotation found and supported for component scanning);}catch (ClassNotFoundException ex) {// JSR-330 API not available - simply skip.} }2.6 ClassPathScanningCandidateComponentProvider#isCandidateComponent 方法调用链由 2.4中的scanCandidateComponents()调用进来 全路径org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#isCandidateComponent 方法注释确定给定的bean定义是否符合候选条件。默认实现检查类是否不是接口是否依赖于封闭类。 源码如下 /***确定给定的bean定义是否符合候选条件。* 默认实现检查类是否不是接口是否依赖于封闭类。* 可以在子类中重写。*/ protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {AnnotationMetadata metadata beanDefinition.getMetadata();return (metadata.isIndependent() (metadata.isConcrete() ||(metadata.isAbstract() metadata.hasAnnotatedMethods(Lookup.class.getName())))); }方法解读这里有2个比较陌生的那就是什么是独立类以及Lookup注解。 独立类及不是普通内部类静态内部类是独立类Lookup注解自己去百度使用方法 总结方法判断是候选Component的条件是 首先是独立类要么是具体的子类要么就是有Lookup注解的抽象类 2.7 ClassPathBeanDefinitionScanner#postProcessBeanDefinition 方法调用链由 2.2中的doScan()调用进来 全路径org.springframework.context.annotation.ClassPathBeanDefinitionScanner#postProcessBeanDefinition 方法注释扫描类路径上的候选Component。 源码如下 /*** 除了通过扫描组件类检索到的内容之外还可以对给定的bean定义应用进一步的设置。*/ protected void postProcessBeanDefinition(AbstractBeanDefinition beanDefinition, String beanName) {beanDefinition.applyDefaults(this.beanDefinitionDefaults);if (this.autowireCandidatePatterns ! null) {beanDefinition.setAutowireCandidate(PatternMatchUtils.simpleMatch(this.autowireCandidatePatterns, beanName));}}方法解读这里主要还初始化了BeanDefinition一些默认属性beanDefinition.applyDefaults(this.beanDefinitionDefaults); 源码如下 public void applyDefaults(BeanDefinitionDefaults defaults) {Boolean lazyInit defaults.getLazyInit();if (lazyInit ! null) {setLazyInit(lazyInit);}setAutowireMode(defaults.getAutowireMode());setDependencyCheck(defaults.getDependencyCheck());setInitMethodName(defaults.getInitMethodName());setEnforceInitMethod(false);setDestroyMethodName(defaults.getDestroyMethodName());setEnforceDestroyMethod(false);}2.8 AnnotationConfigUtils.processCommonDefinitionAnnotations 方法调用链由 2.2中的doScan()调用进来 全路径org.springframework.context.annotation.AnnotationConfigUtils#processCommonDefinitionAnnotations 方法注释根据注解累类元信息设置BeanDefinition注解相关属性。 源码如下 public static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) {processCommonDefinitionAnnotations(abd, abd.getMetadata()); }static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {AnnotationAttributes lazy attributesFor(metadata, Lazy.class);if (lazy ! null) {abd.setLazyInit(lazy.getBoolean(value));}else if (abd.getMetadata() ! metadata) {lazy attributesFor(abd.getMetadata(), Lazy.class);if (lazy ! null) {abd.setLazyInit(lazy.getBoolean(value));}}if (metadata.isAnnotated(Primary.class.getName())) {abd.setPrimary(true);}AnnotationAttributes dependsOn attributesFor(metadata, DependsOn.class);if (dependsOn ! null) {abd.setDependsOn(dependsOn.getStringArray(value));}AnnotationAttributes role attributesFor(metadata, Role.class);if (role ! null) {abd.setRole(role.getNumber(value).intValue());}AnnotationAttributes description attributesFor(metadata, Description.class);if (description ! null) {abd.setDescription(description.getString(value));}}没啥好解读的很简单的判断 2.9 ClassPathBeanDefinitionScanner#checkCandidate 方法调用链由 2.2中的doScan()调用进来 全路径org.springframework.context.annotation.ClassPathBeanDefinitionScanner#checkCandidate 方法注释检查给定的候选bean名称确定是否需要注册相应的bean定义或者是否与现有定义冲突。 源码如下 protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {if (!this.registry.containsBeanDefinition(beanName)) {return true;}BeanDefinition existingDef this.registry.getBeanDefinition(beanName);BeanDefinition originatingDef existingDef.getOriginatingBeanDefinition();if (originatingDef ! null) {existingDef originatingDef;}if (isCompatible(beanDefinition, existingDef)) {return false;}throw new ConflictingBeanDefinitionException(Annotation-specified bean name beanName for bean class [ beanDefinition.getBeanClassName() ] conflicts with existing, non-compatible bean definition of same name and class [ existingDef.getBeanClassName() ]);}方法解读这里正常来说要么返回true要么就是重复定义相同名字的Bean然后抛出异常。直接返回false通常是存在父子容器的情况下。 2.10 DefaultListableBeanFactory#registerBeanDefinition 方法调用链由 2.2中的doScan()调用进来 全路径org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition 方法注释用这个注册中心注册一个新的bean定义。必须支持RootBeanDefinition和ChildBeanDefinition。 源码如下 Overridepublic void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)throws BeanDefinitionStoreException {Assert.hasText(beanName, Bean name must not be empty);Assert.notNull(beanDefinition, BeanDefinition must not be null);if (beanDefinition instanceof AbstractBeanDefinition) {try {((AbstractBeanDefinition) beanDefinition).validate();}catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,Validation of bean definition failed, ex);}}BeanDefinition existingDefinition this.beanDefinitionMap.get(beanName);if (existingDefinition ! null) {if (!isAllowBeanDefinitionOverriding()) {throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);}else if (existingDefinition.getRole() beanDefinition.getRole()) {// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTUREif (logger.isInfoEnabled()) {logger.info(Overriding user-defined bean definition for bean beanName with a framework-generated bean definition: replacing [ existingDefinition ] with [ beanDefinition ]);}}else if (!beanDefinition.equals(existingDefinition)) {if (logger.isDebugEnabled()) {logger.debug(Overriding bean definition for bean beanName with a different definition: replacing [ existingDefinition ] with [ beanDefinition ]);}}else {if (logger.isTraceEnabled()) {logger.trace(Overriding bean definition for bean beanName with an equivalent definition: replacing [ existingDefinition ] with [ beanDefinition ]);}}this.beanDefinitionMap.put(beanName, beanDefinition);}else {if (hasBeanCreationStarted()) {// Cannot modify startup-time collection elements anymore (for stable iteration)synchronized (this.beanDefinitionMap) {this.beanDefinitionMap.put(beanName, beanDefinition);ListString updatedDefinitions new ArrayList(this.beanDefinitionNames.size() 1);updatedDefinitions.addAll(this.beanDefinitionNames);updatedDefinitions.add(beanName);this.beanDefinitionNames updatedDefinitions;removeManualSingletonName(beanName);}}else {// Still in startup registration phasethis.beanDefinitionMap.put(beanName, beanDefinition);this.beanDefinitionNames.add(beanName);removeManualSingletonName(beanName);}this.frozenBeanDefinitionNames null;}if (existingDefinition ! null || containsSingleton(beanName)) {resetBeanDefinition(beanName);}else if (isConfigurationFrozen()) {clearByTypeCache();}}方法解读这个就稍微复杂一点了我也没细看。而且这个隐藏的挺深的虽然是从doScan()里面进调用的但是本质上是调用AnnotationConfigApplicationContext继承自GenericApplicationContext的registerBeanDefinition方法 三、扫描逻辑流程图 四、合并BeanDefinition 到了后面的IOC过程生成Bean的时候我们会看到很多这样的调用 RootBeanDefinition bd getMergedLocalBeanDefinition(beanName);这个就是所谓的获取合并BeanDefinition。什么是合并BeanDefinition其实就是父子BeanDefinition。跟Java里面的继承是一样一样的。但是这个通常发生在xml配置bean里面如下 bean idparent classcom.zhouyu.service.Parent scopeprototype/ bean idchild classcom.zhouyu.service.Child/但是这么定义的情况下child就是原型Bean了。因为child的父BeanDefinition是parent所以会继承parent上所定义的scope属性。而在根据child来生成Bean对象之前需要进行BeanDefinition的合并得到完整的child的BeanDefinition。 学习总结 学习了Spring源码流程中的【扫描】底层原理
http://www.w-s-a.com/news/542127/

相关文章:

  • 网站建设在开封找谁做wordpress 数据转换
  • 旅游网站开发的流程江苏付费网络推广培训
  • 网站软文标题2018wordpress主题
  • 德清网站设计wordpress免登录发布接
  • 可以做游戏的网站有哪些客户关系管理系统的主要功能
  • 整人关不掉的网站怎么做广东省网站免备案表
  • 网站设计素材edu域名网站
  • 中山学校的网站建设wordpress文章图片显示不出
  • 兰溪城市建设规划网站网站联盟的基本流程
  • 免费推广网站注册入口小说阅读网站怎么建设
  • 新网站怎么做网络推广怎么做企业网站排名
  • jsp商业网站开发网站链接如何做二维码
  • 江苏高校品牌专业建设网站怎么制作网站搜索窗口
  • 北京app建设 网站开发公司织梦网站seo
  • 大学网站 作风建设专题汽车配件外贸出口公司
  • 东莞做网站系统购物网站建设精英
  • 建设vip网站相关视频网站营销建设公司
  • 微站直播平台杭州seo按天计费
  • seo 新旧网站 两个域名福州设计网站建设
  • 如何做网站客户端如何做网络营销网站
  • 苏州网站建设制度打鱼网站建设
  • 瓜子二手车直卖网上海小红书seo
  • 天津中小企业网站制作珠海做网站的
  • 网站排名影响因素最牛的科技网站建设
  • 长春网站建设公司怎么样电商网站建设与开发期末考试
  • 品牌网站建设搭建国内外网站建设
  • 辽宁人社app一直更新整站seo定制
  • 兰州网站建设论坛装修品牌
  • 云南省城乡住房与建设厅网站用什么网站可以做电子书
  • 自己电脑怎么做网站服务器吗0基础如何做网站