docker build -t_在Docker环境构建、打包和运行Spring Boot应用

8022a1bb0d915fda151a04a7dc112c59.png

为何考虑采用Docker?

6155ab1456e4e4835268ab69f53bf02d.png

Docker是提供用户构建镜像的一种容器化技术,所构建的镜像包含了主要的应用程序和运行应用所需的所有依赖项。该镜像可在任何虚拟机或物理机器上的Docker容器上运行。它的强大之处在于允许用户在开发、测试、预生产和生产中运行同样的镜像,而不必担心在每个环境中依赖项的安装或配置。采用Docker构建和运行应用

6155ab1456e4e4835268ab69f53bf02d.png

以Java程序员的视角看,Docker的典型应用场景是在容器内运行应用。这固然不错,但如果Docker能提供应用的构建是不是更好?本文中,我将演示如何在容器内用Docker来编排、构建和运行Spring Boot应用。请先按如下步骤创建一个Docker镜像:
  • 从源主机复制应用程序源代码到镜像的临时构建目录

  • 采用Maven完成应用的编译和打包,生成可执行的JAR文件

  • 采用JRE运行JAR文件

镜像大小的提示

6155ab1456e4e4835268ab69f53bf02d.png

关注所构建镜像文件的大小非常重要。较小的镜像文件具有更快的构建速度、下载速度和更低的存储成本优势。所以要尽可能地让镜像只包括所需的几项组件即可。采用较小的基本镜像同样的道理,选用只包含必须功能的基础镜像文件也是最佳的选择。本文后续采用Alpine镜像也是基于同样考虑,Alpine是只有5MB的超细Linux发行版。非常适合构建精细的镜像。同时Alpine提供一个包管理器,让用户可以安装任何需要的包。但由于Alpine的初始包非常小,所以安装大量包的过程会有些麻烦。如果有看DockerHub的话,就会发现很多流行的镜像都提供了Alpine版,可以直接使用。后续我们也将用到Alpine版本的Maven和Open JDK JRE镜像。抛弃不需要的内容在稍后过程中所定义编译、打包并运行的Spring Boot应用的镜像。就是可部署运行的最终Docker镜像,因此它只需要包含应用本身和运行时依赖项,能够满足在单个容器中构建和运行就可以了。也就是说它可以纯粹就是可执行的JAR包和运行所需的Java JRE文件,而无需包含Maven(包括本地Maven库)或目标目录的全部内容。那么,用户所要做的就是构建应用,然后从最终镜像中剔除不需要的内容。这个正是多阶段构建的作用所在。它允许用户将Docker构建分解为不同的步骤,并在步骤之间复制特定的目标项,抛弃非必须的内容,从而实现抛弃构建工具本身和其他对应用没有关联的内容。测试案例执行步骤

6155ab1456e4e4835268ab69f53bf02d.png

项目构建非常简单,举个例子,我用一个类创建一个标准的Spring Boot应用,并在项目的根目录中添加了一个Dockerfile。(用户可在GitHub[1]上获取这个实验的完整源代码,同步实验。)

c8b6e9a1e2881e79bbae0d9def42ac5d.png

主类的代码显示如下,且没有添加任何其他内容。接下来我将采用默认的执行器健康状况端点来测试这个应用。

package com.blog.samples.docker;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication

public class Application {

public static void main(String[] args) {

SpringApplication.run(Application.class, args);

}

}

定义Docker镜像

6155ab1456e4e4835268ab69f53bf02d.png

如下内容是Dockerfile中定义的镜像文件,尽管内容不多,但包含了很多步的工作。我将在下面详细解释每一行。

FROM maven:3.5.2-jdk-8-alpine AS MAVEN_BUILD

MAINTAINER Brian Hannaway

COPY pom.xml /build/

COPY src /build/src/

WORKDIR /build/

RUN mvn package

FROM openjdk:8-jre-alpine

WORKDIR /app

COPY --from=MAVEN_BUILD /build/target/docker-boot-intro-0.1.0.jar /app/

ENTRYPOINT ["java", "-jar", "docker-boot-intro-0.1.0.jar"]

