SpringBoot启动流程

大致流程

关于SpringBoot的启动流程,大致是这样的

  1. 加载启动类
    启动类是使用了@SpringBootApplication注解标注的类,该注解包含了@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan三个注解的功能。SpringBoot通过扫描启动类所在的包及子包,自动配置相应的Bean

  2. 加载配置文件
    SpringBoot程序默认从applicaiton.properties或application.yml中加载配置,也可以通过在启动类上标注@PropertySource来引入其他的配置文件

  3. 创建Spring容器
    SpringBoot使用SpringBootApplication类创建Spring容器,SpringApplication类是SpringBoot的核心类,它提供了配置和管理Bean的方法。如果是Web应用,SpringApplication会创建一个内置的Web服务器

  4. 加载自动配置
    SpringBoot通过@EnableAutoConfiguration来完成自动配置,根据starter依赖中的Configuration和Bean的装配情况,自动装配相应的Bean

  5. 运行SpringBoot应用程序
    当一切准备就绪后,SpringBoot就会启动应用程序,如果是Web应用,就会启动内置的Web服务器,如果使用的是Web服务器,可以将应用程序打包成一个可以直接运行的jar文件

源码解读

当我们执行启动类中的SpringApplication.run()方法后,
SpringBoot的动作实际上可以大致分为两部分:实例化SpringApplication和运行SpringApplication

参考文章
9千字长文带你了解SpringBoot启动过程–史上最详细 SpringBoot启动流程-图文并茂_Fly丶X的博客-CSDN博客
spring boot 启动流程分析 - 掘金
https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6ba8bf5c8177430b8f462f35948d1c74~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp?

图片来自以上两篇文章

实例化SpringApplication

我们查看源码
当执行了静态方法SpringApplcation.run()后,就来到了这两个方法

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

进入到SpringApplication的构造方法,在实例化中干了哪些事情?

  
public SpringApplication(Class<?>... primarySources) {  
    this((ResourceLoader)null, primarySources);  
}  
  
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {  
    this.sources = new LinkedHashSet();  
    this.bannerMode = Mode.CONSOLE;  
    this.logStartupInfo = true;  
    this.addCommandLineProperties = true;  
    this.addConversionService = true;  
    this.headless = true;  
    this.registerShutdownHook = true;  
    this.additionalProfiles = Collections.emptySet();  
    this.isCustomEnvironment = false;  
    this.lazyInitialization = false;  
    this.applicationContextFactory = ApplicationContextFactory.DEFAULT;  
    this.applicationStartup = ApplicationStartup.DEFAULT;  
    this.resourceLoader = resourceLoader;  
    Assert.notNull(primarySources, "PrimarySources must not be null");  
    this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));  
    // 1. 判断此应用是否是Web应用
    this.webApplicationType = WebApplicationType.deduceFromClasspath();  
    // 2.加载类加载器
    this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));  
    this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));  
    // 3. 加载监听器
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));  
    // 4. 设置主类
    this.mainApplicationClass = this.deduceMainApplicationClass();  
}
1. 判断应用类型

通过一个枚举类WebApplicationType的静态方法来判断应用类型

package org.springframework.boot;  
  
import org.springframework.util.ClassUtils;  
  
public enum WebApplicationType {  
    NONE,  
    SERVLET,  
    REACTIVE;  
	// Web 应用相关的类
    private static final String[] SERVLET_INDICATOR_CLASSES = new String[]{"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"};  
    private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";  
    private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";  
    private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";  
  
    private WebApplicationType() {  
    }  
  
    static WebApplicationType deduceFromClasspath() {  
        if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {  
            return REACTIVE;  
        } else {  
            String[] var0 = SERVLET_INDICATOR_CLASSES;  
            int var1 = var0.length;  
  
            for(int var2 = 0; var2 < var1; ++var2) {  
                String className = var0[var2];  
                if (!ClassUtils.isPresent(className, (ClassLoader)null)) {  
                    return NONE;  
                }  
            }  
  
            return SERVLET;  
        }  
    }  
}

