Spring Boot 2.x使用篇(一)—— 初识Spring Boot

2023-10-27

1、Spring Boot 2.x概述

  2018年3月初,Spring Boot发生了重大版本更替,正式更新到了2.x的正式(GA)版本。在学习Spring Boot的过程中使用的参考书籍《深入浅出Spring Boot 2.x》使用的Spring Boot的版本是2.0.0.RELEASE版本。但在本地IDE中的联系项目使用的版本为2.1.6.RELEASE。这里需要注意的是Spring Boot 2.x和Spring Boot 1.x在使用上有很多地方存在不同,并且只能支持JDK 8或者以上的版本。

1.1 Spring Boot与Spring

  在之前的系列博客《Spring使用篇系列博客传送门》中,我们曾经讨论过在Spring框架中何时使用注解,何时使用XML的问题,在这里我觉得有必要再次将它提出来做一下明确。

  • 对于自己编写的业务类使用注解。例如,对于MVC开发,控制器使用@Controller注解,业务层使用@Service注解,持久层使用@Repository注解。

  • 对于一些公用的Bean,例如对于数据库、第三方资源等则使用XML进行配置。

  这已经是一个不成文的共识,直至今日这样的配置方式还在企业中广泛使用着。

  随着Spring的版本不断更新,在Spring 4.x时注解功能越来越强大,对于XML的依赖越来越少。与此同时,Pivotal团地在原有Spring的基础上主要通过注解的方式继续简化了Spring框架的开发,他们基于Spring框架开发了Spring Boot,所以Spring Boot并非是代替Spring框架,而是让Spring框架更加容易得到快速的使用。

  随着近年来微服务的流行,越来越多的企业需要快速的开发,而Spring Boot除了以注解为主的开发,还有其他的绑定,例如,对服务器进行了绑定和默认对Spring的最大化配置,所以开发者能够尽快进行代码开发、发布和测试自己的项目。这符合了现金微服务快速开发、测试和部署的需要,于是越来越多的企业选择Spring Boot作为开发的选型,进而使得Spring Boot更加兴旺流行起来。

1.2 Spring Boot与Spring MVC

  在之前的系列博客《Spring MVC使用篇系列博客传送门》中,详细介绍了如何使用Spring MVC框架开发一个MVC的Web项目,这里便不再赘述,如感兴趣请查看博看《Spring MVC使用篇(二)—— 环境搭建》。其核心是需要配置DispatcherServlet,也需要配置Spring IoC的容器。而对于这些配置可以选择使用web.xml的配置来实现,当然如果使用的是Servlet 3.1规范,也可以继承又Spring MVC提供的AbstractAnnotationConfigDispatcherServletInitializer来配置Spring MVC项目。

  传统的Spring MVC开发需要配置的内容还是比较多的,而且对设计人员要求较高。在开发完成后,开发者还需要找到对应的服务器去运行,例如Tomcat或者Jetty等,这样既要进行开发,又要进行配置和部署,工作量不小。

  而使用Spring Boot开发后,我们可以通过Maven引入多个相关的Spring Boot的starter,具体依赖如下所示。Spring Boot会将对应的jar包加载到工程中,而且它还会把绑定的服务器也加载到工程中,这些都不需要你再进行处理。正如Spring Boot承诺的那样,绑定服务器,并且实现Spring的尽可能配置,采用约定优于配置的原则。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.6.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.ccff.springboot.demo</groupId>
	<artifactId>chapter1</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>chapter1</name>
	<packaging>war</packaging>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<!--aop包-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>

		<!--Web开发包,将载入Spring MVC所需要的包,且内嵌Tomcat-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<!--加载测试依赖包-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<!--引入插件-->
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