代码备注:FROM maven:3.5.2-jdk-8-alpine AS MAVEN_BUILD告知Docker采用Maven编译器。maven:3.5.2-jdk-8-alpine构建第一步采用的基础镜像,Docker将首先在本地查找镜像,本地不存在后,将从DockerHub拉取。Maven会在最后阶段被剔除掉(后续COPY命令介绍)考虑下载快速和镜像大小控制的原因,选择Alpine版的Maven镜像。MAINTAINERBrianHannaway非必选项,但是为映像作者提供一个接触点可提高可维护性。(本实验应用验证的点)COPY pom.xml/build/在镜像中创建一个build目录, 并拷入pom.xml文件。COPY src/build/src/拷入src目录到镜像中build目录。WORKDIR/build/设置build为工作目录。后续任何命令都在此目录中运行。RUN mvnpackage执行mvn包来运行编译和打包应用,生成成可执行的JAR文件。在第一次构建镜像时,Maven将从公共Maven库拉取所有需要的依赖项,并将它们缓存在镜像的本地。后续的构建将使用这个缓存版的镜像层,这意味着依赖项将在本地引用,而不必再次从外部拉取。至此,已经完成了镜像定义,只需等其构建成一个可执行的JAR文件。这是多阶段构建的第一部分。下一阶段将获取JAR并运行它。FROM openjdk:8-jre-alpine告知Docker多阶段构建的下一步采用openjdk:8-jre-alpine的基础镜像。再次使用Java 8 JRE的Alpine版本,这一步的选择其实比前面的Maven版本选择更为重要,因为存在于最终版的镜像只是openjdk:8-jre-alpine,因此如果要尽可能控制最终镜像大小的话,选择轻量级JRE镜像就非常重要。WORKDIR/app告知Docker在镜像内创建另一个/app工作目录,后续任何命令都在此目录中运行。COPY--from=MAVEN_BUILD/build/target/docker-boot-intro-0.1.0.jar/app/告知Docker从MAVEN_BUILD阶段的/build/target目录复制ocker-boot-intro-0.1.0.jar到/app目录。如前文所述,多阶段构建的优势就是允许用户将特定的内容从一个构建阶段复制到另一个构建阶段,并丢弃其他所有的内容。如果需要保留从MAVENBUILD阶段开始的所有内容,那最终镜像会包含Maven(包括Maven本地库)工具,以及目标目录中生成的所有类文件。通过从MAVENBUILD阶段选择必须要的内容,那最终得到的镜像会小很多。ENTRYPOINT["java","-jar","app.jar"]告知Docker在容器运行本镜像时,运行哪些命令。本部分用冒号进行多命令的隔离。本案例中,需要把执行JAR文件复制到/app目录运行。构建镜像

6155ab1456e4e4835268ab69f53bf02d.png

完成Docker镜像定义后,就可以着手构建。打开包含Dockerfile(根目录)的目录。运行以下命令构建镜像:

docker image build -t docker-boot-intro

-t参数为指定名称和可选标签。如果不指定标签,Docker会自动标记为最latest。

$ docker image build -t docker-boot-intro .

Sending build context to Docker daemon 26.56MB

Step 1/10 : FROM maven:3.5.2-jdk-8-alpine AS MAVEN_BUILD

---> 293423a981a7

Step 2/10 : MAINTAINER Brian Hannaway

---> Using cache

---> db354a426bfd

Step 3/10 : COPY pom.xml /build/

---> Using cache

---> 256340699bc3

Step 4/10 : COPY src /build/src/

---> Using cache

---> 65eb0f98bb79

Step 5/10 : WORKDIR /build/

---> Using cache

---> b16b294b6b74

Step 6/10 : RUN mvn package

---> Using cache

---> c48659e0197e

Step 7/10 : FROM openjdk:8-jre-alpine

---> f7a292bbb70c

Step 8/10 : WORKDIR /app

---> Using cache

---> 1723d5b9c22f

Step 9/10 : COPY --from=MAVEN_BUILD /build/target/docker-boot-intro-0.1.0.jar /app/

---> Using cache

---> d0e2f8fbe5c9

Step 10/10 : ENTRYPOINT ["java", "-jar", "docker-boot-intro-0.1.0.jar"]

---> Using cache

---> f265acb14147

Successfully built f265acb14147

Successfully tagged docker-boot-intro:latest

SECURITY WARNING: You are building a Docker image from Windows against a non-Windows Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories.

Brians Computer@DESKTOP-077OUJ8 MINGW64 /c/dev/docker-boot-intro (master)