在此方法中,通过扫描Classpath中是否有预设的几个Web应用相关的类,来判断此应用是否是一个Web应用。
此处提供了几个Web关键类的全限定名,拿到了全限定名,就可以利用反射的机制来判断是否存在这些类。
此枚举类有三个类型:

  • NONE
  • SERVLET
  • REACTIVE
    其中REACTIVE的意思就是标志这个SpringBoot应用程序就是一个普通的boot项目
2. 加载Initializer初始化构造器

此处是加载Spring-boot中自带的初始化器,并不是第三方的starter中的初始化器。
会扫描spring-boot自动的jar中META-INF/spring.factories文件中定义的配置类和相关Bean
可以利用此机制,实现自己的初始化器。

3. 加载应用监听器Listener

同样,仍然是在spring-boot自带的jar中的META-INF/spring.factories文件中加载。
此监听器的类型是ApplicationListener,也就是对整个应用程序的监听器。

4. 设置主类

我们的启动类,就是主类,使用了@SpringBootApplication注解标注,并且包含main方法的类。
通过deduceMainApplicationClass()方法来推断主类所在的位置,确定主类的位置,为后面的包扫描提供条件。

执行SpringApplication的run()

实例化完成SpringApplication后,就会执行SpringApplication实例的run()方法
在该方法中,大致完成了以下几件事情:

  1. 启动应用监听器
  2. 准备Environment
  3. 发布事件
  4. 创建上下文对象、bean
  5. 刷新refresh()上下文对象
  6. 结束
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {  
    return (new SpringApplication(primarySources)).run(args);  
}

此方法会返回一个ConfigurableApplicationContext的实例,我们可以在启动类中拿到这个返回值。
看run()方法的源码

public ConfigurableApplicationContext run(String... args) {  
	// 1. 开启计时
    long startTime = System.nanoTime();  
    DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();  
    // Spring上下文对象
    ConfigurableApplicationContext context = null;  
    this.configureHeadlessProperty();  
    // 2. 实例化应用监听器并封装
    SpringApplicationRunListeners listeners = this.getRunListeners(args);  
    // 启动应用监听器
    listeners.starting(bootstrapContext, this.mainApplicationClass);  
  
    try {  
	    // 3.准备环境参数以及初始化环境
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);  
        ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);  
        this.configureIgnoreBeanInfo(environment);  
        // 4. 打印Banner,无所吊用
        Banner printedBanner = this.printBanner(environment);  
        // 5. 实例化Spring上下文对象
        context = this.createApplicationContext();  
        context.setApplicationStartup(this.applicationStartup);  
        // 6.Spring容器初始化
        this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);  
        // 7. 刷新容器
        this.refreshContext(context);  
        this.afterRefresh(context, applicationArguments);  
        Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);  
        if (this.logStartupInfo) {  
            (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);  
        }  
  
        listeners.started(context, timeTakenToStartup);  
        this.callRunners(context, applicationArguments);  
    } catch (Throwable var12) {  
        this.handleRunFailure(context, var12, listeners);  
        throw new IllegalStateException(var12);  
    }  
  
    try {  
        Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);  
        listeners.ready(context, timeTakenToReady);  
        return context;  
    } catch (Throwable var11) {  
        this.handleRunFailure(context, var11, (SpringApplicationRunListeners)null);  
        throw new IllegalStateException(var11);  
    }  
}
1. 启动计时器

我现在使用的Springboot版本是2.7.10,发现取消了计时器这个类,而是直接通过来获取启动耗时

System.nanoTime()

如果你是低版本的Springboot,会发现有这样的几行代码,这就是启动计时器

// 实例化计时器
StopWatch stopWatch = new StopWatch(); 
// 开始计时
stopWatch.start();

不论是否使用了计时器,目的都是来记录SpringBoot的启动流程。

2.启动应用监听器

通过这几行代码

// 获取所有的应用监听器
SpringApplicationRunListeners listeners = this.getRunListeners(args);  
// 启动
listeners.starting(bootstrapContext, this.mainApplicationClass);

