CMake学习笔记(三)——以笔者的Robosub竞赛为例

CMake学习笔记(三)——以笔者的Robosub竞赛为例

继笔者认真学习了CMake语法之后,便开始尝试自己用CMake将以前用Qt写的软件框架程序改编为CMake指令生成模式。现已成功,在此奉上一系列CMakeLists.txt的源码。

一. 前言

1. 比赛项目简要介绍

笔者曾经参加过美国海军作为主办方的竞赛,竞赛名称叫IAUVC(International Autonomous Underwater Vehicle Competiton),即国际水下无人航行器竞赛。笔者在2016年作为团队的图像及总控负责人,2017年作为团队的技术顾问。 由于2016年团队的控制系统仍有很大的改进空间,所以笔者就写了新的软件框架,主要思想基于多进程通信。

2. 本文思路

本文并不系统的解释语法,而是从根目录的CMakeLists.txt开始,按照指令执行流程进行讲解。 注: 关于语法的总结,笔者前面的文章《CMake学习笔记(二)——CMake语法》中,也对CMake语法做了较为系统的总结。

3. 文件列表

在该CMake项目下使用Linux的tree指令,得到如下的文件列表:

.
├── CMakeLists.txt
├── CustomizeFunctions
│   ├── CMakeLists.txt
│   ├── CustomizeStructs
│   │   └── imageprocessheader.h
│   ├── GeneralImageProcess
│   │   ├── CMakeLists.txt
│   │   ├── contours_fun.cpp
│   │   ├── contours_fun.h
│   │   ├── imageprocessing_fun.cpp
│   │   └── imageprocessing_fun.h
│   └── SupportFunctions
│       ├── CMakeLists.txt
│       ├── string_fun.cpp
│       ├── string_fun.h
│       ├── sys_fun.cpp
│       └── sys_fun.h
├── IPCClients
│   ├── CMakeLists.txt
│   ├── IPCImageClient
│   │   └── ncclient_image_main.cpp
│   ├── IPCSendClient
│   │   └── ncclient_send_main.cpp
│   └── IPCSurfClient
│       └── NCClient_Surf_Main.cpp
├── IPCServer
│   ├── CMakeLists.txt
│   └── main.cpp
└── NCFunctions
    ├── CMakeLists.txt
    ├── NCClient
    │   ├── CMakeLists.txt
    │   ├── ncclient.cpp
    │   ├── ncclient.h
    │   ├── ncclient_image.cpp
    │   ├── ncclient_image.h
    │   ├── ncclient_imagemacros.h
    │   ├── ncclient_macros.h
    │   ├── ncclient_send.cpp
    │   ├── ncclient_send.h
    │   ├── ncclient_surface.cpp
    │   └── ncclient_surface.h
    ├── NCServer
    │   ├── CMakeLists.txt
    │   ├── ncserver_dataproc.cpp
    │   ├── ncserver.h
    │   ├── ncserver_link.cpp
    │   ├── ncserver_macros.h
    │   ├── ncserver_record.cpp
    │   ├── ncserver_stage.cpp
    │   └── ncserver_strategy.cpp
    └── NCStage
        ├── Basic
        │   ├── ncstage_basic.cpp
        │   ├── ncstage_basic.h
        │   ├── ncstage.cpp
        │   ├── ncstage.h
        │   └── ncstage_macro.h
        ├── CMakeLists.txt
        ├── ncstage_test.cpp
        └── ncstage_test.h

该文件列表中,下列路径存在CMakeLists.txt:

  • IPCSocket,根目录
    • CustomizeFunctions,自定义底层函数源码
    • NCFunctions,针对笔者一系列类与函数的源码
      • NCClient,客户端及其派生类源码
      • NCServer,服务器端源码
      • NCStage,任务阶段的类实现源码
    • IPCClients,不同客户端二进制文件生成源码
    • IPCServer,服务器端文件生成源码

笔者编译生成整个项目,是在根目录IPCSocket下创造文件夹build,在build文件夹内执行cmake与make指令而生成的。指令如下:

mkdir build
cd ./build
cmake ../
make

GitHub源码地址: https://github.com/upcAutoLang/Framework-for-NACIT2017

下面笔者将按照整个cmake的生成流程逐步分析讲解。存在重复的指令时,笔者会偷懒略过。

二. CMakeLists.txt详解

下面笔者讲从根目录向下逐层讲解:

1. 根目录/CMakeLists.txt