运行构建时,Docker将逐条执行Docker文件中的每个命令。为每个步骤创建一个带有唯一ID的层。例如,步骤1创建的层的ID为293423a981a7。第一次构建图像时,Docker将从DockerHub获取它需要的任何外部图像,然后在此之上开始构建新的层。这会使得第一次构建速度非常慢。在构建过程中,Docker在尝试构建层之前会检查缓存,看看是否已经有所构建层的缓存版本。如果该层的缓存版本可用,Docker将直接使用它而不是从头开始构建。这意味着一旦构建了一个镜像层,后续的构建就是重用,速度会快很多。你可以在上面的构建输出中通过Docker缓存输出的hash值看到使用了缓存层。以上面第6步所发生的为例:作为RUN mvn包命令的一部分,Docker将从公共Maven库获取所有POM依赖项,构建成一个可执行JAR,并将所有这些内容存储在ID为c48659e0197e的层中。下一次构建这个镜像时,Maven依赖项和应用程序JAR将从缓存层中取出,而不必再次下载和构建。镜像大小

6155ab1456e4e4835268ab69f53bf02d.png

运行docker image ls命令将罗列出所有的本地镜像。可发现docker-boot-intro镜像大小为105 MB。

Brians Computer@DESKTOP-077OUJ8 MINGW64 /c/dev/docker-boot-intro (master)

$ docker image ls

REPOSITORY TAG IMAGE ID CREATED SIZE

docker-boot-intro latest 823730301d60 15 minutes ago 105MB

853d42b823c3 15 minutes ago 136MB

39ac5e9e9562 19 minutes ago 105MB

dfda2356bd36 19 minutes ago 136MB

Brians Computer@DESKTOP-077OUJ8 MINGW64 /c/dev/docker-boot-intro (master)

我在前文中提到过尽可能保持镜像大小的最佳实践,接下来让我们细探一下docker-boot-intro镜像的105MB由什么组成的。运行如下命令:

docker image history boot-docker-intro

将看到镜像中各个层的内容情况。

Brians Computer@DESKTOP-077OUJ8 MINGW64 /c/dev/docker-boot-intro/target (master)

$ docker image history docker-boot-intro

IMAGE CREATED CREATED BY SIZE COMMENT

823730301d60 19 minutes ago /bin/sh -c #(nop) ENTRYPOINT ["java" "-jar"... 0B

7e43d899f02f 19 minutes ago /bin/sh -c #(nop) COPY file:05f3666306f8c7af... 20.1MB

1723d5b9c22f 6 days ago /bin/sh -c #(nop) WORKDIR /app 0B

f7a292bbb70c 4 months ago /bin/sh -c set -x && apk add --no-cache o... 79.4MB

4 months ago /bin/sh -c #(nop) ENV JAVA_ALPINE_VERSION=8... 0B

4 months ago /bin/sh -c #(nop) ENV JAVA_VERSION=8u212 0B

4 months ago /bin/sh -c #(nop) ENV PATH=/usr/local/sbin:... 0B

4 months ago /bin/sh -c #(nop) ENV JAVA_HOME=/usr/lib/jv... 0B

4 months ago /bin/sh -c { echo '#!/bin/sh'; echo 'set... 87B

4 months ago /bin/sh -c #(nop) ENV LANG=C.UTF-8 0B

4 months ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B

4 months ago /bin/sh -c #(nop) ADD file:a86aea1f3a7d68f6a... 5.53MB

Brians Computer@DESKTOP-077OUJ8 MINGW64 /c/dev/docker-boot-intro/target (master)

如上所显示5.53 MB的Alpine基础镜像处于第一层。在之上的几层配置了一系列的环境变量,然后是大小为79.4 MB的JRE文件。最后的3层是我们在Dockerfile中定义的层,并包含了20.1 MB的应用JAR。可以发现这个镜像只包括了运行应用所必须的组件,是一个非常不错的轻量级镜像。运行容器

6155ab1456e4e4835268ab69f53bf02d.png

镜像构建好后,可以使用以下命令运行一个容器:

docker container run -p 8080:8080 docker-boot-intro

run命令包括一个可选的-p参数,作用是允许用户将容器应用的端口映射到主机的端口。熟悉Spring Boot的人都知道,应用程序的默认启动端口就是8080。运行一个容器时,Docker将运行可执行JAR文件来启动应用,使用容器的8080端口。但如果要访问容器中的应用,需要通过主机的端口访问,通过端口映射去到容器端口。-p 8080:8080参数就是将容器端口8080映射到主机端口8080。如果没有异常的话,应该可以看到应用程序在端口8080成功启动的信息。

Brians Computer@DESKTOP-077OUJ8 MINGW64 /c/dev/docker-boot-intro/target (master)

$ docker container run -p 8080:8080 docker-boot-intro

. ____ _ __ _ _

/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \

( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \

\\/ ___)| |_)| | | | | || (_| | ) ) ) )