在getRunListener()方法中

private SpringApplicationRunListeners getRunListeners(String[] args) {  
    Class<?>[] types = new Class[]{SpringApplication.class, String[].class};  
    return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args), this.applicationStartup);  
}

通过一个Class类型的数组来封装所有的监听器
再看SpringApplicationRunListeners的构造方法

class SpringApplicationRunListeners {  
    private final Log log;  
    private final List<SpringApplicationRunListener> listeners;  
    private final ApplicationStartup applicationStartup;  
  
    SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners, ApplicationStartup applicationStartup) {  
        this.log = log;  
        this.listeners = new ArrayList(listeners);  
        this.applicationStartup = applicationStartup;  
    }
}

在这个SpringApplicationRunListeners内部,通过一个List集合来封装所有的应用监听器,以此来达到统一管理所有的应用监听器

3.准备Environment

通过以下代码

// 准备应用参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);  
// 准备Environment
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);

Environment接口是对程序运行环境的抽象,是保存系统配置的中心
来打开prepareEnvironment()方法

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) { 
// 1. 创建Environment实例,自动根据环境的不同,创建对应的Environment实例
    ConfigurableEnvironment environment = this.getOrCreateEnvironment();  
// 2. 设置启动参数到Environment实例中
    this.configureEnvironment(environment, applicationArguments.getSourceArgs());  
// 3. 更新参数
    ConfigurationPropertySources.attach(environment);  
// 4. 通过应用监听器来发布事件    
    listeners.environmentPrepared(bootstrapContext, environment);  
    DefaultPropertiesPropertySource.moveToEnd(environment);  
    Assert.state(!environment.containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties.");  
// 5. 绑定主类
    this.bindToSpringApplication(environment);  
    if (!this.isCustomEnvironment) {  
        EnvironmentConverter environmentConverter = new EnvironmentConverter(this.getClassLoader());  
        environment = environmentConverter.convertEnvironmentIfNecessary(environment, this.deduceEnvironmentClass());  
    }  

    ConfigurationPropertySources.attach(environment);  
    return environment;  
}
4. 实例化SpringContext对象
// 实例化SpringContext对象
context = this.createApplicationContext();  
context.setApplicationStartup(this.applicationStartup);  
// 设置SpringContext参数
this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);  

在prepareContext()方法中,