此处根目录为IPCSocket。该目录下的CMakeLists.txt如下:

# CMake最低版本要求
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
# CMake项目名称
PROJECT(IPCSocket)

# 设置二进制目标文件生成路径
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
# 设置库文件生成路径
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)

# 添加四个CMake子目录
ADD_SUBDIRECTORY(./CustomizeFunctions)
ADD_SUBDIRECTORY(./NCFunctions)
ADD_SUBDIRECTORY(./IPCClients)
ADD_SUBDIRECTORY(./IPCServer)

此段代码下,定义了项目名称IPCSocket后,CMake便自动生成了两个变量: // 二进制文件保存路径; PROJECT_BINARY_DIR = IPCSocket_BINARY_DIR // 源代码路径; SOURCE_DIR = IPCSocket_BINARY_DIR 随后又定义了二进制目标文件与库文件的生成路径。此处两行SET代码是指将这两个路径设置为PROJECT_BINARY_DIR(即执行cmake指令的路径)下的bin, lib路径中。

后面的紧接的四个ADD_SUBDIRECTORY指令,是指CMake指令顺序进入四个路径中,顺序执行几个路径中的CMakeLists.txt文件。 这里笔者认为可以将其理解成C++的四个函数。四个函数顺序执行,按先后顺序依次处理./CustomizeFunctions ./NCFunctions ./IPCClients ./IPCServer中的CMakeLists.txt文件。如果这些CMakeLists.txt文件中也存在ADD_SUBDIRECTORY指令也同理。

下面按照ADD_SUBDIRECTORY的顺序进行说明。

2. /CustomizeFunctions/CMakeLists.txt

/CustomizeFunctions文件夹中存储了笔者的自定义函数,笔者此处想要将其封装为库文件。

/CustomizeFunctions/CMakeLists.txt文件内容如下所示:

# CMake最低版本要求
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
# 添加两个CMake子目录
ADD_SUBDIRECTORY(./GeneralImageProcess)
ADD_SUBDIRECTORY(./SupportFunctions)

即进入./GeneralImageProcess, ./SupportFunctions继续寻找CMakeLists.txt。

(1) /CustomizeFunctions/GeneralImageProcess/CMakeLists.txt

GeneralImageProcess文件夹中存储了笔者按照自己的使用习惯而对OpenCV做的自定义函数封装,笔者准备将其封装成库文件。 主要被定义为两部分内容:

  • contours_fun:笔者自定义的基于轮廓处理的函数;
  • imageprocessing_fun:笔者自定义的图像预处理函数;

文件内容如下:

# CMake最低版本要求
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
# 系统中寻找OpenCV的安装路径
FIND_PACKAGE(OpenCV REQUIRED)
# 添加源文件中的#include路径
INCLUDE_DIRECTORIES(./)
INCLUDE_DIRECTORIES(../)
INCLUDE_DIRECTORIES(../CustomizeStructs)
# 设置路径变量
SET(LIB_CONTOUR_SOURCE 
    ../CustomizeStructs/imageprocessheader.h
    ./contours_fun.h
    ./contours_fun.cpp)
SET(LIB_IMAGEPROCESS_SOURCE 
    ../CustomizeStructs/imageprocessheader.h
    ./imageprocessing_fun.h
    ./imageprocessing_fun.cpp)
# 生成库文件
ADD_LIBRARY(contours_fun SHARED ${LIB_CONTOUR_SOURCE})
ADD_LIBRARY(imageprocessing_fun SHARED ${LIB_IMAGEPROCESS_SOURCE})

其中,FIND_PACKAGE(OpenCV REQUIRED)在系统中寻找OpenCV的安装路径。 平常的软件,CMake的FIND_PACKAGE路径是/usr/share/cmake-2.8/Modules

但对于OpenCV的FIND_PACKAGE指令,寻找路径是/usr/local/share/OpenCV/OpenCVConfig.cmake。其中, /usr/local/share/OpenCV/ 是笔者在使用源码编译安装OpenCV时设置的安装地址OpenCV_INSTALL_DIR。

关于FIND_PACKAGE的工作原理,请参考地址: http://blog.csdn.net/bytxl/article/details/50637277

