`
liujiekasini0312
  • 浏览: 147473 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

用 Docker 构建、运行、发布来一个 Spring Boot 应用

 
阅读更多

原文同步至http://waylau.com/docker-spring-boot/


本文演示了如何用Docker构建、运行、发布来一个springBoot 应用。

Docker 简介

Docker是一个 Linux 容器管理工具包,具备“社交”方面,允许用户发布容器的 image (镜像),并使用别人发布的 image。Docker image 是用于运行容器化进程的方案,在本文中,我们将构建一个简单的 Spring Boot 应用程序。

有关 Docker 的详细介绍,可以移步至《简述 Docker》

前置条件

用 Maven 构建项目

创建目录结构

项目的目录结构因符合 Maven 的约定。

在 *nix 系统下执行mkdir -p src/main/Java/docker_spring_boot,生产如下结构 :

└── src
    └── main
        └── java
            └── com
                └── waylau
                    └── docker_spring_boot
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

创建 pom.xml 文件

<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/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>com.waylau</groupId>
    <artifactId>docker-spring-boot</artifactId>
    <packaging>jar</packaging>
    <version>1.0.0</version>
    <name>docker-spring-boot</name>
    <description>Getting started with Spring Boot and Docker</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.3.RELEASE</version>
        <relativePath/>
    </parent>

    <dependencies>
        <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>
    <!-- tag::plugin[] -->
            <plugin>
                <groupId>com.spotify</groupId>
                <artifactId>docker-maven-plugin</artifactId>
                <version>0.4.3</version>
                <configuration>
                    <imageName>${docker.image.prefix}/${project.artifactId}</imageName>
                    <dockerDirectory>src/main/docker</dockerDirectory>
                    <resources>
                        <resource>
                            <targetPath>/</targetPath>
                            <directory>${project.build.directory}</directory>
                            <include>${project.build.finalName}.jar</include>
                        </resource>
                    </resources>
                </configuration>
            </plugin>
    <!-- end::plugin[] -->
        </plugins>
    </build>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <docker.image.prefix>waylau</docker.image.prefix>
        <spring.boot.version>1.3.3.RELEASE</spring.boot.version>
    </properties>
</project>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66

Spring Boot Maven plugin提供了很多方便的功能:

  • 它收集的类路径上所有 jar 文件,并构建成一个单一的、可运行的“über-jar”,这使得它更方便地执行和传输服务。
  • 它搜索的public static void main()方法来标记为可运行的类。
  • 它提供了一个内置的依赖解析器,用于设置版本号以匹配Spring Boot 的依赖。您可以覆盖任何你想要的版本,但它会默认选择的 Boot 的版本集。

Spotify 的docker-maven-plugin插件是用于构建 Maven 的 Docker Image

  • imageName指定了镜像的名字,本例为waylau/docker-spring-boot
  • dockerDirectory指定 Dockerfile 的位置
  • resources是指那些需要和 Dockerfile 放在一起,在构建镜像时使用的文件,一般应用 jar 包需要纳入。本例,只需一个 jar 文件。

编写 Spring Boot 应用

编写一个简单的 Spring Boot 应用 :

src/main/java/com/waylau/docker_spring_boot/Application.java:

package com.waylau.docker_spring_boot;

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

/**
 * 主应用入口
 * @author <a href="http://waylau.com">waylau.com</a>
 * @date 2016年3月19日
 */
@SpringBootApplication
@RestController
public class Application {

    @RequestMapping("/")
    public String home() {
        return "Hello Docker World."
                + "<br />Welcome to <a href='http://waylau.com'>waylau.com</a></li>";
    }

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

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

解释下上面的代码:

  • 类用@SpringBootApplication@RestController标识,可用 Spring MVC 来处理 Web 请求。
  • @RequestMapping/映射到home(),并将”Hello Docker World” 文本作为响应。
  • main()方法使用 Spring Boot 的SpringApplication.run()方法来启动应用。

运行程序

使用 Maven

编译:

mvn package

运行:

java -jar target/docker-spring-boot-1.0.0.jar

访问项目

如果程序正确运行,浏览器访问http://localhost:8080/,可以看到页面 “Hello Docker World.” 字样。

将项目容器化

Docker 使用Dockerfile文件格式来指定 image 层,

创建文件src/main/docker/Dockerfile:

FROM frolvlad/alpine-oraclejdk8:slim
VOLUME /tmp
ADD docker-spring-boot-1.0.0.jar app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

解释下这个配置文件:

  • VOLUME指定了临时文件目录为/tmp。其效果是在主机/var/lib/docker目录下创建了一个临时文件,并链接到容器的/tmp。改步骤是可选的,如果涉及到文件系统的应用就很有必要了。/tmp目录用来持久化到 Docker 数据文件夹,因为 Spring Boot 使用的内嵌 Tomcat 容器默认使用/tmp作为工作目录
  • 项目的 jar 文件作为 “app.jar” 添加到容器的
  • ENTRYPOINT执行项目 app.jar。为了缩短Tomcat 启动时间,添加一个系统属性指向 “/dev/urandom” 作为 Entropy Source

构建 Docker Image

执行构建成为 docker image:

mvn package docker:build

运行

运行 Docker Image

docker run -p 8080:8080 -t waylau/docker-spring-boot

[root@waylau spring-boot]# docker run -p 8080:8080 -t waylau/docker-spring-boot

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.3.3.RELEASE)

2016-03-20 08:45:51.276  INFO 1 --- [           main] c.waylau.docker_spring_boot.Application  : Starting Application v1.0.0 on 048fb623038f with PID 1 (/app.jar started by root in /)
2016-03-20 08:45:51.289  INFO 1 --- [           main] c.waylau.docker_spring_boot.Application  : No active profile set, falling back to default profiles: default
2016-03-20 08:45:51.722  INFO 1 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@669af5fc: startup date [Sun Mar 20 08:45:51 GMT 2016]; root of context hierarchy
2016-03-20 08:45:54.874  INFO 1 --- [           main] o.s.b.f.s.DefaultListableBeanFactory     : Overriding bean definition for bean 'beanNameViewResolver' with a different definition: replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration; factoryMethodName=beanNameViewResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter; factoryMethodName=beanNameViewResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter.class]]
2016-03-20 08:45:57.893  INFO 1 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8080 (http)
2016-03-20 08:45:57.982  INFO 1 --- [           main] o.apache.catalina.core.StandardService   : Starting service Tomcat
2016-03-20 08:45:57.984  INFO 1 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.0.32
2016-03-20 08:45:58.473  INFO 1 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2016-03-20 08:45:58.473  INFO 1 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 6877 ms
2016-03-20 08:45:59.672  INFO 1 --- [ost-startStop-1] o.s.b.c.e.ServletRegistrationBean        : Mapping servlet: 'dispatcherServlet' to [/]
2016-03-20 08:45:59.695  INFO 1 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'characterEncodingFilter' to: [/*]
2016-03-20 08:45:59.701  INFO 1 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2016-03-20 08:45:59.703  INFO 1 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2016-03-20 08:45:59.703  INFO 1 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'requestContextFilter' to: [/*]
2016-03-20 08:46:00.862  INFO 1 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@669af5fc: startup date [Sun Mar 20 08:45:51 GMT 2016]; root of context hierarchy
2016-03-20 08:46:01.166  INFO 1 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/]}" onto public java.lang.String com.waylau.docker_spring_boot.Application.home()
2016-03-20 08:46:01.189  INFO 1 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2016-03-20 08:46:01.190  INFO 1 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2016-03-20 08:46:01.302  INFO 1 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2016-03-20 08:46:01.302  INFO 1 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2016-03-20 08:46:01.438  INFO 1 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2016-03-20 08:46:01.833  INFO 1 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2016-03-20 08:46:02.332  INFO 1 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2016-03-20 08:46:02.343  INFO 1 --- [           main] c.waylau.docker_spring_boot.Application  : Started Application in 13.194 seconds (JVM running for 15.828)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

推送 image 到 Docker Hub

首先,你在 Docker Hub 要有注册账号,且创建了相应的库;

其次,docker 推送前,先要登录,否则报unauthorized: access to the requested resource is not authorized的错误

执行:

docker login

[root@waylau spring-boot]# docker login
Username: waylau
Password: 
Email: waylau521@gmail.com
WARNING: login credentials saved in /root/.docker/config.json
Login Succeeded
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

执行推送

docker push waylau/docker-spring-boot

[root@waylau spring-boot]# docker push waylau/docker-spring-boot
The push refers to a repository [docker.io/waylau/docker-spring-boot]
751d29eef02e: Layer already exists 
4da3741f39c7: Pushed 
5f70bf18a086: Layer already exists 
7e4d0cb13643: Layer already exists 
8f045733649f: Layer already exists 
latest: digest: sha256:eb4d5308ba1bb27489d808279e74784bda6761b3574f4298d746abba59b35164 size: 9415
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

镜像加速器

Docker Hub 在国外,有时候拉取 Image 极其缓慢,可以使用国内的镜像来实现加速

阿里云

echo "DOCKER_OPTS=\"--registry-mirror=https://yourlocation.mirror.aliyuncs.com\"" | sudo tee -a /etc/default/docker
sudo service docker restart
  • 1
  • 2
  • 1
  • 2

其中https://yourlocation.mirror.aliyuncs.com是您在阿里云注册后的专属加速器地址:

DaoCloud

sudo echo “DOCKER_OPTS=\”\$DOCKER_OPTS –registry-mirror=http://your-id.m.daocloud.io -d\”” >> /etc/default/docker
sudo service docker restart
  • 1
  • 2
  • 1
  • 2

其中http://your-id.m.daocloud.io是您在 DaoCloud 注册后的专属加速器地址:

源码

获取项目源码,
执行

Gitclonehttps://github.com/waylau/docker-demos.git
cd docker-demos/samples/spring-boot

获取项目镜像,
执行

docker pull waylau/docker-spring-boot

参考引用

分享到:
评论

相关推荐

    Docker 构建、运行、发布一个 Spring Boot 应用

    Docker 构建、运行、发布一个 Spring Boot 应用,Docker 构建、运行、发布一个 Spring Boot 应用

    使用Spring Cloud和Docker构建微服务

    Spring Cloud与Spring Boot紧密集成,Spring Boot是一个旨在简化Spring应用创建和开发过程的项目,它集成了大量Spring生态系统中的最佳实践,并通过最少的配置就能生产就绪。 在使用Spring Cloud构建微服务时,可以...

    spring boot 、spring cloud 、docker 微服务实战

    Spring Boot通过提供预配置的starter来减少项目配置,使得开发者可以快速启动一个包含各种功能(如Web、数据访问、安全等)的应用。它还内置了Tomcat服务器,无需额外配置即可运行。Spring Boot强调“约定优于配置”...

    34-构建运行Spring Boot的Docker1

    34-构建运行Spring Boot的Docker1

    spring boot资料以及项目

    Spring Boot的核心理念是“约定优于配置”,它通过默认配置和自动配置,使得开发者可以快速搭建一个可运行的应用。它内置了Tomcat服务器,支持Java或Groovy编写应用,并且提供了大量的起步依赖(Starters),如数据...

    使用 IntelliJ IDEA、Maven 用 Docker 部署了一个 Spring Boot 项目

    使用 IntelliJ IDEA、Maven 用 Docker 部署了一个 Spring Boot 项目,Docker 一次将应用代码、JDK、环境变量、配置文件、以及其他配置信息都打包到一个镜像里面了,一次构建,处处运行,所以再也不用担心环境和配置...

    Spring Boot应用开发框架 v3.0.12.zip

    Spring Boot是Java领域的一款热门应用开发框架,它简化了基于Spring的应用程序开发流程,通过预配置的设置使得开发者能够快速构建可运行的应用程序。在"Spring Boot应用开发框架 v3.0.12.zip"中,包含了一份详细的...

    Spring Boot2企业应用实战

    Spring Boot作为Spring框架的一个子项目,旨在简化Java应用的初始搭建以及开发过程,尤其对于微服务架构来说,它的重要性不言而喻。 1. **约定优于配置**:Spring Boot的核心理念之一就是"约定优于配置"。这意味着...

    Spring Boot实战 ,丁雪丰 (译者).zip

    Spring Boot是Java领域中的一个热门框架,它极大地简化了Spring应用程序的初始搭建以及开发过程,旨在“开箱即用”,让开发者能够快速构建可运行的Java应用。 这本书首先会介绍Spring Boot的基础概念,包括其核心...

    spring boot 相关技术

    Spring Boot 是一个由 Pivotal 团队开发的框架,旨在简化 Spring 应用程序的初始搭建以及开发过程。它提供了预配置的依赖项和自动配置功能,使得开发者可以快速地创建生产级别的基于 Spring 的应用程序。 《Spring ...

    Spring Boot实战派(源码)

    - Spring Boot通过一系列的起步依赖来简化Maven或Gradle配置,比如`spring-boot-starter-web`用于构建Web应用,`spring-boot-starter-data-jpa`用于JPA数据访问。 3. **嵌入式Web服务器** - Spring Boot支持内嵌...

    Spring Boot-实战

    Spring Boot是Spring生态系统中的一个关键组件,它旨在简化Spring应用程序的初始搭建以及开发过程,使得开发者能够快速地创建独立运行的、生产级别的Java应用。 Spring Boot的核心特性包括自动配置、内嵌Web服务器...

    Spring Boot 2 Cookbook 第二版

    Spring Boot是Spring框架的一个子项目,旨在简化Java应用程序的初始搭建以及开发过程,通过提供预配置的特性,如嵌入式Web服务器、自动配置、起步依赖等,极大地提高了开发效率。 本书首先会讲解Spring Boot 2的...

    Spring+Boot实战

    书中会详细介绍如何创建一个基础的Spring Boot应用,包括使用Spring Initializr初始化项目、添加依赖、配置启动类等步骤。 其次,Spring Boot集成了许多流行的技术,如Spring MVC、Thymeleaf、MyBatis等,使得开发...

    Learning Spring Boot 2.0 Second Edition

    在本书中,首先会讲解Spring Boot的基础知识,包括如何创建第一个Spring Boot项目,配置Spring Boot的起步依赖,以及如何利用Spring Initializr快速初始化项目。此外,还会详细讨论Spring Boot的自动配置机制,它是...

    《Spring Boot企业级开发教程》配套资料

    "blog_system"则是一个完整的博客系统项目,学习者可以通过阅读和运行这些代码,了解Spring Boot如何在实际项目中应用,包括数据库交互、前后端分离、用户认证、权限管理等功能。 综上所述,这份《Spring Boot企业...

    Spring Boot应用开发框架 v2.3.7.zip

    - 构建Web应用:以一个实际的在线商城为例,展示如何使用Spring Boot开发完整的Web应用程序。 - 实时日志监控:利用Spring Boot Actuator的监控功能,建立实时日志分析系统。 - 异常处理:研究Spring Boot中的...

    Spring Boot 2企业应用实战_pdf和源码

    Spring Boot 2是Java开发领域中的一个热门框架,它由Pivotal团队开发,旨在简化Spring应用程序的初始搭建以及开发过程。Spring Boot的核心理念是“约定优于配置”,它通过默认配置来减少大量的XML配置文件,使得...

    基于Spring Boot+Spring Cloud+Docker的个人理财系统源码

    PiggyMetrics ,一个开源项目,适合微服务入门,可以指导开发者使用 Spring Boot、Spring Cloud 和 Docker 搭建微服务架构。 该开源项目有一个典型的微服务实现案例 - 个人理财微服务系统。采用Spring Boot/Spring ...

Global site tag (gtag.js) - Google Analytics