private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {  
// 1. 绑定环境Enviroment
    context.setEnvironment(environment);  
// 2. 如果Application有设置BeanName、resourceLoader等,
// 就将其注入到Context中
    this.postProcessApplicationContext(context);  
    this.applyInitializers(context);  
// 3. 发布ApplicationContextInitializer事件    
    listeners.contextPrepared(context);  
    bootstrapContext.close(context);  
    if (this.logStartupInfo) {  
        this.logStartupInfo(context.getParent() == null);  
        this.logStartupProfileInfo(context);  
    }
5. 刷新容器

对Context做出了一系列设置后,刷新容器

// 刷新SpringContext容器
this.refreshContext(context);
秋天code
关注 关注
  • 2
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot启动流程
quanzhan_King的博客
05-29 4442
自定义初始化器首先要实现 ApplicationContextInitializer 接口执行自定义初始化器的方式有三种方式一:通过通过SpringApplication对象调用addInitializers(new 自定义初始化器)@Order(2)@Override方式二:通过META-INF/spring.factories中指定org.springframework.context.ApplicationContextInitializer=自定义初始化器。
Springboot启动流程
热门推荐
zsh2050的专栏
04-30 6万+
一、SpringBoot启动的时候,会构造一个SpringApplication的实例,构造SpringApplication的时候会进行初始化的工作,初始化的时候会做以下几件事: 1、把参数sources设置到SpringApplication属性中,这个sources可以是任何类型的参数. 2、判断是否是web程序,并设置到webEnvironment的boolean属性中. 3、创建并初始化ApplicationInitializer,设置到initializers属性中 。 4、创建并初始化App
一文搞懂springboot的启动和自动装配流程
最新发布
weixin_44202287的博客
08-08 1180
getAutoConfigurationEntry(AnnotationMetadata annotationMetadata)根据annotationMetadata(即我们的启动类SpringbootdemoApplication)获取AutoConfigurationEntry。由此可见,@SpringBootApplication注解是一个组合注解,由@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan注解组成。
springboot启动流程
weixin_35750747的博客
12-30 356
Spring Boot是一个用于快速构建企业级应用的框架。它的启动流程如下: 加载和配置 Spring Boot 应用程序 在启动时,Spring Boot 会加载应用程序的配置文件,例如 application.properties 或 application.yml。这些配置文件包含了有关应用程序的信息,例如应用程序的端口号、数据库连接信息等。 启动 Spring 应用上下文 Spri...
Spring Boot
湫兮若风的博客
01-27 214
启动过程: 我们发现启动流程主要分为三个部分,第一部分进行SpringApplication的初始化模块,配置一些基本的环境变量、资源、构造器、监听器,第二部分实现了应用具体的启动方案,包括启动流程的监听模块、加载配置环境模块、及核心的创建上下文环境模块,第三部分是自动化配置模块,该模块作为springboot自动配置核心,在后面的分析中会详细讨论。在下面的启动程序中我们会串联起结构中的主要...
头秃系列,二十三张图带你从源码分析Spring Boot 启动流程~
码猿技术专栏
11-19 393
持续原创输出,点击上方蓝字关注我目录前言源码版本从哪入手?源码如何切分?如何创建SpringApplication?设置应用类型设置初始化器(Initializer)设置监听器(List...
可能是全网最全的SpringBoot启动流程源码分析(基于 2.1.5 版本)
01-20
使用 Spring Boot 启动一个微服务十分简单,只需要在启动类上调用 SpringApplication 的run方法即可 点击进入run方法 1 run 静态辅助类,可用于运行使用默认配置(即我们添加的一系列注解)的指定源的 ...
SpringBoot启动流程.png
12-12
springboot启动路程图
一文详解Springboot启动流程
Java技术攻略的博客
04-21 1527
本文会对Springboot启动流程进行详细分析。但是请注意,Springboot启动流程Springboot的逻辑,请千万不要将Springboot启动流程相关逻辑与Spring的相关逻辑混在一起,比如把Spring的bean生命周期的逻辑混在Springboot启动流程中,那么整个体系就复杂且混乱了。所以本文仅重点关注Springboot启动流程,涉及Spring的部分,会略作说明并跳过。整体的一个结构图如下。Springboot2.4.1Springboot启动时,第一件重要事件就是初始化。
SpringBoot 启动流程
qq_33807380的博客
01-10 6683
方法来启动 Spring Boot 应用程序。该方法接受两个参数,第一个参数是启动类的类对象,第二个参数是主方法的参数。方法中做了一系列操作来完成项目启动所需的初始化Spring容器、启动内置tomcat、启动应用程序以及通知监听者等步骤。总的来说,Spring Boot的启动过程是一个复杂的流程,从启动类的Main方法中调用。启动类必须使用 @SpringBootApplication 注解标记该类。方法开始,然后在SpringApplication对象的。方法来执行项目启动的后续操作。
Spring Boot启动流程详解
吴代庄的博客
03-08 3031
Spring Boot是一个基于Spring框架的快速开发工具,它可以帮助我们快速搭建一个可运行的Spring应用。本文将详细介绍Spring Boot启动流程,帮助大家更好地理解Spring Boot的工作原理。
Spring Boot启动流程
流楚丶格念的博客
08-24 2万+
- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 创建springbootApplication对象 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - 1. 创建springbootApplication对象springboot容器初始化操作 2. 获取当前应用的启动类型。2.1 :通过判断当前classpath是否加载servlet类,返回servlet web启动方式。
Spring Boot 启动流程
Dark丶照萤映雪
04-11 589
一、创建SpringApplication对象 在META-INF配置相关监听器 1、以Debug模式启动应用 2、Step Into SpringApplication.run方法,对run方法进行包装 3、继续Step Into run方法,这时候会先创建SpringApplication对象,然后才允许run方法 4、继续Step Into 5、进入this...
SpringBoot启动流程
m0_52058662的博客
02-10 3127
SpringBoot启动流程
SpringBoot启动流程
yztezhl的专栏
11-20 3117
springboot是依赖于spring的,比起spring,除了拥有spring的全部功能以外,springboot无需繁琐的xml配置,这取决于它自身强大的自动装配功能;并且自身已嵌入Tomcat、Jetty等web容器,集成了springmvc,使得springboot可以直接运行,不需要额外的容器,提供了一些大型项目中常见的非功能性特性,如嵌入式服务器、安全、指标,健康检测、外部配置等。 springboot 是一个服务于框架的框架,简化了spring的配置。
SPringBoot启动流程
08-22
- *1* *2* *3* [9千字长文带你了解SpringBoot启动过程--史上最详细 SpringBoot启动流程-图文并茂](https://blog.csdn.net/weixin_44947701/article/details/124055713)[target="_blank" data-report-click={"spm":...
写文章

热门文章

  • Markdown编辑器Vditor的基本使用以及在Vue3中使用 6484
  • 介绍几款在线传输网站 6180
  • mysql 自增字段、属性 5607
  • C语言字符串输入输出 5460
  • 关于ElementPlus中的表单验证 5211

分类专栏

  • 编程思想 29篇
  • 应用分享篇 10篇
  • 软件安装 4篇
  • Redis 9篇
  • 算法刷题笔记 10篇
  • BUG\踩坑 9篇
  • GitHub50天50项目 8篇

最新评论

  • 关于SpringMVC中的几个配置类WebMvcConfigurer 、WebMvcConfigurationSupport 、WebMvcConfigurerAdapter

    湫稻渔: 有用,谢谢

  • 踩坑:JSP中使用ES6、JavaScript的模板字符串

    ttjavaxyz: 谢谢,搞了半天原来是这个原因

  • 关于ElementPlus中的表单验证

    彩虹猪头怪: 写的太好了,这样的作者多来点

  • Markdown编辑器Vditor的基本使用以及在Vue3中使用

    minecraft_box: 没用,没有任何显示表情包

  • JDK动态代理解析,InvocationHandler的第一个参数的解析

    秋天code: InvocationHandler.invoke()中的第一个参数就是代理对象,不是目标对象,你可以在invoke()方法中return出去第一个参数,然后与代理对象做一次==判断,结果是true,说明invoke()的第一个参数就是代理对象。你也可以去看看JDK源码中对参数的解释

最新文章

  • TypeScript依赖注入框架Typedi的使用、原理、源码解读
  • 在MySQL中如何存储一个IPv4地址?
  • 字符集、编码格式的理解
2024年1篇
2023年80篇
2022年47篇
2021年1篇

目录

目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

玻璃钢生产厂家宁德玻璃钢卡通座椅雕塑北京大厂玻璃钢雕塑生产厂家牡丹花玻璃钢雕塑玻璃钢雕塑和平鸽玻璃钢人物雕塑生产成都特色玻璃钢雕塑长沙市玻璃钢雕塑商场气球美陈火烈鸟工业玻璃钢花盆研发公司北安玻璃钢雕塑亳州景区玻璃钢雕塑销售厂家亳州广场玻璃钢雕塑陕西玻璃钢抽象雕塑大型玻璃钢雕塑哪个靠谱商场美陈加工宿州大型玻璃钢雕塑武汉抽象玻璃钢雕塑浙江户外商场美陈批发商场柜台内的圣诞美陈商场开幕美陈是什么投标书咸宁商场美陈商场美陈是干什么公仔玻璃钢动物雕塑供应福建创意玻璃钢雕塑销售厂家河源玻璃钢雕塑费用山东常用商场美陈厂家直销商场美陈效果作用玻璃钢素描雕塑哪里有玻璃钢鹿雕塑金山区拉丝玻璃钢雕塑产品介绍香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化