两条SET指令设置了两个路径,在ADD_LIBRARY指令中使用。 指令ADD_LIBRARY(contours_fun SHARED ${LIB_CONTOUR_SOURCE})中,有三个部分如下:

  • contours_fun:生成的库文件基准名称
    • 此处笔者生成的是共享库,所以加上前缀lib,后缀.so,完整的库文件名称应该为libcontours_fun.so
  • SHARED:生成库的属性为共享库;此处若为STATIC则为静态库
  • ${LIB_CONTOUR_SOURCE}:此处为生成库文件的源码路径,该变量在前面已经被定义。

所以该指令便依赖变量LIB_CONTOUR_SOURCE中的路径,生成了libcontours_fun.so库文件。

另外一条ADD_LIBRARY指令同理。

(2) /CustomizeFunctions/SupportFunctions/CMakeLists.txt

CustomizeFunctions/SupportFunctions路径中,保存了笔者按照操作习惯而定义的支持函数。主要有两部分:

  • string_fun:自定义字符串操作函数;
  • sys_fun:自定义系统操作函数;

CMakeLists.txt文件内容如下:

# CMake最低版本要求
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)

# 添加源文件中的#include路径
INCLUDE_DIRECTORIES(./)
INCLUDE_DIRECTORIES(../)

# 设置路径变量
SET(LIB_STRING_SOURCE 
    ./string_fun.h
    ./string_fun.cpp)
SET(LIB_SYS_SOURCE 
    ./string_fun.h
    ./string_fun.cpp
    ./sys_fun.h
    ./sys_fun.cpp)

# 生成库文件
ADD_LIBRARY(string_fun SHARED ${LIB_STRING_SOURCE})
ADD_LIBRARY(sys_fun SHARED ${LIB_SYS_SOURCE})

原理同2.(1),此处不再赘述。

3. /NCFunctions/CMakeLists.txt

/NCFunctions文件夹中,存放了专为笔者所属团队而写类的源码。CMakeLists.txt文件如下:

CMAKE_MINIMUM_REQUIRED(VERSION 2.8)

ADD_SUBDIRECTORY(./NCStage)
ADD_SUBDIRECTORY(./NCClient)
ADD_SUBDIRECTORY(./NCServer)

进入./NCServer ./NCClient ./NCStage继续寻找CMakeLists.txt文件。

(1) /NCFunctions/NCStage/CMakeLists.txt

/NCFunctions/NCStage文件夹中存放的是笔者自定义任务阶段类的源码。主要有三部分:

  • ncstage:任务阶段类的基类源码;
  • ncstage_basic:由ncstage派生出的基础阶段类的源码;
  • ncstage_test:用于调试的由ncstage派生出的阶段类的源码;

CMakeLists.txt文件如下:

# CMake最低版本要求
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)

# 添加源文件中的#include路径
INCLUDE_DIRECTORIES(./)
INCLUDE_DIRECTORIES(./Basic)

# 设置路径变量
SET(LIB_STAGE_SOURCE
    ./Basic/ncstage.cpp)
SET(LIB_STAGEBASIC_SOURCE
    ${LIB_STAGE_SOURCE}
    ./Basic/ncstage_basic.cpp)
SET(LIB_STAGETEST_SOURCE
    ${LIB_STAGE_SOURCE}
    ./ncstage_test.cpp)

# 生成库文件
ADD_LIBRARY(ncstage SHARED ${LIB_STAGE_SOURCE})
ADD_LIBRARY(ncstage_basic SHARED ${LIB_STAGEBASIC_SOURCE})
ADD_LIBRARY(ncstage_test SHARED ${LIB_STAGETEST_SOURCE})

原理同2.(1),此处不再赘述。

(2) /NCFunctions/NCClient/CMakeLists.txt

笔者整个工程的框架,采用的是由socket实现进程间的通讯。/NCFunctions/NCClient文件夹中存放的是各个客户端的类源码。主要有四部分:

  • ncclient:客户端基类源码;
  • ncclient_image:图像客户端源码,用于图像信息处理,派生于客户端基类;
  • ncclient_send:串口通讯客户端源码,用于与下位机传递信息,派生于客户端基类;
  • ncclient_surface:界面客户端源码,用于服务器与其他客户端之间的通讯,派生于客户端基类;

/NCFunctions/NCClient/CMakeLists.txt内容如下:

# CMake最低版本要求
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)

# 系统中寻找OpenCV的安装路径
FIND_PACKAGE(OpenCV REQUIRED)