' |____| .__|_| |_|_| |_\__, | / / / /

=========|_|==============|___/=/_/_/_/

:: Spring Boot :: (v2.1.7.RELEASE)

5436 [main] INFO com.blog.samples.docker.Application - Starting Application v0.1.0 on 934a1d731576 with PID 1 (/app/docker-boot-intro-0.1.0.jar started by root in /app)

5466 [main] INFO com.blog.samples.docker.Application - No active profile set, falling back to default profiles: default

16585 [main] INFO o.s.b.w.e.tomcat.TomcatWebServer - Tomcat initialized with port(s): 8080 (http)

16742 [main] INFO o.a.coyote.http11.Http11NioProtocol - Initializing ProtocolHandler ["http-nio-8080"]

16886 [main] INFO o.a.catalina.core.StandardService - Starting service [Tomcat]

16892 [main] INFO o.a.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/9.0.22]

17622 [main] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext

17628 [main] INFO o.s.web.context.ContextLoader - Root WebApplicationContext: initialization completed in 11614 ms

21399 [main] INFO o.s.s.c.ThreadPoolTaskExecutor - Initializing ExecutorService 'applicationTaskExecutor'

23347 [main] INFO o.s.b.a.e.web.EndpointLinksResolver - Exposing 2 endpoint(s) beneath base path '/actuator'

23695 [main] INFO o.a.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8080"]

23791 [main] INFO o.s.b.w.e.tomcat.TomcatWebServer - Tomcat started on port(s): 8080 (http) with context path ''

23801 [main] INFO com.blog.samples.docker.Application - Started Application in 21.831 seconds (JVM running for 25.901)

应用测试

6155ab1456e4e4835268ab69f53bf02d.png

如果看到类似于上面显示的信息输出,那表示容器已经顺利启动。接下来就可以测试应用。如果你在Windows或Mac上运行Docker,需要使用的工具是一个Linux虚拟机Docker Toolbox。需要通过运行docker-machine ip命令可以获得Linux VM的IP地址。本案例中的Linux VM IP是192.168.99.100。

Brians Computer@DESKTOP-077OUJ8 MINGW64 /c/dev/docker-boot-intro (master)

$ docker-machine ip

192.168.99.100

获得IP后,可以使用cURL命令cURL 192.168.99.100:8080/actuator/health来调用应用的健康检查点来测试应用情况。如果应用程序启动并运行正常,即可获得HTTP 200的响应,响应内容为{“status”:“up”}。

Brians Computer@DESKTOP-077OUJ8 MINGW64 /c/dev/docker-boot-intro (master)

$ curl 192.168.99.100:8080/actuator/health

% Total % Received % Xferd Average Speed Time Time Time Current

Dload Upload Total Spent Left Speed

100 15 0 15 0 0 937 0 --:--:-- --:--:-- --:--:-- 937{"status":"UP"}

本方法的局限性

6155ab1456e4e4835268ab69f53bf02d.png

我在前文提到过,可以重用Docker缓存层以减少构建时间。虽然这是事实,但是在构建Java应用时需要考虑存在的例外。每当对Java源代码或POM文件进行更改后,Docker将会发现变更差异,从而忽略缓存的副本层,重新构建所需的层。这是正常的,但问题是这个变化会导致缓存中的Maven依赖项丢失。因此,当使用mvn包命令重新构建这个层时,所有Maven依赖项将再次从远程库中拉取一次,导致显著减慢了构建的速度,成为开发过程中真正的痛点。而且这个问题在构建没有Docker的Java应用程序时完全不存在,仅仅发生在使用Docker构建应用层时发生。解决方案是什么?

6155ab1456e4e4835268ab69f53bf02d.png

目前解决这个问题的方法是使用主机上的本地Maven存储库作为Maven依赖项的源。通过卷告诉Docker去访问主机本地的Maven库,而非从公共库中拉取依赖项。这种方法可以解决这个问题。但也是有利有弊。从好的方面看,你使用的是主机缓存的Maven依赖项,可以在更改源代码后,快速重新构建,节省了构建时间。但不利的方面是Docker镜像的管理因此而失去了一些自主性。使用Docker的主要初衷之一就是不必担心在其运行的环境中的软件配置。理想情况下,Docker镜像应该是自我构建且拥有构建和运行所需的一切元素,而不必存在主机依赖。而这个方法恰好违背了这个初衷,让Docker构建失去了部分自主性。在下一篇文章中,我们将介绍Docker卷,并展示如何使用它们访问主机上的Maven库。结束语

