MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的,旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。它支持的数据结构非常松散,是类似 JSON 的 BSON 格式,因此可以存储比较复杂的数据类型。
本文将介绍通过 mongodb 存储二进制文件,从而实现一个文件服务器 MongoDB File Server。
文件服务器的需求
本文件服务器致力于小型文件的存储,比如博客中图片、普通文档等。由于MongoDB 支持多种数据格式的存储,对于二进制的存储自然也是不话下,所以可以很方便的用于存储文件。由于 MongoDB 的 BSON 文档对于数据量大小的限制(每个文档不超过16M),所以本文件服务器主要针对的是小型文件的存储。对于大型文件的存储(比如超过16M),MongoDB 官方已经提供了成熟的产品 GridFS,读者朋友可以自行了解。
本文不会对 MongoDB 的概念、基本用法做过多的介绍,有兴趣的朋友可自行查阅其他文献,比如,笔者所著的《分布式系统常用技术及案例分析》一书,对 MongoDB 方面也有所着墨。
所需环境
本例子采用的开发环境如下:
- MongoDB 3.4.4
- Spring Boot 1.5.3.RELEASE
- Thymeleaf 3.0.3.RELEASE
- Thymeleaf Layout Dialect 2.2.0
- Embedded MongoDB 2.0.0
- Gradle 3.5
其中,spring Boot 用于快速构建一个可独立运行的 Java 项目;Thymeleaf 作为前端页面模板,方便展示数据;Embedded MongoDB 则是一款由 Organization Flapdoodle OSS 出品的内嵌 MongoDB,可以在不启动 MongoDB 服务器的前提下,方面进行相关的 MongoDB 接口测试;Gradle 是一个类似于 Apache Maven 概念的新一代项目自动化构建工具。
有关 Spring Boot 的方面的内容,可以参阅笔者所著著的开源书《Spring Boot 教程》。有关 Thymeleaf 的方面的内容,可以参阅笔者所著著的开源书《Thymeleaf 教程》。有关 Gradle 的方面的内容,可以参阅笔者所著著的开源书《Gradle 3 用户指南》。
build.gradle
本文所演示的项目,是采用 Gradle 进行组织以及构建的,如果您对 Gradle 不熟悉,也可以自行将项目转为 Maven 项目。
build.gradle 文件内容如下:
// buildscript 代码块中脚本优先执行
buildscript {
// ext 用于定义动态属性
ext {
springBootVersion = '1.5.3.RELEASE'
}
// 自定义 Thymeleaf 和 Thymeleaf Layout Dialect 的版本
ext['thymeleaf.version'] = '3.0.3.RELEASE'
ext['thymeleaf-layout-dialect.version'] = '2.2.0'
// 自定义 Embedded MongoDB 的 依赖
ext['embedded-mongo.version'] = '2.0.0'
// 使用了 Maven 的中央仓库(你也可以指定其他仓库)
repositories {
//mavenCentral()
maven {
url 'http://maven.aliyun.com/nexus/content/groups/public/'
}
}
// 依赖关系
dependencies {
// classpath 声明说明了在执行其余的脚本时,ClassLoader 可以使用这些依赖项
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
// 使用插件
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
// 打包的类型为 jar,并指定了版本
version = '1.0.0'
// 指定编译 .java 文件的 JDK 版本
sourceCompatibility = 1.8
// 默认使用了 Maven 的中央仓库。这里改用自定义的镜像库
repositories {
//mavenCentral()
maven {
url 'http://maven.aliyun.com/nexus/content/groups/public/'
}
}
// 依赖关系
dependencies {
// 该依赖对于编译发行是必须的
compile('org.springframework.boot:spring-boot-starter-web')
// 添加 Thymeleaf 的依赖
compile('org.springframework.boot:spring-boot-starter-thymeleaf')
// 添加 Spring Data Mongodb 的依赖
compile 'org.springframework.boot:spring-boot-starter-data-mongodb'
// 添加 Embedded MongoDB 的依赖用于测试
compile('de.flapdoodle.embed:de.flapdoodle.embed.mongo')
// 该依赖对于编译测试是必须的,默认包含编译产品依赖和编译时依
testCompile('org.springframework.boot:spring-boot-starter-test')
}
- 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
该 build.gradle 文件中的各配置项的注释已经非常详尽了,这里就不再赘述其配置项的含义了。
领域对象
文档类 File
文档类是类似与 JPA 中的实体的概念。
import org.springframework.data.mongodb.core.mapping.Document;
@Document
public class File {
@Id // 主键
private String id;
private String name; // 文件名称
private String contentType; // 文件类型
private long size;
private Date uploadDate;
private String md5;
private byte[] content; // 文件内容
private String path; // 文件路径
...
// getter/setter
...
protected File() {
}
public File(String name, String contentType, long size,byte[] content) {
this.name = name;
this.contentType = contentType;
this.size = size;
this.uploadDate = new Date();
this.content = content;
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object == null || getClass() != object.getClass()) {
return false;
}
File fileInfo = (File) object;
return java.util.Objects.equals(size, fileInfo.size)
&& java.util.Objects.equals(name, fileInfo.name)
&& java.util.Objects.equals(contentType, fileInfo.contentType)
&& java.util.Objects.equals(uploadDate, fileInfo.uploadDate)
&& java.util.Objects.equals(md5, fileInfo.md5)
&& java.util.Objects.equals(id, fileInfo.id);
}
@Override
public int hashCode() {
return java.util.Objects.hash(name, contentType, size, uploadDate, md5, id);
}
@Override
public String toString() {
return "File{"
+ "name='" + name + '\''
+ ", contentType='" + contentType + '\''
+ ", size=" + size
+ ", uploadDate=" + uploadDate
+ ", md5='" + md5 + '\''
+ ", id='" + id + '\''
+ '}';
}
}
- 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
- 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
文档类,主要采用的是 Spring Data MongoDB 中的注解,用于标识这是个 NoSQL 中的文档概念。
存储库 FileRepository
存储库用于提供与数据库打交道的常用的数据访问接口。其中 FileRepository 接口继承自org.springframework.data.mongodb.repository.MongoRepository
即可,无需自行实现该接口的功能,
Spring Data MongoDB 会自动实现接口中的方法。
import org.springframework.data.mongodb.repository.MongoRepository;
import com.waylau.spring.boot.fileserver.domain.File;
public interface FileRepository extends MongoRepository<File, String> {
}
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
服务接口及实现类
FileService 接口定义了对于文件的 CURD 操作,其中查询文件接口是采用的分页处理,以有效提高查询性能。
public interface FileService {
/**
* 保存文件
* @param File
* @return
*/
File saveFile(File file);
/**
* 删除文件
* @param File
* @return
*/
void removeFile(String id);
/**
* 根据id获取文件
* @param File
* @return
*/
File getFileById(String id);
/**
* 分页查询,按上传时间降序
* @param pageIndex
* @param pageSize
* @return
*/
List<File> listFilesByPage(int pageIndex, int pageSize);
}
- 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
- 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
FileServiceImpl 实现了 FileService 中所有的接口。
@Service
public class FileServiceImpl implements FileService {
@Autowired
public FileRepository fileRepository;
@Override
public File saveFile(File file) {
return fileRepository.save(file);
}
@Override
public void removeFile(String id) {
fileRepository.delete(id);
}
@Override
public File getFileById(String id) {
return fileRepository.findOne(id);
}
@Override
public List<File> listFilesByPage(int pageIndex, int pageSize) {
Page<File> page = null;
List<File> list = null;
Sort sort = new Sort(Direction.DESC,"uploadDate");
Pageable pageable = new PageRequest(pageIndex, pageSize, sort);
page = fileRepository.findAll(pageable);
list = page.getContent();
return list;
}
}
- 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
控制层/API 资源层
FileController 控制器作为 API 的提供者,接收用户的请求及响应。API 的定义符合 RESTful 的风格。有关 REST 相关的知识,读者可以参阅笔者所著的开源书《[REST 实战]》(https://github.com/waylau/rest-in-action)。
@CrossOrigin(origins = "*", maxAge = 3600) // 允许所有域名访问
@Controller
public class FileController {
@Autowired
private FileService fileService;
@Value("${server.address}")
private String serverAddress;
@Value("${server.port}")
private String serverPort;
@RequestMapping(value = "/")
public String index(Model model) {
// 展示最新二十条数据
model.addAttribute("files", fileService.listFilesByPage(0,20));
return "index";
}
/**
* 分页查询文件
* @param pageIndex
* @param pageSize
* @return
*/
@GetMapping("files/{pageIndex}/{pageSize}")
@ResponseBody
public List<File> listFilesByPage(@PathVariable int pageIndex, @PathVariable int pageSize){
return fileService.listFilesByPage(pageIndex, pageSize);
}
/**
* 获取文件片信息
* @param id
* @return
*/
@GetMapping("files/{id}")
@ResponseBody
public ResponseEntity<Object> serveFile(@PathVariable String id) {
File file = fileService.getFileById(id);
if (file != null) {
return ResponseEntity
.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; fileName=\"" + file.getName() + "\"")
.header(HttpHeaders.CONTENT_TYPE, "application/octet-stream" )
.header(HttpHeaders.CONTENT_LENGTH, file.getSize()+"")
.header("Connection", "close")
.body( file.getContent());
} else {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("File was not fount");
}
}
/**
* 在线显示文件
* @param id
* @return
*/
@GetMapping("/view/{id}")
@ResponseBody
public ResponseEntity<Object> serveFileOnline(@PathVariable String id) {
File file = fileService.getFileById(id);
if (file != null) {
return ResponseEntity
.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "fileName=\"" + file.getName() + "\"")
.header(HttpHeaders.CONTENT_TYPE, file.getContentType() )
.header(HttpHeaders.CONTENT_LENGTH, file.getSize()+"")
.header("Connection", "close")
.body( file.getContent());
} else {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("File was not fount");
}
}
/**
* 上传
* @param file
* @param redirectAttributes
* @return
*/
@PostMapping("/")
public String handleFileUpload(@RequestParam("file") MultipartFile file,
RedirectAttributes redirectAttributes) {
try {
File f = new File(file.getOriginalFilename(), file.getContentType(), file.getSize(), file.getBytes());
f.setMd5( MD5Util.getMD5(file.getInputStream()) );
fileService.saveFile(f);
} catch (IOException | NoSuchAlgorithmException ex) {
ex.printStackTrace();
redirectAttributes.addFlashAttribute("message",
"Your " + file.getOriginalFilename() + " is wrong!");
return "redirect:/";
}
redirectAttributes.addFlashAttribute("message",
"You successfully uploaded " + file.getOriginalFilename() + "!");
return "redirect:/";
}
/**
* 上传接口
* @param file
* @return
*/
@PostMapping("/upload")
@ResponseBody
public ResponseEntity<String> handleFileUpload(@RequestParam("file") MultipartFile file) {
File returnFile = null;
try {
File f = new File(file.getOriginalFilename(), file.getContentType(), file.getSize(),file.getBytes());
f.setMd5( MD5Util.getMD5(file.getInputStream()) );
returnFile = fileService.saveFile(f);
String path = "//"+ serverAddress + ":" + serverPort + "/view/"+returnFile.getId();
return ResponseEntity.status(HttpStatus.OK).body(path);
} catch (IOException | NoSuchAlgorithmException ex) {
ex.printStackTrace();
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ex.getMessage());
}
}
/**
* 删除文件
* @param id
* @return
*/
@DeleteMapping("/{id}")
@ResponseBody
public ResponseEntity<String> deleteFile(@PathVariable String id) {
try {
fileService.removeFile(id);
return ResponseEntity.status(HttpStatus.OK).body("DELETE Success!");
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());
}
}
}
- 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
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 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
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
其中@CrossOrigin(origins = "*", maxAge = 3600)
注解标识了 API 可以被跨域请求。为了能够启用该注解,仍然需要安全配置类的支持。
安全配置
为了支持跨域请求,我们设置了安全配置类 SecurityConfig:
@Configuration
@EnableWebMvc
public class SecurityConfig extends WebMvcConfigurerAdapter {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*") ; // 允许跨域请求
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
运行
有多种方式可以运行 Gradle 的 Java 项目。使用 Spring Boot Gradle Plugin 插件运行是较为简便的一种方式,只需要执行:
$ gradlew bootRun
- 1
- 1
其他运行方式,请参阅笔者的开源书《Spring Boot 教程》
项目成功运行后,通过浏览器访问 http://localhost:8081 即可。首页提供了上传的演示界面,上传后,就能看到上传文件的详细信息:
相关上传的接口暴露在了 http://localhost:8081/ ,其中
- GET /files/{pageIndex}/{pageSize} : 分页查询已经上传了的文件
- GET /files/{id} : 下载某个文件
- GET /view/{id} : 在线预览某个文件。比如,显示图片
- POST /upload : 上传文件
- DELETE /{id} : 删除文件
http://blog.csdn.net/kkkloveyou/article/details/73196521
相关推荐
MongoDB文件服务器(基于MongoDB的文件服务器) MongoDB文件服务器是基于MongoDB的文件服务器系统。 MongoDB File Server致力于存储小文件,例如博客中的图片,普通文档等。 它使用了一些非常流行的技术,例如: ...
这个压缩包"Vue+axios+Spring Boot+mongoDB 基于Mongo图片存储和基于GridFS的文件存储.zip"提供了一个完整的解决方案,结合了前端Vue.js框架、axios库、后端Spring Boot微服务以及MongoDB数据库的GridFS文件系统,...
2. **配置MongoDB**: 在Spring Boot应用中整合MongoDB,首先需要在`application.properties`或`application.yml`文件中配置MongoDB的相关信息,包括连接地址、端口、数据库名称等。例如: ``` spring.data.mongodb...
在集成 Spring Boot 和 MongoDB 的过程中,我们可以利用 Spring Data MongoDB 模块来实现。Spring Data MongoDB 提供了与 MongoDB 交互的一系列抽象层,使得开发者能够更方便地操作数据库,而无需深入学习 MongoDB ...
基于node.js、vue、mongodb等技术构建的web系统,界面美观,功能齐全,适合用作毕业设计、课程设计作业等,项目均经过测试,可快速部署运行! 基于node.js、vue、mongodb等技术构建的web系统,界面美观,功能齐全,...
Spring Cloud是基于Spring Boot构建的一套微服务治理框架,它为开发者提供了在分布式系统(如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话、集群状态)操作...
- **自动配置**:Spring Boot 通过扫描特定的包来发现组件,并基于这些组件自动进行配置,如数据源、缓存、消息等。 - **起步依赖**:Spring Boot 通过“起步依赖”(Starter POMs)提供了一种简单的方式来添加功能...
Spring Boot是Spring框架的一个子项目,旨在简化Java应用程序的初始搭建以及开发过程,通过提供预配置的特性,如嵌入式Web服务器、自动配置、起步依赖等,极大地提高了开发效率。 本书首先会讲解Spring Boot 2的...
总结起来,本文介绍了如何使用Spring Boot搭建WebSocket服务器,实现广播和点对点通信,并集成MongoDB的GridFS进行文件存储,特别适用于图片服务器场景。同时,我们也讨论了非GridFS的文件存储方式及其16MB的限制。...
Spring Cloud是基于Spring Boot的一系列框架,它们使得开发人员可以快速构建分布式系统中的一些常见模式(如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话和...
2. **嵌入式服务器**:Spring Boot 支持嵌入式 Tomcat、Jetty 或 Undertow 服务器,这意味着你无需单独部署到外部应用服务器,可以直接运行打包后的 JAR 文件。 3. **自动配置(Auto Configuration)**:Spring ...
本项目"spring-cache-mongodb"正是一个针对Spring Cache的实现,它利用MongoDB作为底层的存储引擎。 MongoDB是一个流行的、高性能的、开源的文档数据库,它以JSON格式存储数据,非常适合处理结构灵活的数据。将...
6. **数据访问**:Spring Boot与多种数据库(如MySQL、MongoDB)有良好的集成。书中会讲述如何配置数据源,使用JPA或Hibernate进行对象关系映射,以及如何执行数据库查询。 7. **Actuator**:Spring Boot Actuator...
最后,Spring Boot内置了Tomcat服务器,使得你可以直接运行`jar`文件启动应用,无需额外部署到其他服务器上。通过`spring-boot-maven-plugin`插件,你可以方便地打包应用为可执行的JAR或WAR文件。 总的来说,...
Spring Boot 2提供了对MongoDB的便捷支持,包括自动配置连接到MongoDB服务器、数据访问对象(DAO)的支持以及文档模型化的工具。通过`spring-boot-starter-data-mongodb`起步依赖,你可以轻松地将MongoDB整合进你的...
本文将深入探讨一个基于Spring Boot、GraphQL和MongoDB实现的物资管理系统后端设计,帮助读者理解这三大技术在实际项目中的应用与整合。 首先,Spring Boot是Java开发领域的轻量级框架,以其简化Spring应用的初始...
Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅... 本文档是Spring框架高级源码笔记+Spring Boot data mongoDB中文文档 资源合集
在本项目中,"Spring Boot + Netty 实现温湿度采集的展示" 是一个结合了Spring Boot微服务框架和Netty高性能网络库的系统,用于实时监控和展示环境中的温湿度数据。这样的系统通常用于物联网(IoT)场景,如智能家居、...
3. **内嵌式Web服务器**:Spring Boot支持内嵌Tomcat、Jetty或Undertow服务器,无需额外部署,直接运行jar文件即可启动应用。 4. **健康检查**:通过`/actuator/health`端点,Spring Boot可以提供应用的健康检查,...