1.3 Spring Boot的优点

  根据官网的文档,Spring Boot具有以下优点:

  • 创建独立的Spring应用程序
  • 已经嵌入了Tomcat、Jetty或者Undertow,无需部署WAR文件
  • 允许通过Maven来根据需要获取starter
  • 尽可能地自动化配置Spring
  • 提供生产就绪型功能,如指标、健康系统和外部部署
  • 绝对没有代码生成,对XML没有要求配置

  通过上面官网文档的描述,我们可以看出:首先,Spring Boot是一个基于Spring框架搭建起来的应用,其次它会嵌入Tomcat、Jetty或者Undertow等服务器;同时提供通过Maven(或者Grandle)依赖的starter,这些starter可以直接获取开发所需的相关包,通过这些starter项目就能以Java Application的形式运行Spring Boot项目,而无须其他服务器配置;对于配置,Spring Boot提供Spring框架的最大自动化配置,大量使用自动化配置,使得开发者对Spring的配置尽量减少;此外还提供了一些监测、自动检测的功能和外部配置,与此同时没有附加代码和XML的配置要求。

  约定优于配置,这是Spring Boot的主导思想。 对于Spring Boot而言,大部分情况下存在默认配置,你甚至可以在没有任何定义的情况下使用Spring框架,如果需要自定义也只需要在配置文件中配置一些属性便可, 十分便捷。而对于部署这些项目必需的功能,Spring Boot提供starter的依赖,这样使得开发者能够尽可能快地搭建开发环境,快速进行开发和部署,这就是Spring Boot的特色。

  使用Spring Boot配置Spring MVC,开发控制器类的示例代码如下所示:

package com.ccff.springboot.demo.chapter1;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@SpringBootApplication
public class Chapter1Application {
	@GetMapping("/index")
	public String index() {
		return "<h1>Hello Spring Boot 2.x!</h1>";
	}

	public static void main(String[] args) {
		SpringApplication.run(Chapter1Application.class, args);
	}

}

  直接运行上面的启动类,在控制台出现如下信息则说明Spring Boot已经启动成功。
在这里插入图片描述
  启动成功后,在浏览器内打开相应url查看输出信息如下:
在这里插入图片描述
  由此可见,Spring Boot允许直接进行开发,这就是它的优势。在传统所需要配置的地方,Spring Boot都进行了约定,也就是你可以直接以Spring Boot约定的方式进行开发和运行你的项目。

  当你需要修改配置的时候,它也提供了一些快速配置的约定,尽可能地配置好Spring项目和绑定对应的服务器,使得开发人员的配置更少,更加直接地开发项目。

2、IntelliJ IDEA中搭建Spring Boot开发环境

2.1 使用Spring Initializr自动创建项目

  首先启动IntelliJ IDEA开发环境,然后选择Create New Project,在弹出的窗口中选择Spring Initializr,将JDK版本设置为1.8,并选择创建服务的URL后点击Next进入下一步。具体如下图所示:
在这里插入图片描述
  其次,对我们创建的项目进行相应的配置后点击Next,进入下一步,需要注意的是这里我们仍然选择了War的打包形式。具体如下:
在这里插入图片描述
  然后,在新窗口中需要选择Spring Boot的版本为2.1.6,并根据自己的需要选择对应的starter进行依赖,IntelliJ IDEA也会为你建好工程。这里我们先选择Web进行初始项目的创建,点击Next进入下一步。具体如下图所示:
在这里插入图片描述
  最后,输入项目名称、项目保存位置,点击Finish,完成Spring Boot项目的创建。

2.2 IntelliJ IDEA中搭建多模块项目

  在本小节中,我们利用之前创建的Spring Boot项目,在IntelliJ IDEA中搭建一个由多个模块相互组成并形成依赖的Spring Boot项目。

  首先,将父项目中的.idea文件夹、.mvn文件夹、src文件夹、.gitignore文件、mvnw文件、mvnw.cmd文件、Help.md文件全部删除。

  其次,创建两个子module,分别为"chapter1"与"chapter2"。两个子module的创建方式与创建父项目的方法一致。

  然后,修改父项目中的pom.xml文件中的打包方式,将之前的打包成War包的形式修改为打包成pom的形式。具体如下所示:

<groupId>com.ccff.springboot.demo</groupId>
<artifactId>springboot-learning-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>	<!-- 这里由原来的war修改为现在的pom  -->

  接着,在父项目中的pom.xml文件中添加modules标签,声明其子module,具体配置如下:

<modules>
	<module>chapter1</module>
	<module>chapter2</module>
</modules>

  最后,分别修改chapter1和chapter2的子module中的pom.xml文件。将其中依赖的父项目修改为当前父项目,修改后的配置如下:

<parent>
	<groupId>com.ccff.springboot.demo</groupId>
	<artifactId>springboot-learning-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<relativePath>../pom.xml</relativePath>
</parent>

  在最后一步中有一个坑,这里需要特殊说明一下: 在修改子module依赖的父项目时,如果不指定relativePath的值,可能会出现如下错误提示:

maven报错:Non-resolvable parent POM

  这说明了子module中的relativePath属性指向的路径不对,需要手动修改,使其指向父项目的pom.xml文件

2.3 Spring Boot的依赖和自动配置

  我们以最常用的Spring MVC为例说明Spring Boot为什么在很少的配置下就能够运行。

  打开本地Maven仓库,找到Spring Boot的文件夹:org.springframework.boot下的spring-boot-start-web,进入后发现有一个名为"2.0.0.RELEASE"的文件夹,进入后打开名为"spring-boot-starter-web-2.0.0.RELEASE.pom"的文件,如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starters</artifactId>
    <version>2.0.0.RELEASE</version>
  </parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
  <version>2.0.0.RELEASE</version>
  <name>Spring Boot Web Starter</name>
  <description>Starter for building web, including RESTful, applications using Spring
		MVC. Uses Tomcat as the default embedded container</description>
  <url>https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-web</url>
  <organization>
    <name>Pivotal Software, Inc.</name>
    <url>https://spring.io</url>
  </organization>
  <licenses>
    <license>
      <name>Apache License, Version 2.0</name>
      <url>http://www.apache.org/licenses/LICENSE-2.0</url>
    </license>
  </licenses>
  <developers>
    <developer>
      <name>Pivotal</name>
      <email>info@pivotal.io</email>
      <organization>Pivotal Software, Inc.</organization>
      <organizationUrl>http://www.spring.io</organizationUrl>
    </developer>
  </developers>
  <scm>
    <connection>scm:git:git://github.com/spring-projects/spring-boot.git/spring-boot-starters/spring-boot-starter-web</connection>
    <developerConnection>scm:git:ssh://git@github.com/spring-projects/spring-boot.git/spring-boot-starters/spring-boot-starter-web</developerConnection>
    <url>http://github.com/spring-projects/spring-boot/spring-boot-starters/spring-boot-starter-web</url>
  </scm>
  <issueManagement>
    <system>Github</system>
    <url>https://github.com/spring-projects/spring-boot/issues</url>
  </issueManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
      <version>2.1.6.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <!-- JSON依赖 -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-json</artifactId>
      <version>2.1.6.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <!-- Tomcat依赖 -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-tomcat</artifactId>
      <version>2.1.6.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <!-- Hibernate Validator依赖 -->
    <dependency>
      <groupId>org.hibernate.validator</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>6.0.7.Final</version>
      <scope>compile</scope>
    </dependency>
    <!-- Spring Web依赖 -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>5.0.4.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <!-- Spring Web MVC依赖 -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.0.4.RELEASE</version>
      <scope>compile</scope>
    </dependency>
  </dependencies>
</project>

  从上面的文件中可以看出,当加入spring-boot-starter-web后,它会通过Maven将对应的资源加载到我们的工程中,这样便能够形成依赖。

  但这样还不足以运行Spring MVC项目,要运行它还需要对Spring MVC进行配置,让它能够生产Spring MVC所需的对象,才能启用Spring MVC。

  为了探讨Spring MVC在Spring Boot自动配置的问题,我们还需要在本地Maven仓库中找到spring-boot-autoconfigure目录下的名为"2.1.6.RELEASE"的文件夹下的名为"spring-boot-autoconfigure-2.1.6.RELEASE.jar"的源码包。将其解压缩出来,打开它目录下的子目录:/org/springframework/boot/autoconfigtre/web.servlet后,我们可以看到很多配置类。其中DispatcherServletAutoConfiguration就是一个对DispatcherServlet进行自动配置的类,其源码中的一个内部类DispatcherServletConfiguration对Spring Boot的自动配置做最基本的配置。