6155ab1456e4e4835268ab69f53bf02d.png

在本文中,我们定义了一个Docker镜像来构建和运行一个Spring Boot应用程序。我们讨论了让镜像保持尽可能小的重要性,可以通过使用超级小的Alpine基础镜像和在多阶段构建过程中进行内容剔除的方式来实现。我们还讨论了使用Docker构建Java应用程序的局限性和可能的解决方案。用户可以从GitHub[1]获取文章中的测试完整源代码。相关链接:
  1. https://github.com/briansjavablog/build-and-run-spring-boot-with-docker

原文链接:https://dzone.com/articles/build-package-and-run-spring-boot-apps-with-docker基于Kubernetes的DevOps实战培训

6155ab1456e4e4835268ab69f53bf02d.png

基于Kubernetes的DevOps实战培训将于2019年12月27日在上海开课,3天时间带你系统掌握Kubernetes,学习效果不好可以继续学习。本次培训包括:容器特性、镜像、网络;Kubernetes架构、核心组件、基本功能;Kubernetes设计理念、架构设计、基本功能、常用对象、设计原则;Kubernetes的数据库、运行时、网络、插件已经落地经验;微服务架构、组件、监控方案等,点击下方图片或者阅读原文链接查看详情。2523c5b4ce92bd1f8e55d1844f530810.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/564651.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java内部类是什么?

在类内部可定义成员变量和方法,且在类内部也可以定义另一个类。如果在类 Outer 的内部再定义一个类 Inner,此时类 Inner 就称为内部类(或称为嵌套类),而类 Outer 则称为外部类(或称为宿主类)。 …

MOOC-人工智能实践:Tensorflow笔记4.2自制数据集txt文件和数据集不匹配解决办法(读取文件名并提取内容并合并原始内容与提取内容)

1、读取文件名称生成txt文件 # P01 批量读取文件名,并将读取的文件名保存到指定路径下的txt中(带.*** 后缀)import os def ListFilesToTxt(dir, file, wildcard, recursion):exts wildcard.split(" ")files os.listdir(dir)for …

file数组 删除文件_java编程IO基础之一:File类