# 添加源文件中的#include路径
INCLUDE_DIRECTORIES(./)
INCLUDE_DIRECTORIES(../)
INCLUDE_DIRECTORIES(../../CustomizeFunctions/SupportFunctions)
INCLUDE_DIRECTORIES(../../CustomizeFunctions/CustomizeStructs)
INCLUDE_DIRECTORIES(../../CustomizeFunctions/GeneralImageProcess)
INCLUDE_DIRECTORIES(${OpenCV_INCLUDE_DIRS})

# 添加依赖库路径
LINK_DIRECTORIES(${PROJECT_BINARY_DIR}/lib)

# 设置路径变量
SET(LIB_CLIENT_SOURCE
    ./ncclient.cpp
    ../../CustomizeFunctions/SupportFunctions/string_fun.cpp)
SET(LIB_IMAGE_SOURCE
    ${LIB_CLIENT_SOURCE}
    ./ncclient_image.cpp)
SET(LIB_SEND_SOURCE
    ${LIB_CLIENT_SOURCE}
    ./ncclient_send.cpp)
SET(LIB_SURF_SOURCE
    ${LIB_CLIENT_SOURCE}
    ./ncclient_surface.cpp)

# 生成库文件
ADD_LIBRARY(ncclient SHARED ${LIB_CLIENT_SOURCE})
ADD_LIBRARY(ncclient_image SHARED ${LIB_IMAGE_SOURCE}) 
# 连接库文件,以这些库文件为基础生成目标文件
TARGET_LINK_LIBRARIES(ncclient_image 
    ${OpenCV_LIBS}
    imageprocessing_fun)

ADD_LIBRARY(ncclient_send SHARED ${LIB_SEND_SOURCE})
ADD_LIBRARY(ncclient_surf SHARED ${LIB_SURF_SOURCE})

这里值得提的是LINK_DIRECTORIES指令与TARGET_LINK_LIBRARIES指令的运用。 生成图像客户端目标文件,是基于之前自定义函数部分中的imageprocessing_fun的图像处理函数的,而之前这些函数已经被处理为库文件,被存储在工程生成路径下的lib路径中,所以指令LINK_DIRECTORIES(${PROJECT_BINARY_DIR}/lib)就是将库文件路径包含在工程中,以便后面库文件的链接。 后面TARGET_LINK_LIBRARIES命令便通过链接先前的库文件来生成目标文件,该条命令主要有几部分组成:

  • ncclient_image:生成的目标文件名称
    • 由于先前存在指令ADD_LIBRARY(ncclient_image SHARED ${LIB_IMAGE_SOURCE}),所以此处生成的目标文件是共享库文件;
  • ${OpenCV_LIBS}:依赖库文件列表
    • 此处的变量${OpenCV_LIBS}是在通过编译安装OpenCV源码后,系统中便已经存在了该变量。该变量是在OpenCV安装地址下的OpenCVConfig.cmake(笔者的路径是/usr/local/share/OpenCV/OpenCVConfig.cmake)中通过遍历而得到的。该变量获取的过程大致如下图所示:
  • imageprocessing_fun:依赖库文件
    • 该库文件是由前面2.(1)中/CustomizeFunctions/GeneralImageProcess/CMakeLists.txt文件生成的;

注:TARGET_LINK_LIBRARIES指令一定要在ADD_LIBRARY指令之后。

其余部分在2.(1)中都有讲解,此处不再赘述。

(3) /NCFunctions/NCServer/CMakeLists.txt

前面是客户端类代码,此处的/NCFunctions/NCServer文件夹中存放的便是服务器端的类源码。服务器类只有一个,但依照完成不同功能的模块,被笔者分为五部分:

  • ncserver_link:服务器端socket网络通信部分的类函数源码
  • ncserver_dataproc:服务器端解算获得数据部分的类函数源码
  • ncserver_stage:服务器端判断当前任务进行状态的类函数源码
  • ncserver_strategy:服务器端依据当前各传感器数据与当前任务进行状态,判断下一步策略的类函数源码
  • ncserver_record:服务器端记录信息的类函数源码

/NCFunctions/NCServer/CMakeLists.txt内容如下:

# CMake最低版本要求
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)


# 添加源文件中的#include路径
INCLUDE_DIRECTORIES(./)
INCLUDE_DIRECTORIES(../)
INCLUDE_DIRECTORIES(../NCClient)
INCLUDE_DIRECTORIES(../NCStage)
INCLUDE_DIRECTORIES(../NCStage/Basic)
INCLUDE_DIRECTORIES(../../CustomizeFunctions/SupportFunctions)
INCLUDE_DIRECTORIES(../../CustomizeFunctions/CustomizeStructs)
INCLUDE_DIRECTORIES(../../CustomizeFunctions/GeneralImageProcess)