2.4 使用自定义配置

  通过上一小节,我们知道在几乎没有任何配置下就能用Spring Boot启动Spring MVC项目,是因为Spring Boot通过Maven依赖找到对应的jar包和嵌入的服务器,然后使用默认自动配置类来创建默认的开发环境。

  但有的时候,我们需要对这些默认的环境进行修改以适应个性化的要求,这些在Spring Boot中也是非常简单的。正如@EnableConfigurationProperties注解那样,它允许读入配置文件的内容来自定义自动初始化所需的内容。

  在使用IntelliJ IDEA创建Spring Boot项目时,会在src/main/resources路径下创建一个名为"application.properties"的属性文件。它是一个默认的配置文件,通过它可以根据自己的需要实现自定义。

  假设当前8080端口已经被占用,因此我们希望使用8090端口号启动Tomcat,那么只需要在application.properties属性文件中添加如下属性配置:

server.port=8090

  事实上,在平时的日常开发中,除了使用properties文件外,yml文件也是一种十分常见的格式。yml文件的配置与properties文件只是简写和缩进的差别,因此差异并不大。

3、Spring Boot项目Quick Start

  在2.4小节中,我们修改了服务器的端口号,除此之外有时我们还需要修改Spring MVC的视图解析器。在开发中较为常用的视图之一为JSP。

  例如,现在控制器中返回一个字符串"index",那么我们希望它对应的是开发项目的/WEN-INF/jsp/index.jsp文件。

  首先,我们需要在chapter2 module中的pom.xml文件中添加该模块特有的JSP和JSTL依赖包,具体如下:

<dependency>
	<groupId>org.apache.tomcat.embed</groupId>
	<artifactId>tomcat-embed-jasper</artifactId>
	<scope>provided</scope>
</dependency>
<dependency>
	<groupId>javax.servlet</groupId>
	<artifactId>jstl</artifactId>
	<scope>provided</scope>
</dependency>

  其次,修改application.properties属性文件,设置Spring MVC视图解析器的前缀后后缀,具体属性配置如下:

server.port=8090

spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp

  然后,在com.ccff.springboot.demo.chapter2.controller包下创建名为"IndexController"的控制器,具体代码如下所示:

package com.ccff.springboot.demo.chapter2.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * Created by wangzhefeng01 on 2019/8/2.
 */
@RestController
@RequestMapping("/Index")
public class IndexController {
    @GetMapping("/index")
    public String index(){
        return "index";
    }
}

  接着,在src/main下创建webapp/WEB-INF/jsp/index.jsp,具体代码如下所示:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Spring boot 视图解析器</title>
</head>
<body>
    <h1>测试视图解析器</h1>
</body>
</html>

  最后,运行Spring Boot启动类Chapter2Application,项目启动后在浏览器内输入url:,结果如下图所示:
在这里插入图片描述
  这里有一个很深的坑需要大家注意:在IndexController控制器中使用@Controller注解时会出现404的问题,报错信息如下:

spring boot整合jsp的时候访问页面错误日志:Path with "WEB-INF" or "META-INF":

  问题解析戳这里:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring Boot 2.x使用篇(一)—— 初识Spring Boot 的相关文章

  • 我在使用 JavaFX 绘制十字时遇到问题

    我正在尝试编写代码 在网格上对角绘制 3 个形状 前两个形状是正方形和圆形 我能做到 然而 第三种形状让我有些悲伤 我应该画一个十字 T 版本 而不是 X 每次我写出代码时 它看起来就像一个侧面 我知道我只是错过了一些简单的东西 但我真的很
  • Hibernate 每个子类一个表继承策略的效率

    我正在考虑 Hibernate 管理的类层次结构的表布局 当然 每个子类表技术在我看来是一般意义上最合适的 然而 通过逻辑思考 我对其性能有些担忧 尤其是随着子类数量的扩展 举一个非常简短 且经典 的示例 假设您有以下类 public ab
  • 如何将日期字符串解析为Date? [复制]

    这个问题在这里已经有答案了 如何将下面的日期字符串解析为Date object String target Thu Sep 28 20 29 30 JST 2000 DateFormat df new SimpleDateFormat E
  • Android:TelephonyManager 类

    我不明白为什么 API 文档中这么写TelephonyManager类是public 但是当我尝试创建一个实例时 它说它不是公共类 并且无法从包中访问 我看到它也说使用Context getSystemService Context TEL
  • WSDL2Java 抛出无法找到主类:org.apache.axis.wsdl.WSDL2Java

    我正在尝试从远程 Web 服务创建 java 文件 我下载了axis 1 4 将lib文件夹复制到c data axis lib其中包含这些文件 axis jar 轴 ant jar commons discovery 0 2 jar co
  • 为什么当达到 InitiatingHeapOccupancyPercent 时 G1 不开始标记周期?

    根据文档 http www oracle com technetwork articles java g1gc 1984535 html XX InitiatingHeapOccupancyPercent 设置触发标记周期的Java堆占用阈
  • 为什么不自动装箱泛型的 Java 基本类型?

    Java 不允许在通用数据结构中使用原始类型 例如 不允许使用 ArrayList 原因是 原始类型不能直接转换为Object 然而 Java 1 5 确实支持自动装箱 并且包装类在通用数据结构中工作 那么为什么编译器不能将其自动装箱到 A
  • Eclipse RCP - 将视图与编辑器区域堆叠?

    在开发 Eclipse RCP 应用程序时 是否可以将视图与编辑器区域堆叠在一起 像这样 我有多个列表 表格 我想创建一种预览组合 当通过单击鼠标选择列表上的项目时 我希望我的预览合成显示该项目的数据 如果用户双击某个项目 我想在预览合成后
  • Java 8 Stream - 并行执行 - 不同的结果 - 为什么?

    假设我有一个List
  • Thymeleaf 下拉菜单中的默认值

    我正在使用 Spring MVC 和 thymeleaf 构建一个 Web 应用程序 我的下拉菜单是这样的并且它按预期工作
  • 在 java 8 下使用泛型出现类型错误,但在 java 7 下则不然

    我有一段代码可以在 java 7 下编译良好 但不能在 java 8 下编译 这是一个独立的重现示例 我已经采用了显示此问题的真实代码并删除了所有实现 import java util Iterator class ASTNode
  • Java ArrayList 和 HashMap 动态

    有人可以提供一个创建Java的例子吗ArrayList and HashMap在飞行中 所以而不是做一个add or put 实际上在类实例化时为数组 哈希提供种子数据 举个例子 类似于 PHP 的例子 array array 3 1 2
  • 用于 Eclipse 的 Resharper [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • Web 服务客户端的 AXIS 与 JAX-WS

    我决定用Java 实现Web 服务客户端 我已经在 Eclipse 中生成了 Axis 客户端 并使用 wsimport 生成了 JAS WS 客户端 两种解决方案都有效 现在我必须选择一种来继续 在选择其中之一之前我应该 考虑什么 JAX
  • 对于双核手机,availableProcessors() 返回 1

    我最近购买了一部 Moto Atrix 2 手机 当我尝试查看手机中的处理器规格时 Runtime getRuntime availableProcessors 返回 1 proc cpuinfo 也仅包含有关处理器 0 的信息 出于好奇
  • Eclipse 如何创建一个未解决编译问题的类?

    当我尝试使用 javac 编译此类时 出现编译错误并且未创建 Test class public class Test public static void main String args int x 1L lt this cannot
  • 如何在 Hibernate 中自动递增复合主键中的 Id?

    我有一个带有复合主键的表 groupId and batchId 实体类看起来像 Entity name EMPLOYEE public class Employee EmbeddedId private EmployeePK employ
  • java mysql 准备好的语句

    我正在尝试使用 java 向数据库中进行简单的插入 它告诉我我的 sql 语法已关闭 但是 当我复制打印出来的字符串并将其放入 phpmyadmin 中的 sql 命令中时 它会正确执行该命令 并且我似乎无法弄清楚 java 中的字符串查询
  • java中什么是静态接口?

    我正在阅读Map Entry界面 当我注意到它是一个static界面 我不太明白什么是静态接口 它与常规接口有什么不同 public static interface Map Entry
  • Java有没有类似微软CHESS的工具?

    是否有类似于 Microsoft 的现有 Java 工具CHESS http research microsoft com chess 或者 CHESS 源代码是否开放 以便我可以尝试将其转换为 Java 谷歌的织线工 http code

随机推荐