在整个java.io包中,File类是唯一的一个与文件本身操作有关的类,既可以执行文件的创建、删除、重命名,取得文件大小和修改日期。File的构造函数:public File(String pathname) 给定一个要操作文件的完整路径。public File(File par…

excel工具箱_WPS搬来救兵智能工具箱,强大到Excel没有还手之力

EXCEL不加班眼见Excel连胜2局,WPS慌了,搬来救兵智能工具箱,这个神器,所到之处,问题瞬间解决。这智能工具箱究竟是何方宝物,强大到Excel没有还手之力!Excel2016的闪电填充确实牛逼,不…

mysql cve 2016 3521_MySQL-based databases CVE -2016-6663 本地提权

date: 2016/11/3author: dlive0x01 漏洞原文翻译水平不高求轻喷感觉作者在写文章的时候有些地方描述的也不是特别清楚,不过结合poc可以清晰理解漏洞利用过程0x04漏洞利用过程总结给出了清晰的漏洞利用过程,结合poc食用效果更佳# http://legalhackers.com…

mysql 数据迁移_CentOS7迁移Mysql数据库文件

1、查看Mysql安装路径cat /etc/my.cnf2、查看数据盘路径df3、数据盘下创建mysql数据目录,设置目录磁盘写入权限cd /mnt1/xfrb_database/mkdir mysqlchmod -R 777 /mnt1/xfrb_database/mysql/ls -l -a目录创建和权限设置成功。4、停止Mysql服务,拷贝Mysql…

mysql json类型最大长度限制_MySQL json 数据类型

必须要5.7以上版本才能使用 写在开头 mysql json 的功能很强大,只是用来当一个储存数据的字段 就没什么意义了。 使用proto做交互的话,只要JSON 写得好 用proro.Unmarshal() 就可以很方便的转换类型 可以精简很多代码 JSON path 是以 $ 开头,之后就是JSON的层级使用。$ $[0]…

查看目录是否为内存盘_Linux buff/cache内存释放

free 命令 产看内存占用一览:free 参数:今天部署项目的时候,发现pm2命令卡顿不能正确执行,一开始以为是pm2版本的问题,后面升级pm2到最新版本也没有解决问题。既然和pm2本身没有关系,那么就从其它地方检查&…

kafka是存储到本地磁盘么_Kafka 中的消息存储在磁盘上的目录布局是怎样的?

Kafka 中的消息是以主题为基本单位进行归类的,各个主题在逻辑上相互独立。每个主题又可以分为一个或多个分区,分区的数量可以在主题创建的时候指定,也可以在之后修改。每条消息在发送的时候会根据分区规则被追加到指定的分区中,分…

英特尔全部cpu列表_Debian发布安全更新 以修复近期披露的英特尔MDS安全漏洞

Debian项目团队今天发布了针对英特尔微代码固件的新安全更新,以修复近期披露的英特尔MDS(微架构数据采样)漏洞。今年5月14日,英特尔披露了影响旗下诸多英特尔微处理器系列的四个新安全漏洞,不过随后很快发布了缓解这些漏洞的安全升级&#xf…

poi 设置word表格颜色_POI工具练习

POI是一个可以对excel文件进行操作的jar包,使用它可以帮助我们对excel进行操作,也就可以帮助我们实现在jsp页面添加导入数据的功能。只要我们在控制层servlet中加入处理的方法就可以了;首先使用到POI都会与JXL进行对比:查阅之后大…

excel文件存入mysql_解析excel文件并将数据导入到数据库中

今天领导给安排了一个临时工作,让我将一个excel里面的数据解析后放入数据库中,经过一个下午的努力成功完成,现在将代码献上,希望对大家有所帮助一、需要导入的jar1.commons-collections4-4.1.jar2.poi-3.17-beta1.jar3.poi-ooxml-…

android app性能优化_Android性能优化之Apk 瘦身优化

瘦身主要是下载转换率提高头部App都有Lite版本渠道合作产商要求APK 分析工具ApkTool 反编译工具官网:https://ibotpeaches.github.io/ApkTool apktool d students.apkAnalyze apkAndroid studio 2.2之后使用 - 查看apk组成大小,各资源占比 - 查看dex文件…

vue 拖动 datatransfer 问题_electron-vue跨平台桌面应用开发实战教程(四)——窗口样式amp;打开新窗口...

> 本文我们讲解下怎么修改窗口样式,系统默认的窗口非常普通,通常与设计不符,所以我们要自定义,接下来我们讲解下怎么去掉原有样式,怎么实现实现窗口的最小化,最大化和关闭按钮。还有怎么打开…

string 包含_一文搞懂String常见面试题,从基础到实战,到原理分析和源码解析...

01 string基础1.1 Java String 类字符串广泛应用 在 Java 编程中,在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串。1.2 创建字符串创建字符串最简单的方式如下:String greeting "菜鸟教程";在代码中遇到字符串常量时&…

RPN网络在图像处理中的应用

RPN(Region Proposal Network,区域建议网络)是深度学习中用于目标检测的关键组件之一,它通常与后续的目标检测网络(如Fast R-CNN、Faster R-CNN等)结合使用。RPN的主要作用是生成候选目标区域,从…

pythonwhile爬虫教程_Python 爬虫从入门到进阶之路(十一)

之前的文章我们介绍了一下 Xpath 模块,接下来我们就利用 Xpath 模块爬取《糗事百科》的糗事。之前我们已经利用 re 模块爬取过一次糗百,我们只需要在其基础上做一些修改就可以了,为了保证项目的完整性,我们重新再来一遍。我们通过…

阿联酋esma认证怎么做_百度爱采购企业认证是怎么做的?这些你要知道!

入驻百度爱采购的企业可以获得认证标识,这个标识是企业品牌树立的重要表现,在产品展示页面和百度首页搜索企业名称都有企业信息的醒目展示,并且可以直接跳转到企业店铺,为企业带来流量的同时,还能进行品牌的宣传。百度…

人脸离线识别模块_人脸消费机离线刷脸如何实现?

随着越来越多刷卡刷脸一卡通的设备出现。大家在享受一卡通的便利的同时。也在考虑人脸消费机的基本功能和安全属性,人脸消费机,是指人脸在消费访客管理方面的相关。在人脸刷脸进行消费的时候是一下几个阶段1.人脸特征提取首先得先让人脸消费机看到你的脸…

junit版本_Junit-jupiter-api 和 junit-jupiter-engine 的区别是什么

我们都知道 JUnit 是用于进行单元测试的。但是 Junit 5 和 Junit 4 的区别比较大。 很多时候你可能会遇到 Junit 引擎配置错误导致测试无法进行。junit-jupiter-api JUnit 5 Jupiter API 的测试,你需要使用这个 API 来写测试和进行扩展。junit-jupiter-engine JUnit…