# 查找某个路径下的所有源文件,并将源文件列表存储到某个变量中 
AUX_SOURCE_DIRECTORY(./ LIB_SERVER_SOURCE)
SET(LIB_SERVER_SOURCE 
    ${LIB_SERVER_SOURCE}
    ../NCClient/ncclient_macros.h
    ../NCStage/Basic/ncstage.cpp
    ../../CustomizeFunctions/SupportFunctions/string_fun.cpp
    ../../CustomizeFunctions/SupportFunctions/sys_fun.cpp)

# 生成库文件
ADD_LIBRARY(ncserver SHARED ${LIB_SERVER_SOURCE})

该部分源码中存在AUX_SOURCE_DIRECTORY指令,有两个参数:

  • ./:查找的路径
  • LIB_SERVER_SOURCE:将上述路径中的源文件列表存入该变量

经过这条指令,可以查找当前CMakeLists文件所在路径下的所有源文件,并将整个源文件列表存入变量LIB_SERVER_SOURCE中。

其余部分在前文已经叙述,此处不再赘述。

经过前面的处理,${PROJECT_BINARY_DIR}/lib下的库文件已经生成完毕。生成文件如图所示:

4. /IPCClients/CMakeLists.txt

/IPCClients文件夹中,存放了所有客户端的实现源码。CMakeLists.txt文件如下:

# CMake最低版本要求
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)

# 系统中寻找OpenCV的安装路径
FIND_PACKAGE(OpenCV REQUIRED)

# 添加依赖库路径
LINK_DIRECTORIES(${PROJECT_BINARY_DIR}/lib)

# 添加源文件中的#include路径
INCLUDE_DIRECTORIES(./)
INCLUDE_DIRECTORIES(./IPCImageClient)
INCLUDE_DIRECTORIES(./IPCSendClient)
INCLUDE_DIRECTORIES(./IPCSurfClient)
INCLUDE_DIRECTORIES(../NCFunctions)
INCLUDE_DIRECTORIES(../NCFunctions/NCClient)
INCLUDE_DIRECTORIES(../CustomizeFunctions)
INCLUDE_DIRECTORIES(../CustomizeFunctions/CustomizeStructs)
INCLUDE_DIRECTORIES(../CustomizeFunctions/GeneralImageProcess)
INCLUDE_DIRECTORIES(../CustomizeFunctions/SupportFunctions)

# 设置路径变量
SET(LIBS_CLIENT
    ncclient)
SET(LIBS_IMAGE
    ${LIBS_CLIENT}
    ncclient_image)
SET(LIBS_SEND
    ${LIBS_CLIENT}
    ncclient_send)
SET(LIBS_SURF
    ${LIBS_CLIENT}
    ncclient_surf)

# 生成二进制可执行文件
ADD_EXECUTABLE(IPCImageClient 
    ./IPCImageClient/ncclient_image_main.cpp)
# 链接库文件,以这些库文件为基础生成目标文件
TARGET_LINK_LIBRARIES(IPCImageClient 
    ${LIBS_IMAGE})

ADD_EXECUTABLE(IPCSendClient
    ./IPCSendClient/ncclient_send_main.cpp)
TARGET_LINK_LIBRARIES(IPCSendClient
    ${LIBS_SEND})

ADD_EXECUTABLE(IPCSurfClient
    ./IPCSurfClient/NCClient_Surf_Main.cpp)
TARGET_LINK_LIBRARIES(IPCSurfClient
    ${LIBS_SURF})

其中命令ADD_EXECUTABLE用于生成目标二进制可执行文件,有两个参数如下:

  • IPCImageClient:生成目标二进制可执行文件名称
  • ./IPCImageClient/ncclient_image_main.cpp:生成该可执行文件所依赖的源码

随后的TARGET_LINK_LIBRARIES指令,以指定的库文件为基础,生成目标文件。有两个参数如下:

  • IPCImageClient:先前在ADD_EXECUTABLE中指定的目标二进制可执行文件名称
  • ${LIBS_IMAGE}:依赖库文件列表

