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

服务器搭建网站数据库网络平台推广方式

服务器搭建网站数据库,网络平台推广方式,WordPress 网店 主体,小米发布会直播在线观看背景 本文作为SpringBoot系列的开篇,介绍SpringBoot的启动流程,包括Spring容器和Tomcat启动过程。SpringBoot作为流行的微服务框架,其是基于约定和自动装配机制对Spring的封装和增强。 由于前面的Spring系列对Spring容器已经进行了较为细致的…

背景

本文作为SpringBoot系列的开篇,介绍SpringBoot的启动流程,包括Spring容器和Tomcat启动过程。SpringBoot作为流行的微服务框架,其是基于约定和自动装配机制对Spring的封装和增强。

由于前面的Spring系列对Spring容器已经进行了较为细致的梳理,相同内容不进行重复说明。

1.案例

添加SpringBoot和web依赖:

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.2.RELEASE</version>
</parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
</dependencies>

启动类:

@SpringBootApplication
// 标注[1]
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}
}

Note:
按规范,一般将main方法所在的类命名为artifactId+Application.

2.启动过程

跟踪SpringApplication.run(DemoApplication.class, args):

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {return new SpringApplication(primarySources).run(args);
}

从逻辑上可以拆分为两个部分:构造SpringApplication对象,调用SpringApplication对象的run方法.

2.1 构造SpringApplication对象

new SpringApplication(primarySources), primarySources参数为传入的DemoApplication.class对象;
说明:传入的primarySources不一定为main方法所在类,只需要保证为@SpringBootApplication注解的类即可。
经过public SpringApplication(Class<?>… primarySources) {this(null, primarySources);}进入:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {// resourceLoader为null;this.resourceLoader = resourceLoader;// 主配置类,此时为DemoApplication.classthis.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));// web类型为SERVLETthis.webApplicationType = WebApplicationType.deduceFromClasspath();// 使用SPI机制加载ApplicationContextInitializer和ApplicationListener类型的对象,并保存到initializers和listeners属性中;[标注1]setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));// 推算出main方法所在类(通过构造异常对象获取), 此时为DemoApplication.classthis.mainApplicationClass = deduceMainApplicationClass();
}

Note:
getSpringFactoriesInstances方法逻辑如下:

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {ClassLoader classLoader = getClassLoader();// 从spring.factories文件中加载指定名称的类型,此时为ApplicationContextInitializer和ApplicationListenerSet<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));// 通过反射创建对象List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);// 排序(继承了Ordered接口的使用getOrder获取,使用@Order注解的根据注解的值)AnnotationAwareOrderComparator.sort(instances);return instances;
}

ApplicationContextInitializer包括:

// spring-boot包中的spring.factories文件:
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer// spring-boot-autoconfiguration包中的spring.factories文件:
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

ApplicationListener包括:

// spring-boot包中的spring.factories文件:
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener// spring-boot-autoconfiguration包中的spring.factories文件:
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

2.2 执行run方法

删除计时、Banner打印、日志打印、异常逻辑后:

public ConfigurableApplicationContext run(String... args) {// 对java.awt.headless参数设置[Ignore]this.configureHeadlessProperty();// 使用SPI机制获取SpringApplicationRunListeners,// 内部仅包含一个EventPublishingRunListener类型的监听器SpringApplicationRunListeners listeners = this.getRunListeners(args);listeners.starting();// 对启动方法的入参进行包装ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);// 创建环境变量 [标注1]ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);// spring.beaninfo.ignore属性设置[Ignore]this.configureIgnoreBeanInfo(environment);// 根据Servlet类型创建Spring容器 [标注2]ConfigurableApplicationContext context = this.createApplicationContext();// 刷新前的预处理 [标注3]this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);// 刷新容器 标注[4]this.refreshContext(context);// 容器刷新后处理,预留扩展接口,此时逻辑为空this.afterRefresh(context, applicationArguments);listeners.started(context);// 调用run方法 [标注5]this.callRunners(context, applicationArguments);// 返回AnnotationConfigServletWebServerApplicationContext类型的Spring容器return context;}

Note1: 创建环境变量

prepareEnvironment方法用户构造环境变量,类型为StandardServletEnvironment,包括:
[1] systemProperties保存系统属性,如java.version, user.name;
[2] systemEnvironment保存环境变量,如JAVA_HOME, GRADLE_HOME;
[3] applicationConfig保存application.yml配置文件信息 …

Note2: 创建Spring容器

protected ConfigurableApplicationContext createApplicationContext() {
// 省略部分代码 ...switch (this.webApplicationType) {case SERVLET:contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");break;case REACTIVE:contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");break;default:contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");}return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
}

webApplicationTypeSERVLET类型,通过反射构造一个AnnotationConfigServletWebServerApplicationContext类型的对象返回。

Note3: 刷新前的预处理

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {// 为Spring容器设置环境变量context.setEnvironment(environment);// 对Spring容器的beanNameGenerator(默认启动流程为空)、resourceLoader(默认启动流程为空)、addConversionService组件进行设置postProcessApplicationContext(context);// 调用initializers的initialize方法,在new SpringApplication阶段通过SPI获取的ApplicationContextInitializer对象列表applyInitializers(context);listeners.contextPrepared(context);ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();// 将启动入参和printedBanner注册到IOC中beanFactory.registerSingleton("springApplicationArguments", applicationArguments);if (printedBanner != null) {beanFactory.registerSingleton("springBootBanner", printedBanner);}// SpringBoot不支持循环依赖if (beanFactory instanceof DefaultListableBeanFactory) {((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);}// lazyInitialization为false, 不进行懒加载(LazyInitializationBeanFactoryPostProcessor会将容器中的BeanDefinition的lazyInit属性设置为true,从而实现懒加载)if (this.lazyInitialization) {context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());}// 向容器导入主配置类,此时为DemoApplication[标注3-1]Set<Object> sources = getAllSources();load(context, sources.toArray(new Object[0]));listeners.contextLoaded(context);
}

跟踪load方法进入:

class BeanDefinitionLoader {// ...private int load(Class<?> source) {// ...this.annotatedReader.register(source);}
}
public class AnnotatedBeanDefinitionReader {// ...public void registerBean(Class<?> beanClass) {doRegisterBean(beanClass, null, null, null, null);}
}

doRegisterBean方法表示向IOC容器中注册一个beanClass类型的对象,此时为DemoApplication. registerBean方法实现逻辑的解读是Spring导入Bean对象的内容,本文不进行深究。

Note4: 刷新容器

this.refreshContext(context);内容如下

private void refreshContext(ConfigurableApplicationContext context) {this.refresh((ApplicationContext)context);if (this.registerShutdownHook) {try {context.registerShutdownHook();} catch (AccessControlException var3) {}}
}

逻辑可以分为两个部分:
调用this.refresh((ApplicationContext)context);刷新容器,调用context.registerShutdownHook()注册关闭时的勾子函数。context.registerShutdownHook()是通过Runtime.getRuntime().addShutdownHook方法向JVM注册钩子函数,在当JVM关闭时执行。
这里需要执行的逻辑包括:注销Bean、关闭Bean工厂、清理缓存等。

进入refresh方法:

public void refresh() throws BeansException, IllegalStateException {
//... 	省略部分与主体逻辑无关的代码this.prepareRefresh();ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();this.prepareBeanFactory(beanFactory);this.postProcessBeanFactory(beanFactory);this.invokeBeanFactoryPostProcessors(beanFactory);this.registerBeanPostProcessors(beanFactory);this.initMessageSource();this.initApplicationEventMulticaster();this.onRefresh();this.registerListeners();this.finishBeanFactoryInitialization(beanFactory);this.finishRefresh();
}

上述流程在 Spring系列-1 启动流程 中以及进行介绍过, 重复内容这里不进行说明。
此处刷新的Spring容器类型为AnnotationConfigServletWebServerApplicationContext,重写了onRefresh方法:

protected void onRefresh() {// 	空方法super.onRefresh();try {this.createWebServer();} catch (Throwable var2) {throw new ApplicationContextException("Unable to start web server", var2);}
}

即onRefresh()的核心逻辑在this.createWebServer()方法中,该方法用于创建并启动web容器,逻辑如下:

private void createWebServer() {// this.webServer为nullWebServer webServer = this.webServer;// servletContext为nullServletContext servletContext = getServletContext();if (webServer == null && servletContext == null) {//获取web容器工厂 [标注4-1]ServletWebServerFactory factory = getWebServerFactory();// 构造web容器 [标注4-2]this.webServer = factory.getWebServer(getSelfInitializer());// 向IOC注册webServerGracefulShutdown和webServerStartStop对象 [标注4-3]getBeanFactory().registerSingleton("webServerGracefulShutdown",new WebServerGracefulShutdownLifecycle(this.webServer));getBeanFactory().registerSingleton("webServerStartStop",new WebServerStartStopLifecycle(this, this.webServer));} else if (servletContext != null) {// Ignore...}//占位符替换:替换环境变量中名称"servletContextInitParams"的资源->容器对象initPropertySources();
}

Note 4-1getWebServerFactory()用于获取容器工厂
spring-boot-autoconfigure包中定义了ServletWebServerFactoryConfiguration配置类,定义了TomcatJettyUndertow容器工厂配置类:

@Configuration(proxyBeanMethods = false)
class ServletWebServerFactoryConfiguration {@Configuration(proxyBeanMethods = false)@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)static class EmbeddedTomcat {@BeanTomcatServletWebServerFactory tomcatServletWebServerFactory(...) {TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();//...return factory;}}@Configuration(proxyBeanMethods = false)@ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)static class EmbeddedJetty {@BeanJettyServletWebServerFactory JettyServletWebServerFactory(...) {JettyServletWebServerFactory factory = new JettyServletWebServerFactory();//...return factory;}}//...
}

其中EmbeddedTomcat的注入条件是IOC容器中还没有注入ServletWebServerFactory对象,以及类路径中存在Tomcat.class类;而Tomcat.class以及Tomcat相关依赖定义在spring-boot-starter-tomcat包中,在引入spring-boot-starter-web依赖时,会自动引入spring-boot-starter-tomcat依赖,即springboot默认使用Tomcat容器。

SpringBoot也可以使用Jetty容器启动,pom依赖需要进行调整,删除spring-boot-starter-web中引入的tomcat依赖,并引入jetty依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId></exclusion></exclusions>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jetty</artifactId><version>2.6.4</version>
</dependency>

本文后续以SpringBoot默认的Tomcat为web容器进行介绍。

Note 4-2: this.webServer = factory.getWebServer(getSelfInitializer())构造web容器
该步骤包含的内容较多,可以拆成两个部分:
(1) 参数部分:是一个lambda表达式,将被作为参数传入到Tomcat类中,在Tomcat启动时被调用;
(2) 函数部分:负责构造并启动web容器。

参数部分:

private void selfInitialize(ServletContext servletContext) throws ServletException {// prepareWebApplicationContext用于实现web容器上下文(ApplicationContextFacade类型)与Spring容器相互指向prepareWebApplicationContext(servletContext);// 将servletContext包装成ServletContextScope对象,作为Scope对象添加到容器的中以及设置到servletContext的application属性registerApplicationScope(servletContext);WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);for (ServletContextInitializer beans : getServletContextInitializerBeans()) {beans.onStartup(servletContext);}}

WebApplicationContextUtils.registerEnvironmentBeans方法将servletContext以"servletContext"为BeanName注册到IOC中,并从servletContext中获取InitParameter和Attribute信息分别以"contextParameters"和""为beanName注册到IOC中。 getServletContextInitializerBeans()方法从IOC中获取ServletContextInitializer、Filter、Servlet对象, 封装为RegistrationBean对象,并调用这些对象的onStartup方法。以DispatcherServletRegistrationBean为例进行介绍:

 public final void onStartup(ServletContext servletContext) throws ServletException {// "servlet dispatcherServlet"String description = getDescription();register(description, servletContext);}register(description, servletContext)方法包含两个主要步骤addRegistration和configure:protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {// "dispatcherServlet"String name = getServletName();// this.servlet就是DispatcherServlet对象,将其注册到serverContext容器上下文中return servletContext.addServlet(name, this.servlet);}protected void configure(ServletRegistration.Dynamic registration) {super.configure(registration);String[] urlMapping = StringUtils.toStringArray(this.urlMappings);if (urlMapping.length == 0 && this.alwaysMapUrl) {urlMapping = DEFAULT_MAPPINGS;}// 注册urlMapping, 默认是 "/"if (!ObjectUtils.isEmpty(urlMapping)) {registration.addMapping(urlMapping);}// 设置loadOnStartup, 默认是-1,表示懒加载registration.setLoadOnStartup(this.loadOnStartup);if (this.multipartConfig != null) {registration.setMultipartConfig(this.multipartConfig);}}

继续进入factory.getWebServer(getSelfInitializer())方法:

public WebServer getWebServer(ServletContextInitializer... initializers) {if (this.disableMBeanRegistry) {Registry.disableRegistry();} // Ignore// 创建Tomcat对象,并设置service、connector、engine、Host// 这个Tomcat来自org.apache.catalina.startup包Tomcat tomcat = new Tomcat();File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");tomcat.setBaseDir(baseDir.getAbsolutePath());Connector connector = new Connector(this.protocol);connector.setThrowOnFailure(true);tomcat.getService().addConnector(connector);customizeConnector(connector);tomcat.setConnector(connector);tomcat.getHost().setAutoDeploy(false);configureEngine(tomcat.getEngine());for (Connector additionalConnector : this.additionalTomcatConnectors) {tomcat.getService().addConnector(additionalConnector);}// 给Host添加一个Context, 该Context被封装后就是前文提到的web容器上下文// initializers属性被保存在Context中, 在tonmcat启动后调用initializers的onStartUp方法(上文涉及的lambda表达式).prepareContext(tomcat.getHost(), initializers);// 构造、启动并返回web容器[标注3]return getTomcatWebServer(tomcat);
}

getTomcatWebServer(tomcat)方法逻辑如下:

protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {new TomcatWebServer(tomcat, getPort() >= 0, Shutdown.IMMEDIATE);
}

其中:getPort()得到的端口来源为Spring配置文件, 因此getPort()>0; Shutdown.IMMEDIATE为枚举变量。

public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {this.tomcat = tomcat;// truethis.autoStart = autoStart;// 传入的Shutdown.IMMEDIATE标志立即关闭(而非优雅关闭),因此this.gracefulShutdown设置为nullthis.gracefulShutdown = (shutdown == Shutdown.GRACEFUL) ? new GracefulShutdown(tomcat) : null;initialize();
}

initialize方法中只有两处逻辑需要注意:

private void initialize() throws WebServerException {// ... // 启动Tomcatthis.tomcat.start();// ... // 开启一个非守护线程,因为Tomcat所有线程为守护线程,否则会直接退出进程startDaemonAwaitThread();
}

Note 4-3:

// 向IOC注册webServerGracefulShutdown和webServerStartStop对象 [标注3]
getBeanFactory().registerSingleton("webServerGracefulShutdown",new WebServerGracefulShutdownLifecycle(this.webServer));
getBeanFactory().registerSingleton("webServerStartStop",new WebServerStartStopLifecycle(this, this.webServer));

将得到的webServer对象进行包装并注册到IOC中。其中: 可通过WebServerGracefulShutdownLifecycle对象优雅地关闭容器,WebServerStartStopLifecycle可以进行容器的启停。优雅关闭指等待所有正在处理的请求完成后再关闭,以确保所有连接都被正确地关闭。

Note5: 执行runner的run方法

this.callRunners(context, applicationArguments);逻辑如下:

private void callRunners(ApplicationContext context, ApplicationArguments args) {List<Object> runners = new ArrayList();// 从IOC容器获取所有的ApplicationRunner类型的Bean对象runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());// 从IOC容器获取所有的CommandLineRunner类型的Bean对象runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());// 按照Ordered接口和@Order注解进行排序AnnotationAwareOrderComparator.sort(runners);// 按顺序调用ApplicationRunner或CommandLineRunner的run方法 [标注5-1]for (Object runner : runners) {if (runner instanceof ApplicationRunner) {this.callRunner((ApplicationRunner)runner, args);}if (runner instanceof CommandLineRunner) {this.callRunner((CommandLineRunner)runner, args);}}
}

Note:5-1
调用ApplicationRunner和CommandLineRunner时,有try-catch异常保护,不会因为某个runner执行异常而影响其他runner执行,传入runner的参数就是容器启动时传给SpringApplication的参数,即main方法的参数。


文章转载自:
http://unalloyed.zfqr.cn
http://rrc.zfqr.cn
http://bethanechol.zfqr.cn
http://tuboplasty.zfqr.cn
http://poetry.zfqr.cn
http://deedless.zfqr.cn
http://recycle.zfqr.cn
http://idiocratically.zfqr.cn
http://minuteness.zfqr.cn
http://jackstaff.zfqr.cn
http://nyp.zfqr.cn
http://tricktrack.zfqr.cn
http://unclutter.zfqr.cn
http://dayworker.zfqr.cn
http://sublate.zfqr.cn
http://haematose.zfqr.cn
http://laryngic.zfqr.cn
http://adrenalize.zfqr.cn
http://bypass.zfqr.cn
http://contract.zfqr.cn
http://tinglass.zfqr.cn
http://breastsummer.zfqr.cn
http://commandress.zfqr.cn
http://cholecalciferol.zfqr.cn
http://expedition.zfqr.cn
http://gratulation.zfqr.cn
http://irdp.zfqr.cn
http://indemnification.zfqr.cn
http://mcmlxxxiv.zfqr.cn
http://indisciplinable.zfqr.cn
http://microanalysis.zfqr.cn
http://formwork.zfqr.cn
http://lacking.zfqr.cn
http://algophobia.zfqr.cn
http://garnet.zfqr.cn
http://deuteronomy.zfqr.cn
http://hobodom.zfqr.cn
http://main.zfqr.cn
http://unc.zfqr.cn
http://analyst.zfqr.cn
http://snuffer.zfqr.cn
http://viscidity.zfqr.cn
http://alcyonarian.zfqr.cn
http://smithsonite.zfqr.cn
http://euclidian.zfqr.cn
http://calcrete.zfqr.cn
http://bason.zfqr.cn
http://indigotin.zfqr.cn
http://scentless.zfqr.cn
http://gentilism.zfqr.cn
http://grumbler.zfqr.cn
http://drainer.zfqr.cn
http://tafia.zfqr.cn
http://tubbish.zfqr.cn
http://photorealism.zfqr.cn
http://vermes.zfqr.cn
http://lmbc.zfqr.cn
http://epigone.zfqr.cn
http://podiatry.zfqr.cn
http://dissolute.zfqr.cn
http://calabar.zfqr.cn
http://portentous.zfqr.cn
http://pancreatectomy.zfqr.cn
http://valorize.zfqr.cn
http://alkalemia.zfqr.cn
http://psychologue.zfqr.cn
http://preexistence.zfqr.cn
http://cayuga.zfqr.cn
http://fluidounce.zfqr.cn
http://tachysterol.zfqr.cn
http://celebes.zfqr.cn
http://papist.zfqr.cn
http://closefisted.zfqr.cn
http://aral.zfqr.cn
http://schlamperei.zfqr.cn
http://mosquito.zfqr.cn
http://mundungus.zfqr.cn
http://brahmaputra.zfqr.cn
http://keelboat.zfqr.cn
http://trenchancy.zfqr.cn
http://halobiont.zfqr.cn
http://voluminousness.zfqr.cn
http://discreetness.zfqr.cn
http://serried.zfqr.cn
http://schoolman.zfqr.cn
http://operetta.zfqr.cn
http://realschule.zfqr.cn
http://epichorial.zfqr.cn
http://basho.zfqr.cn
http://attenuable.zfqr.cn
http://ontario.zfqr.cn
http://micromechanism.zfqr.cn
http://protrudable.zfqr.cn
http://linsang.zfqr.cn
http://precipitantly.zfqr.cn
http://codling.zfqr.cn
http://leukorrhea.zfqr.cn
http://enzyme.zfqr.cn
http://sulfatize.zfqr.cn
http://biociation.zfqr.cn
http://www.hrbkazy.com/news/63461.html

相关文章:

  • 本地网站建设电话东莞seo快速排名
  • 拓者吧室内设计吧官网关键词优化公司如何选择
  • 一个后台管理多个网站中国最新军事新闻
  • 莆田高端模板建站广州网络推广万企在线
  • 怎么做网站接口网站seo推广公司靠谱吗
  • 网站视觉设计规范站长工具seo综合查询烟雨楼
  • 河北沧州网站建设搜狗营销
  • 长乐区住房和城乡建设局网站搜索引擎营销推广方案
  • 公司商标设计logo图案seo网站技术培训
  • 外贸手机商城网站建设 深圳做百度线上推广
  • 8小8x在线免费观看2021罗湖区seo排名
  • 手表东莞网站建设技术支持优化模型的推广
  • 仿win8网站模板seo关键词排名优
  • 南通网站建设技术支持年度关键词有哪些
  • 做网站即墨什么是广告营销
  • 威海做网站公司十大计算机培训机构排名
  • 做爰的细节描述和过程网站微信营销平台系统
  • 哈尔滨网站建设那家好曹操博客seo
  • 商城网站怎么做优化关键词优化哪家好
  • 怎样做网站卖手机号外贸推广平台有哪几个
  • 公司网页网站建设 ppt官网seo关键词排名系统
  • 怎么给网站绑定域名营销型网站建设流程
  • 网站建设 开发人一丶一一人一一湖南seo优化首选
  • wordpress京东客seo网络搜索引擎优化
  • 免费网站建站一级大陆夸克搜索引擎
  • seo发布专员招聘seo中国
  • 响水做网站的公司北京做网络优化的公司
  • 做一元购网站会被封吗百度seo软件首选帝搜软件
  • 单位网站等级保护必须做吗搜索引擎大全网站
  • 重庆免费自助建站模板建网站设计