综上,指令ADD_EXECUTABLE(IPCImageClient ./IPCImageClient/ncclient_image_main.cpp)便以源码文件ncclient_image_main.cpp,以及变量LIB_IMAGE中包含的库文件为基础,生成了目标可执行文件IPCImageClient。

其余的两条ADD_EXECUTABLE, TARGET_LINK_LIBRARIES指令同理,不再赘述。 该部分CMakeLists.txt在${PROJECT_BINARY_DIR}/bin路径中,生成了所有客户端的可执行文件,如下图所示:

4. /IPCServer/CMakeLists.txt

/IPCServer文件夹中,存放了服务器端的实现源码。CMakeLists.txt文件如下:

# CMake最低版本要求
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)

# 添加依赖库路径
LINK_DIRECTORIES(${PROJECT_BINARY_DIR}/lib)

# 添加源文件中的#include路径
INCLUDE_DIRECTORIES(./)
INCLUDE_DIRECTORIES(../NCFunctions)
INCLUDE_DIRECTORIES(../NCFunctions/NCServer)
INCLUDE_DIRECTORIES(../NCFunctions/NCClient)
INCLUDE_DIRECTORIES(../NCFunctions/NCStage)
INCLUDE_DIRECTORIES(../NCFunctions/NCStage/Basic)
INCLUDE_DIRECTORIES(../CustomizeFunctions)
INCLUDE_DIRECTORIES(../CustomizeFunctions/CustomizeStructs)
INCLUDE_DIRECTORIES(../CustomizeFunctions/GeneralImageProcess)
INCLUDE_DIRECTORIES(../CustomizeFunctions/SupportFunctions)

# 设置路径变量
SET(LIBS_SERVER
    ncserver
    ncstage
    ncstage_basic
    ncstage_test
)

# 生成二进制可执行文件
ADD_EXECUTABLE(IPCServer
    ./main.cpp)
# 链接库文件,以这些库文件为基础生成目标文件
TARGET_LINK_LIBRARIES(IPCServer
    ${LIBS_SERVER})

该部分源码原理在2.(1), 4中都有讲解,此处不再赘述。

该部分CMakeLists.txt在${PROJECT_BINARY_DIR}/bin路径中,生成了服务器端的可执行文件,如下图所示:

至此,整个cmake与make流程全部结束。

后记:

该工程项目框架的源码,笔者已经将源码和文档整理并放在GitHub上,链接见前文。届时望各位大神指教~

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏杨建荣的学习笔记

内核参数导致的备库宕机分析 (一)r7笔记第23天

在前几天搭建好备库之后,因为同步文件着实花了些时间,首先配置备库能够正常接收归档,然后内核参数也基本没有设置,简单使用脚本算出一个 Hugepage的值,就直接...

3367
来自专栏编程坑太多

springboot (八) 文件上传下载

1873
来自专栏battcn

一起来学SpringBoot | 第二十八篇:JDK8 日期格式化

在 JDK8 中,一个新的重要特性就是引入了全新的时间和日期API,它被收录在 java.time 包中。借助新的时间和日期API可以以更简洁的方法处理时间和日...

1723
来自专栏24K纯开源

CMake结合Visual Studio中开发Qt应用程序注意事项

2982
来自专栏JavaNew

Spring Boot实战:Restful API的构建

1415
来自专栏Aloys的开发之路

J2EE相关总结

Java Commons The Java™ Tutorials: http://docs.oracle.com/javase/tutorial/index.h...

19510
来自专栏一个会写诗的程序员的博客

8.1 Spring Boot集成Groovy混合Java开发小结

本章节我们使用SpringBoot集成Groovy混合Java开发一个极简的RestAPI。 数据库使用mysql,ORM层使用mybatis,模板引擎使用fr...

1282
来自专栏Netkiller

Glusterfs 文件系统

Glusterfs 我的系列文档 Netkiller Architect 手札 Netkiller Developer 手札 Netkiller PHP...

34410
来自专栏一个会写诗的程序员的博客

第7章 Spring Boot集成模板引擎小结

因为Spring Boot其实是对Spring生态的封装整合打包,以简化开发中使用Spring框架。所以 Spring Boot在集成模板引擎过程中,其实就是对...

1443
来自专栏Java帮帮-微信公众号-技术文章全总结

高级框架-SpringBoot【悟空教程】

Spring 诞生时是 Java 企业版(Java Enterprise Edition,JEE,也称 J2EE)的轻量级代替品。无需开发重量级的 Enterp...

2582

扫码关注云+社区