原文:Mkyong
Spring Boot WebFlux +服务器发送的事件示例
在本文中,我们将向您展示如何使用服务器发送的事件开发一个反应式 web 应用程序
- Spring Boot 2.1.2 .版本
- Spring WebFlux 5.1.4 .发行版
- 百里香叶
- JUnit 5.3.2
- maven3
在 Spring 中,返回 JSON 和 header MediaType.TEXT_EVENT_STREAM_VALUE
@RestController
public class CommentController {
@GetMapping(path = "/comment/stream",
produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<Comment> feed() {
//...
}
}
在 Javascript 中,使用EventSource
向上述端点发送请求。
function loadComments () {
this.source = null;
this.start = function () {
this.source = new EventSource("/comment/stream");
this.source.addEventListener("message", function (event) {
var comment = JSON.parse(event.data);
//... update somewhere
});
this.source.onerror = function () {
this.close();
};
};
this.stop = function() {
this.source.close();
}
}
comment = new loadComments();
window.onload = function() {
comment.start();
};
window.onbeforeunload = function() {
comment.stop();
}
1.项目目录
## 2.专家
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project
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>
<groupId>com.mkyong.spring.reactive</groupId>
<artifactId>webflux-thymeleaf-sse</artifactId>
<version>1.0</version>
<properties>
<java.version>1.8</java.version>
<junit-jupiter.version>5.3.2</junit-jupiter.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
</parent>
<dependencies>
<!-- webflux reactive -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- thymeleaf -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- exclude junit 4, prefer junit 5 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- junit 5 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${
junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
</plugin>
</plugins>
</build>
</project>
显示项目依赖关系。
$ mvn dependency:tree
[INFO] com.mkyong.spring.reactive:webflux-thymeleaf-sse:jar:1.0
[INFO] +- org.springframework.boot:spring-boot-starter-webflux:jar:2.1.2.RELEASE:compile
[INFO] | +- org.springframework.boot:spring-boot-starter:jar:2.1.2.RELEASE:compile
[INFO] | | +- org.springframework.boot:spring-boot-starter-logging:jar:2.1.2.RELEASE:compile
[INFO] | | | +- ch.qos.logback:logback-classic:jar:1.2.3:compile
[INFO] | | | | \- ch.qos.logback:logback-core:jar:1.2.3:compile
[INFO] | | | +- org.apache.logging.log4j:log4j-to-slf4j:jar:2.11.1:compile
[INFO] | | | | \- org.apache.logging.log4j:log4j-api:jar:2.11.1:compile
[INFO] | | | \- org.slf4j:jul-to-slf4j:jar:1.7.25:compile
[INFO] | | +- javax.annotation:javax.annotation-api:jar:1.3.2:compile
[INFO] | | \- org.yaml:snakeyaml:jar:1.23:runtime
[INFO] | +- org.springframework.boot:spring-boot-starter-json:jar:2.1.2.RELEASE:compile
[INFO] | | +- com.fasterxml.jackson.core:jackson-databind:jar:2.9.8:compile
[INFO] | | | +- com.fasterxml.jackson.core:jackson-annotations:jar:2.9.0:compile
[INFO] | | | \- com.fasterxml.jackson.core:jackson-core:jar:2.9.8:compile
[INFO] | | +- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:jar:2.9.8:compile
[INFO] | | +- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:jar:2.9.8:compile
[INFO] | | \- com.fasterxml.jackson.module:jackson-module-parameter-names:jar:2.9.8:compile
[INFO] | +- org.springframework.boot:spring-boot-starter-reactor-netty:jar:2.1.2.RELEASE:compile
[INFO] | | \- io.projectreactor.netty:reactor-netty:jar:0.8.4.RELEASE:compile
[INFO] | | +- io.netty:netty-codec-http:jar:4.1.31.Final:compile
[INFO] | | | \- io.netty:netty-codec:jar:4.1.31.Final:compile
[INFO] | | +- io.netty:netty-codec-http2:jar:4.1.31.Final:compile
[INFO] | | +- io.netty:netty-handler:jar:4.1.31.Final:compile
[INFO] | | | +- io.netty:netty-buffer:jar:4.1.31.Final:compile
[INFO] | | | \- io.netty:netty-transport:jar:4.1.31.Final:compile
[INFO] | | | \- io.netty:netty-resolver:jar:4.1.31.Final:compile
[INFO] | | +- io.netty:netty-handler-proxy:jar:4.1.31.Final:compile
[INFO] | | | \- io.netty:netty-codec-socks:jar:4.1.31.Final:compile
[INFO] | | \- io.netty:netty-transport-native-epoll:jar:linux-x86_64:4.1.31.Final:compile
[INFO] | | +- io.netty:netty-common:jar:4.1.31.Final:compile
[INFO] | | \- io.netty:netty-transport-native-unix-common:jar:4.1.31.Final:compile
[INFO] | +- org.hibernate.validator:hibernate-validator:jar:6.0.14.Final:compile
[INFO] | | +- javax.validation:validation-api:jar:2.0.1.Final:compile
[INFO] | | +- org.jboss.logging:jboss-logging:jar:3.3.2.Final:compile
[INFO] | | \- com.fasterxml:classmate:jar:1.4.0:compile
[INFO] | +- org.springframework:spring-web:jar:5.1.4.RELEASE:compile
[INFO] | | \- org.springframework:spring-beans:jar:5.1.4.RELEASE:compile
[INFO] | +- org.springframework:spring-webflux:jar:5.1.4.RELEASE:compile
[INFO] | | \- io.projectreactor:reactor-core:jar:3.2.5.RELEASE:compile
[INFO] | | \- org.reactivestreams:reactive-streams:jar:1.0.2:compile
[INFO] | \- org.synchronoss.cloud:nio-multipart-parser:jar:1.1.0:compile
[INFO] | +- org.slf4j:slf4j-api:jar:1.7.25:compile
[INFO] | \- org.synchronoss.cloud:nio-stream-storage:jar:1.1.3:compile
[INFO] +- org.springframework.boot:spring-boot-starter-thymeleaf:jar:2.1.2.RELEASE:compile
[INFO] | +- org.thymeleaf:thymeleaf-spring5:jar:3.0.11.RELEASE:compile
[INFO] | | \- org.thymeleaf:thymeleaf:jar:3.0.11.RELEASE:compile
[INFO] | | +- org.attoparser:attoparser:jar:2.0.5.RELEASE:compile
[INFO] | | \- org.unbescape:unbescape:jar:1.1.6.RELEASE:compile
[INFO] | \- org.thymeleaf.extras:thymeleaf-extras-java8time:jar:3.0.2.RELEASE:compile
[INFO] +- org.springframework.boot:spring-boot-starter-test:jar:2.1.2.RELEASE:test
[INFO] | +- org.springframework.boot:spring-boot-test:jar:2.1.2.RELEASE:test
[INFO] | +- org.springframework.boot:spring-boot-test-autoconfigure:jar:2.1.2.RELEASE:test
[INFO] | +- com.jayway.jsonpath:json-path:jar:2.4.0:test
[INFO] | | \- net.minidev:json-smart:jar:2.3:test
[INFO] | | \- net.minidev:accessors-smart:jar:1.2:test
[INFO] | | \- org.ow2.asm:asm:jar:5.0.4:test
[INFO] | +- org.assertj:assertj-core:jar:3.11.1:test
[INFO] | +- org.mockito:mockito-core:jar:2.23.4:test
[INFO] | | +- net.bytebuddy:byte-buddy:jar:1.9.7:test
[INFO] | | +- net.bytebuddy:byte-buddy-agent:jar:1.9.7:test
[INFO] | | \- org.objenesis:objenesis:jar:2.6:test
[INFO] | +- org.hamcrest:hamcrest-core:jar:1.3:test
[INFO] | +- org.hamcrest:hamcrest-library:jar:1.3:test
[INFO] | +- org.skyscreamer:jsonassert:jar:1.5.0:test
[INFO] | | \- com.vaadin.external.google:android-json:jar:0.0.20131108.vaadin1:test
[INFO] | +- org.springframework:spring-core:jar:5.1.4.RELEASE:compile
[INFO] | | \- org.springframework:spring-jcl:jar:5.1.4.RELEASE:compile
[INFO] | +- org.springframework:spring-test:jar:5.1.4.RELEASE:test
[INFO] | \- org.xmlunit:xmlunit-core:jar:2.6.2:test
[INFO] | \- javax.xml.bind:jaxb-api:jar:2.3.1:test
[INFO] | \- javax.activation:javax.activation-api:jar:1.2.0:test
[INFO] +- org.junit.jupiter:junit-jupiter-engine:jar:5.3.2:test
[INFO] | +- org.apiguardian:apiguardian-api:jar:1.0.0:test
[INFO] | +- org.junit.platform:junit-platform-engine:jar:1.3.2:test
[INFO] | | +- org.junit.platform:junit-platform-commons:jar:1.3.2:test
[INFO] | | \- org.opentest4j:opentest4j:jar:1.1.1:test
[INFO] | \- org.junit.jupiter:junit-jupiter-api:jar:5.3.2:test
[INFO] \- org.springframework.boot:spring-boot-devtools:jar:2.1.2.RELEASE:compile (optional)
[INFO] +- org.springframework.boot:spring-boot:jar:2.1.2.RELEASE:compile
[INFO] | \- org.springframework:spring-context:jar:5.1.4.RELEASE:compile
[INFO] | +- org.springframework:spring-aop:jar:5.1.4.RELEASE:compile
[INFO] | \- org.springframework:spring-expression:jar:5.1.4.RELEASE:compile
[INFO] \- org.springframework.boot:spring-boot-autoconfigure:jar:2.1.2.RELEASE:compile
3.Spring Boot +春网流量
3.1 基于 Spring WebFlux 注释的控制器。启用数据流。写produces = MediaType.TEXT_EVENT_STREAM_VALUE
CommentController.java
package com.mkyong.reactive.controller;
import com.mkyong.reactive.model.Comment;
import com.mkyong.reactive.repository.CommentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
public class CommentController {
@Autowired
private CommentRepository commentRepository;
@GetMapping(path = "/comment/stream",
produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<Comment> feed() {
return this.commentRepository.findAll();
}
}
MainController.java
package com.mkyong.reactive.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class MainController {
@GetMapping("/")
public String index(final Model model) {
return "index";
}
}
3.2 在repository
中,返回一个Flux
对象。
CommentRepository.java
package com.mkyong.reactive.repository;
import com.mkyong.reactive.model.Comment;
import reactor.core.publisher.Flux;
public interface CommentRepository {
Flux<Comment> findAll();
}
ReactiveCommentRepository.java
package com.mkyong.reactive.repository;
import com.mkyong.reactive.model.Comment;
import com.mkyong.reactive.utils.CommentGenerator;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Flux;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
@Repository
public class ReactiveCommentRepository implements CommentRepository {
@Override
public Flux<Comment> findAll() {
//simulate data streaming every 1 second.
return Flux.interval(Duration.ofSeconds(1))
.onBackpressureDrop()
.map(this::generateComment)
.flatMapIterable(x -> x);
}
private List<Comment> generateComment(long interval) {
Comment obj = new Comment(
CommentGenerator.randomAuthor(),
CommentGenerator.randomMessage(),
CommentGenerator.getCurrentTimeStamp());
return Arrays.asList(obj);
}
}
3.3 生成随机注释的 utils 类。
CommentGenerator.java
package com.mkyong.reactive.utils;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
public class CommentGenerator {
private static final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
private static final Random RANDOM = new Random(System.currentTimeMillis());
private static final List<String> COMMENT_AUTHOR =
Arrays.asList(
"Mkyong", "Oliver", "Jack", "Harry", "Jacob",
"Isla", "Emily", "Poppy", "Ava", "Isabella");
private static final List<String> COMMENT_MESSAGE =
Arrays.asList(
"I Love this!",
"Me too!",
"Wow",
"True!",
"Hello everyone here?",
"Good!");
public static String randomAuthor() {
return COMMENT_AUTHOR.get(RANDOM.nextInt(COMMENT_AUTHOR.size()));
}
public static String randomMessage() {
return COMMENT_MESSAGE.get(RANDOM.nextInt(COMMENT_MESSAGE.size()));
}
public static String getCurrentTimeStamp() {
return dtf.format(LocalDateTime.now());
}
}
3.4 评论模式。
Movie.java
package com.mkyong.reactive.model;
public class Comment {
private String author;
private String message;
private String timestamp;
//getter, setter and constructor
}
3.5 开始 Spring Boot。
CommentWebApplication.java
package com.mkyong.reactive;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class CommentWebApplication {
public static void main(String[] args) {
SpringApplication.run(CommentWebApplication.class, args);
}
}
4.百里香叶
百里香模板中没有特殊的反应标签,只是使用了普通的循环。
resources/templates/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link data-th-href="@{/css/bootstrap.min.css}" rel="stylesheet">
<link data-th-href="@{/css/main.css}" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row">
<div id="title">
<h1>Spring WebFlux + Server Sent Events</h1>
</div>
<table id="comments" class="table table-striped">
<thead>
<tr>
<th width="10%">Author</th>
<th width="60%">Message</th>
<th width="30%">Date</th>
</tr>
</thead>
<tbody>
<tr class="result" data-th-each="comment : ${comments}">
<td>[[${
comment.author}]]</td>
<td>[[${
comment.message}]]</td>
<td>[[${
comment.timestamp}]]</td>
</tr>
</tbody>
</table>
</div>
</div>
<script data-th-src="@{/js/main.js}"></script>
</body>
</html>
5.JavaScript 事件源。
关键是使用 Javascript EventSource
类发送请求并监听message
事件,并将流数据反应性地更新到一个表中。
resources/static/js/main.js
function loadComments () {
this.source = null;
this.start = function () {
var commentTable = document.getElementById("comments");
this.source = new EventSource("/comment/stream");
this.source.addEventListener("message", function (event) {
// These events are JSON, so parsing and DOM fiddling are needed
var comment = JSON.parse(event.data);
var row = commentTable.getElementsByTagName("tbody")[0].insertRow(0);
var cell0 = row.insertCell(0);
var cell1 = row.insertCell(1);
var cell2 = row.insertCell(2);
cell0.className = "author-style";
cell0.innerHTML = comment.author;
cell1.className = "text";
cell1.innerHTML = comment.message;
cell2.className = "date";
cell2.innerHTML = comment.timestamp;
});
this.source.onerror = function () {
this.close();
};
};
this.stop = function() {
this.source.close();
}
}
comment = new loadComments();
/*
* Register callbacks for starting and stopping the SSE controller.
*/
window.onload = function() {
comment.start();
};
window.onbeforeunload = function() {
comment.stop();
}
6.单元测试
WebTestClient
对流响应进行单元测试
TestCommentWebApplication.java
package com.mkyong.reactive;
import com.mkyong.reactive.model.Comment;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class TestCommentWebApplication {
@Autowired
private WebTestClient webClient;
@Test
public void testCommentStream() {
List<Comment> comments = webClient
.get().uri("/comment/stream")
.accept(MediaType.valueOf(MediaType.TEXT_EVENT_STREAM_VALUE))
.exchange()
.expectStatus().isOk()
.returnResult(Comment.class)
.getResponseBody()
.take(3) // take 3 comment objects
.collectList()
.block();
comments.forEach(x -> System.out.println(x));
assertEquals(3, comments.size());
}
}
7.演示
$ mvn spring-boot:run
2019-02-11 15:41:17.657 INFO 257192 --- [ restartedMain] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 8080
URL =http://localhost:8080
数据是流式的,每 1 秒就会显示随机评论。
下载源代码
$ git clone https://github.com/mkyong/spring-boot.git
$ cd webflux-thymeleaf-serversentevent
$ mvn spring-boot:run
参考
junit 5 reactive spring boot sse thymeleaf webflux
Spring Boot WebFlux +百里香叶反应实例
在本文中,我们将向您展示如何开发一个反应式 web 应用程序。
- Spring Boot 2.1.2 .版本
- Spring WebFlux 5.1.4 .发行版
- 百里香叶
- maven3
Spring Boot 将配置一切,关键是使用百里香叶ReactiveDataDriverContextVariable
来启用百里香叶模板中的数据驱动模式。
@RequestMapping("/")
public String index(final Model model) {
// data streaming, data driven mode.
IReactiveDataDriverContextVariable reactiveDataDrivenMode =
new ReactiveDataDriverContextVariable(movieRepository.findAll(), 1);
model.addAttribute("movies", reactiveDataDrivenMode);
return "view";
}
Note
Working on the Spring Boot WebFlux + Thymeleaf + Server-Sent Events (SSE) integeration. To be updated here.
1.项目目录
2.专家
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project
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>
<groupId>com.mkyong.spring.reactive</groupId>
<artifactId>webflux-thymeleaf</artifactId>
<version>1.0</version>
<properties>
<java.version>1.8</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
</parent>
<dependencies>
<!-- reactive -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- just include the normal thymeleaf -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
显示项目依赖关系。
$ mvn dependency:tree
[INFO] com.mkyong.spring.webflux:ReactiveApp:jar:1.0-SNAPSHOT
[INFO] +- org.springframework.boot:spring-boot-starter-webflux:jar:2.1.2.RELEASE:compile
[INFO] | +- org.springframework.boot:spring-boot-starter:jar:2.1.2.RELEASE:compile
[INFO] | | +- org.springframework.boot:spring-boot:jar:2.1.2.RELEASE:compile
[INFO] | | | \- org.springframework:spring-context:jar:5.1.4.RELEASE:compile
[INFO] | | | +- org.springframework:spring-aop:jar:5.1.4.RELEASE:compile
[INFO] | | | \- org.springframework:spring-expression:jar:5.1.4.RELEASE:compile
[INFO] | | +- org.springframework.boot:spring-boot-autoconfigure:jar:2.1.2.RELEASE:compile
[INFO] | | +- org.springframework.boot:spring-boot-starter-logging:jar:2.1.2.RELEASE:compile
[INFO] | | | +- ch.qos.logback:logback-classic:jar:1.2.3:compile
[INFO] | | | | \- ch.qos.logback:logback-core:jar:1.2.3:compile
[INFO] | | | +- org.apache.logging.log4j:log4j-to-slf4j:jar:2.11.1:compile
[INFO] | | | | \- org.apache.logging.log4j:log4j-api:jar:2.11.1:compile
[INFO] | | | \- org.slf4j:jul-to-slf4j:jar:1.7.25:compile
[INFO] | | +- javax.annotation:javax.annotation-api:jar:1.3.2:compile
[INFO] | | \- org.yaml:snakeyaml:jar:1.23:runtime
[INFO] | +- org.springframework.boot:spring-boot-starter-json:jar:2.1.2.RELEASE:compile
[INFO] | | +- com.fasterxml.jackson.core:jackson-databind:jar:2.9.8:compile
[INFO] | | | +- com.fasterxml.jackson.core:jackson-annotations:jar:2.9.0:compile
[INFO] | | | \- com.fasterxml.jackson.core:jackson-core:jar:2.9.8:compile
[INFO] | | +- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:jar:2.9.8:compile
[INFO] | | +- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:jar:2.9.8:compile
[INFO] | | \- com.fasterxml.jackson.module:jackson-module-parameter-names:jar:2.9.8:compile
[INFO] | +- org.springframework.boot:spring-boot-starter-reactor-netty:jar:2.1.2.RELEASE:compile
[INFO] | | \- io.projectreactor.netty:reactor-netty:jar:0.8.4.RELEASE:compile
[INFO] | | +- io.netty:netty-codec-http:jar:4.1.31.Final:compile
[INFO] | | | \- io.netty:netty-codec:jar:4.1.31.Final:compile
[INFO] | | +- io.netty:netty-codec-http2:jar:4.1.31.Final:compile
[INFO] | | +- io.netty:netty-handler:jar:4.1.31.Final:compile
[INFO] | | | +- io.netty:netty-buffer:jar:4.1.31.Final:compile
[INFO] | | | \- io.netty:netty-transport:jar:4.1.31.Final:compile
[INFO] | | | \- io.netty:netty-resolver:jar:4.1.31.Final:compile
[INFO] | | +- io.netty:netty-handler-proxy:jar:4.1.31.Final:compile
[INFO] | | | \- io.netty:netty-codec-socks:jar:4.1.31.Final:compile
[INFO] | | \- io.netty:netty-transport-native-epoll:jar:linux-x86_64:4.1.31.Final:compile
[INFO] | | +- io.netty:netty-common:jar:4.1.31.Final:compile
[INFO] | | \- io.netty:netty-transport-native-unix-common:jar:4.1.31.Final:compile
[INFO] | +- org.hibernate.validator:hibernate-validator:jar:6.0.14.Final:compile
[INFO] | | +- javax.validation:validation-api:jar:2.0.1.Final:compile
[INFO] | | +- org.jboss.logging:jboss-logging:jar:3.3.2.Final:compile
[INFO] | | \- com.fasterxml:classmate:jar:1.4.0:compile
[INFO] | +- org.springframework:spring-web:jar:5.1.4.RELEASE:compile
[INFO] | | \- org.springframework:spring-beans:jar:5.1.4.RELEASE:compile
[INFO] | +- org.springframework:spring-webflux:jar:5.1.4.RELEASE:compile
[INFO] | | \- io.projectreactor:reactor-core:jar:3.2.5.RELEASE:compile
[INFO] | | \- org.reactivestreams:reactive-streams:jar:1.0.2:compile
[INFO] | \- org.synchronoss.cloud:nio-multipart-parser:jar:1.1.0:compile
[INFO] | +- org.slf4j:slf4j-api:jar:1.7.25:compile
[INFO] | \- org.synchronoss.cloud:nio-stream-storage:jar:1.1.3:compile
[INFO] +- org.springframework.boot:spring-boot-starter-thymeleaf:jar:2.1.2.RELEASE:compile
[INFO] | +- org.thymeleaf:thymeleaf-spring5:jar:3.0.11.RELEASE:compile
[INFO] | | \- org.thymeleaf:thymeleaf:jar:3.0.11.RELEASE:compile
[INFO] | | +- org.attoparser:attoparser:jar:2.0.5.RELEASE:compile
[INFO] | | \- org.unbescape:unbescape:jar:1.1.6.RELEASE:compile
[INFO] | \- org.thymeleaf.extras:thymeleaf-extras-java8time:jar:3.0.2.RELEASE:compile
[INFO] \- org.springframework.boot:spring-boot-starter-test:jar:2.1.2.RELEASE:test
[INFO] +- org.springframework.boot:spring-boot-test:jar:2.1.2.RELEASE:test
[INFO] +- org.springframework.boot:spring-boot-test-autoconfigure:jar:2.1.2.RELEASE:test
[INFO] +- com.jayway.jsonpath:json-path:jar:2.4.0:test
[INFO] | \- net.minidev:json-smart:jar:2.3:test
[INFO] | \- net.minidev:accessors-smart:jar:1.2:test
[INFO] | \- org.ow2.asm:asm:jar:5.0.4:test
[INFO] +- junit:junit:jar:4.12:test
[INFO] +- org.assertj:assertj-core:jar:3.11.1:test
[INFO] +- org.mockito:mockito-core:jar:2.23.4:test
[INFO] | +- net.bytebuddy:byte-buddy:jar:1.9.7:test
[INFO] | +- net.bytebuddy:byte-buddy-agent:jar:1.9.7:test
[INFO] | \- org.objenesis:objenesis:jar:2.6:test
[INFO] +- org.hamcrest:hamcrest-core:jar:1.3:test
[INFO] +- org.hamcrest:hamcrest-library:jar:1.3:test
[INFO] +- org.skyscreamer:jsonassert:jar:1.5.0:test
[INFO] | \- com.vaadin.external.google:android-json:jar:0.0.20131108.vaadin1:test
[INFO] +- org.springframework:spring-core:jar:5.1.4.RELEASE:compile
[INFO] | \- org.springframework:spring-jcl:jar:5.1.4.RELEASE:compile
[INFO] +- org.springframework:spring-test:jar:5.1.4.RELEASE:test
[INFO] \- org.xmlunit:xmlunit-core:jar:2.6.2:test
[INFO] \- javax.xml.bind:jaxb-api:jar:2.3.1:test
[INFO] \- javax.activation:javax.activation-api:jar:1.2.0:test
3.Spring Boot +春网流量
3.1 基于 Spring WebFlux 注释的控制器。用ReactiveDataDriverContextVariable
包装数据,将在百里香模板中启用反应式数据驱动模型。
MovieController.java
package com.mkyong.reactive.controller;
import com.mkyong.reactive.repository.MovieRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.thymeleaf.spring5.context.webflux.IReactiveDataDriverContextVariable;
import org.thymeleaf.spring5.context.webflux.ReactiveDataDriverContextVariable;
@Controller
public class MovieController {
@Autowired
private MovieRepository movieRepository;
@RequestMapping("/")
public String index(final Model model) {
// loads 1 and display 1, stream data, data driven mode.
IReactiveDataDriverContextVariable reactiveDataDrivenMode =
new ReactiveDataDriverContextVariable(movieRepository.findAll(), 1);
model.addAttribute("movies", reactiveDataDrivenMode);
// classic, wait repository loaded all and display it.
//model.addAttribute("movies", movieRepository.findAll());
return "index";
}
}
3.2 在repository
中,返回一个Flux
对象。
MovieRepository.java
package com.mkyong.reactive.repository;
import com.mkyong.reactive.Movie;
import reactor.core.publisher.Flux;
public interface MovieRepository {
Flux<Movie> findAll();
}
ReactiveMovieRepository.java
package com.mkyong.reactive.repository;
import com.mkyong.reactive.Movie;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Flux;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
@Repository
public class ReactiveMovieRepository implements MovieRepository {
private static List<Movie> movie = new ArrayList<>();
static {
movie.add(new Movie("Polar (2019)", 64));
movie.add(new Movie("Iron Man (2008)", 79));
movie.add(new Movie("The Shawshank Redemption (1994)", 93));
movie.add(new Movie("Forrest Gump (1994)", 83));
movie.add(new Movie("Glass (2019)", 70));
}
@Override
public Flux<Movie> findAll() {
//Simulate big list of data, streaming it every 2 second delay
return Flux.fromIterable(movie).delayElements(Duration.ofSeconds(2));
}
}
3.3 电影模式。
Movie.java
package com.mkyong.reactive;
public class Movie {
private String name;
private Integer score;
//getter, setter and constructor
}
3.4 启动 Spring Boot。
MovieWebApplication.java
package com.mkyong.reactive;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MovieWebApplication {
public static void main(String[] args) {
SpringApplication.run(MovieWebApplication.class, args);
}
}
4.百里香叶
百里香模板中没有特殊的反应标签,只是使用了普通的循环。
resources/templates/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link data-th-href="@{/css/bootstrap.min.css}" rel="stylesheet">
<link data-th-href="@{/css/main.css}" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row">
<div id="title">
<h1>Spring WebFlux + Thymeleaf</h1>
</div>
<table id="allMovies" class="table table-striped">
<thead>
<tr>
<th width="70%">Name</th>
<th>Score</th>
</tr>
</thead>
<tbody>
<tr class="result" data-th-each="movie : ${movies}">
<td>[[${
movie.name}]]</td>
<td>[[${
movie.score}]]</td>
</tr>
</tbody>
</table>
</div>
</div>
定义了块大小。
application.properties
spring.thymeleaf.reactive.max-chunk-size=8192
完成了。
5.演示
$ mvn spring-boot:run
URL =http://localhost:8080
数据是流式的,将每 2 秒钟以反应方式显示一次。
下载源代码
$ git clone https://github.com/mkyong/spring-boot.git
$ cd webflux-thymeleaf
$ mvn spring-boot:run
参考
Spring Boot——从哪门主课开始
如果 Spring Boot 项目包含多个主类,Spring Boot 将无法启动或打包进行部署。
Terminal
$ mvn package
#or
$ mvn spring-boot:run
Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:1.4.2.RELEASE:run (default-cli)
Execution default-cli of goal org.springframework.boot:spring-boot-maven-plugin:1.4.2.RELEASE:run failed:
Unable to find a single main class from the following candidates
[com.mkyong.Test, com.mkyong.SpringBootWebApplication] -> [Help 1]
Maven 示例
1.1 通过start-class
属性定义单个主类
pom.xml
<properties>
<!-- The main class to start by executing java -jar -->
<start-class>com.mkyong.SpringBootWebApplication</start-class>
</properties>
1.2 或者,在spring-boot-maven-plugin
中定义主类
pom.xml
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.mkyong.SpringBootWebApplication</mainClass>
</configuration>
</plugin>
</plugins>
</build>
参考
executable jar jar main class spring boot war (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById®;if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!‘undefined’){i[c]++} else{i[c]=1;} })(window, document, ‘InContent’, ‘script’, ‘mediaType’, ‘carambola_proxy’,‘Cbola_IC’,‘localStorage’,‘set’,‘get’,‘Item’,‘cbolaDt’,‘//web.archive.org/web/20190217051121/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0’)
Spring Boot YAML 的例子
原文:http://web.archive.org/web/20230101150211/https://mkyong.com/spring-boot/spring-boot-yaml-example/
在本文中,我们将向您展示如何使用 YAML 来代替 Spring Boot 中的属性文件。
测试对象:
- Spring Boot 2.1.2 .版本
- maven3
- 蛇瓶:1.23
简而言之,在src/resources
文件夹中创建一个application.yml
,Spring Boot 会自动加载并解析.yml
文件,并将值绑定到标注有@ConfigurationProperties
的类中
不能使用@PropertySource
加载 P.S YAML 文件
1.YAML 和房地产
application.yml
logging:
level:
org.springframework: ERROR
com.mkyong: DEBUG
spring:
profiles:
active: dev
main:
banner-mode: off
email: yaml@mkyong.com
thread-pool: 10
wordpress:
menus:
- title: Home
name: Home
path: /
- title: About
name: About
path: /about
themes:
default-folder: /wp-content/themes/mkyong
servers:
- ip: 127.0.0.1
path: /dev1
- ip: 127.0.0.2
path: /dev2
- ip: 127.0.0.3
path: /dev3
application.properties
# Spring Boot
logging.level.org.springframework=ERROR
logging.level.com.mkyong=DEBUG
spring.profiles.active=dev
spring.main.banner-mode=off
# Global
email=properties@mkyong.com
thread-pool=10
# WordPress
wordpress.menus[0].title=Home
wordpress.menus[0].name=Home
wordpress.menus[0].path=/
wordpress.menus[1].title=About
wordpress.menus[1].name=About
wordpress.menus[1].path=/about
wordpress.themes.default-folder=/wp-content/themes/mkyong
wordpress.servers[0].ip=127.0.0.1
wordpress.servers[0].path=/dev1
wordpress.servers[1].ip=127.0.0.2
wordpress.servers[1].path=/dev2
wordpress.servers[2].ip=127.0.0.3
wordpress.servers[2].path=/dev3
2.项目结构
3.项目依赖性
Spring Boot 使用 SnakeYAML 库解析 YAML 文件,SnakeYAML 库由spring-boot-starter
提供
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project
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>
<artifactId>spring-boot-yaml-example</artifactId>
<packaging>jar</packaging>
<name>Spring Boot YAML Example</name>
<url>https://www.mkyong.com</url>
<version>1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
</plugin>
</plugins>
</build>
</project>
项目依赖关系:
+- org.springframework.boot:spring-boot-starter:jar:2.1.2.RELEASE:compile
[INFO] | +- org.springframework.boot:spring-boot:jar:2.1.2.RELEASE:compile
[INFO] | | \- org.springframework:spring-context:jar:5.1.4.RELEASE:compile
[INFO] | | +- org.springframework:spring-aop:jar:5.1.4.RELEASE:compile
[INFO] | | +- org.springframework:spring-beans:jar:5.1.4.RELEASE:compile
[INFO] | | \- org.springframework:spring-expression:jar:5.1.4.RELEASE:compile
[INFO] | +- org.springframework.boot:spring-boot-autoconfigure:jar:2.1.2.RELEASE:compile
[INFO] | +- org.springframework.boot:spring-boot-starter-logging:jar:2.1.2.RELEASE:compile
[INFO] | | +- ch.qos.logback:logback-classic:jar:1.2.3:compile
[INFO] | | | \- ch.qos.logback:logback-core:jar:1.2.3:compile
[INFO] | | +- org.apache.logging.log4j:log4j-to-slf4j:jar:2.11.1:compile
[INFO] | | | \- org.apache.logging.log4j:log4j-api:jar:2.11.1:compile
[INFO] | | \- org.slf4j:jul-to-slf4j:jar:1.7.25:compile
[INFO] | +- javax.annotation:javax.annotation-api:jar:1.3.2:compile
[INFO] | +- org.springframework:spring-core:jar:5.1.4.RELEASE:compile
[INFO] | | \- org.springframework:spring-jcl:jar:5.1.4.RELEASE:compile
[INFO] | \- org.yaml:snakeyaml:jar:1.23:runtime <<------------- SnakeYAML
4.Spring Boot + YAML
4.1 Spring Boot 将加载并解析 YAML 文件,并将值绑定到下面的@ConfigurationProperties
类中。
GlobalProperties.java
package com.mkyong.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties // no prefix, root level.
public class GlobalProperties {
//thread-pool , relax binding
private int threadPool;
private String email;
//... getters and setters, toString()
}
WordPressProperties.java
package com.mkyong.config;
import com.mkyong.config.model.Menu;
import com.mkyong.config.model.Server;
import com.mkyong.config.model.Theme;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
@ConfigurationProperties("wordpress")
public class WordPressProperties {
private List<Menu> menus = new ArrayList<>();
private Theme themes;
private List<Server> servers = new ArrayList<>();
//... getters and setters, toString()
}
4.2 模型,没什么特别的,就是一些标准类。
Menu.java
package com.mkyong.config.model;
public class Menu {
private String name;
private String path;
private String title;
//... getters and setters, toString()
}
Server.java
package com.mkyong.config.model;
public class Server {
private String ip;
private String path;
//... getters and setters, toString()
}
Theme.java
package com.mkyong.config.model;
public class Theme {
private String defaultFolder;
//... getters and setters, toString()
}
4.3 正常启动 Spring Boot,并打印出数值。
Application.java
package com.mkyong;
import com.mkyong.config.GlobalProperties;
import com.mkyong.config.WordpressProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application implements CommandLineRunner {
@Autowired
private WordPressProperties wpProperties;
@Autowired
private GlobalProperties globalProperties;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Override
public void run(String... args) {
System.out.println(globalProperties);
System.out.println(wpProperties);
}
}
5.演示
打包并运行它。
# run spring boot directly
$ mvn spring-boot:run
# or package and run it
$ mvn package
$ java -jar target/spring-boot-yaml-example-1.0.jar
输出
GlobalProperties{
threadPool=10,
email='[email protected]'
}
WordpressProperties{
menus=[
Menu{
name='Home', path='/', title='Home'},
Menu{
name='About', path='/about', title='About'}
],
themes=Theme{
defaultFolder='/wp-content/themes/mkyong'},
servers=[
Server{
ip='127.0.0.1', path='/dev1'},
Server{
ip='127.0.0.2', path='/dev2'},
Server{
ip='127.0.0.3', path='/dev3'}
]
}
YAML Multi-Profiles
Refer to this Spring Boot + Multiple Profiles YAML example
下载源代码
$ git clone https://github.com/mkyong/spring-boot.git
$ cd yaml-simple
$ mvn spring-boot:run
参考
Spring 集合(列表、集合、映射和属性)示例
Spring 示例向您展示了如何将值注入集合类型(列表、集合、映射和属性)。支持 4 种主要集合类型:
- 列表—
- 设置-
- 地图—
- 属性—
春豆
一个客户对象,具有四个集合属性。
package com.mkyong.common;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class Customer
{
private List<Object> lists;
private Set<Object> sets;
private Map<Object, Object> maps;
private Properties pros;
//...
}
查看不同的代码片段以在 bean 配置文件中声明集合。
1.列表示例
<property name="lists">
<list>
<value>1</value>
<ref bean="PersonBean" />
<bean class="com.mkyong.common.Person">
<property name="name" value="mkyongList" />
<property name="address" value="address" />
<property name="age" value="28" />
</bean>
</list>
</property>
2.树立榜样
<property name="sets">
<set>
<value>1</value>
<ref bean="PersonBean" />
<bean class="com.mkyong.common.Person">
<property name="name" value="mkyongSet" />
<property name="address" value="address" />
<property name="age" value="28" />
</bean>
</set>
</property>
3.地图示例
<property name="maps">
<map>
<entry key="Key 1" value="1" />
<entry key="Key 2" value-ref="PersonBean" />
<entry key="Key 3">
<bean class="com.mkyong.common.Person">
<property name="name" value="mkyongMap" />
<property name="address" value="address" />
<property name="age" value="28" />
</bean>
</entry>
</map>
</property>
4.属性示例
<property name="pros">
<props>
<prop key="admin">admin@nospam.com</prop>
<prop key="support">support@nospam.com</prop>
</props>
</property>
完整的 Spring 的 bean 配置文件。
<beans
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="CustomerBean" class="com.mkyong.common.Customer">
<!-- java.util.List -->
<property name="lists">
<list>
<value>1</value>
<ref bean="PersonBean" />
<bean class="com.mkyong.common.Person">
<property name="name" value="mkyongList" />
<property name="address" value="address" />
<property name="age" value="28" />
</bean>
</list>
</property>
<!-- java.util.Set -->
<property name="sets">
<set>
<value>1</value>
<ref bean="PersonBean" />
<bean class="com.mkyong.common.Person">
<property name="name" value="mkyongSet" />
<property name="address" value="address" />
<property name="age" value="28" />
</bean>
</set>
</property>
<!-- java.util.Map -->
<property name="maps">
<map>
<entry key="Key 1" value="1" />
<entry key="Key 2" value-ref="PersonBean" />
<entry key="Key 3">
<bean class="com.mkyong.common.Person">
<property name="name" value="mkyongMap" />
<property name="address" value="address" />
<property name="age" value="28" />
</bean>
</entry>
</map>
</property>
<!-- java.util.Properties -->
<property name="pros">
<props>
<prop key="admin">admin@nospam.com</prop>
<prop key="support">support@nospam.com</prop>
</props>
</property>
</bean>
<bean id="PersonBean" class="com.mkyong.common.Person">
<property name="name" value="mkyong1" />
<property name="address" value="address 1" />
<property name="age" value="28" />
</bean>
</beans>
运行它…
package com.mkyong.common;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App
{
public static void main( String[] args )
{
ApplicationContext context = new ClassPathXmlApplicationContext("SpringBeans.xml");
Customer cust = (Customer)context.getBean("CustomerBean");
System.out.println(cust);
}
}
输出
Customer [
lists=[
1,
Person [address=address 1, age=28, name=mkyong1],
Person [address=address, age=28, name=mkyongList]
],
maps={
key 1=1,
key 2=Person [address=address 1, age=28, name=mkyong1],
key 3=Person [address=address, age=28, name=mkyongMap]
},
pros={
admin=admin@nospam.com, support=support@nospam.com},
sets=[
1,
Person [address=address 1, age=28, name=mkyong1],
Person [address=address, age=28, name=mkyongSet]]
]
下载源代码
Download it – Spring-Collection-Example.zip (6 KB)spring
Spring 数据 MongoDB–自动序列 ID 示例
在本教程中,我们将向您展示如何在 MongoDB + Spring 数据环境中生成一个自动递增的序列 id。
本项目中使用的工具:
- Spring Data MongoDB 1.2.1.RELEASE
- MongoDB 2.4.5
- Eclipse 4.2
- maven3
在本教程结束时,如果保存了集合名称“hosting ”,将会分配一个新的自动递增序列 id。下面是生成序列 id 的 Java 代码片段。
public long getNextSequenceId(String key) {
Query query = new Query(Criteria.where("_id").is(key));
Update update = new Update();
update.inc("seq", 1);
FindAndModifyOptions options = new FindAndModifyOptions();
options.returnNew(true);
SequenceId seqId =
mongoOperation.findAndModify(query, update, options, SequenceId.class);
return seqId.getSeq();
}
1.项目结构
查看项目目录结构,这是一个标准的 Maven 项目。
## 2.Maven Pom
如果您对项目依赖关系感兴趣。
pom.xml
<project ...>
<properties>
<jdk.version>1.6</jdk.version>
<spring.version>3.2.2.RELEASE</spring.version>
<mongojavadriver.version>2.11.1</mongojavadriver.version>
<springdata.version>1.2.1.RELEASE</springdata.version>
</properties>
<dependencies>
<!-- Spring Core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${
spring.version}</version>
</dependency>
<!-- need this for @Configuration -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
<!-- Spring Data for MongoDB -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>${
springdata.version}</version>
</dependency>
<!-- Java MongoDB Driver -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>${
mongojavadriver.version}</version>
</dependency>
</dependencies>
<build>
<finalName>SpringData</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>${
jdk.version}</source>
<target>${
jdk.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
3.序列集合
我们创建一个名为“sequence”的集合来存储自动增加的序列 id。参考下面的SequenceDaoImpl.java
,它显示了生成序列 id 的代码。
Note
Create the “sequence” collection in your MongoDB first!
db.sequence.insert({
_id: "hosting",seq: 0})
SequenceId.java
package com.mkyong.seq.model;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Document(collection = "sequence")
public class SequenceId {
@Id
private String id;
private long seq;
//get, set, toString...
}
SequenceDao.java
package com.mkyong.seq.dao;
import com.mkyong.seq.exception.SequenceException;
public interface SequenceDao {
long getNextSequenceId(String key) throws SequenceException;
}
SequenceDaoImpl.java
package com.mkyong.seq.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.FindAndModifyOptions;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Repository;
import com.mkyong.seq.exception.SequenceException;
import com.mkyong.seq.model.SequenceId;
@Repository
public class SequenceDaoImpl implements SequenceDao {
@Autowired
private MongoOperations mongoOperation;
@Override
public long getNextSequenceId(String key) throws SequenceException {
//get sequence id
Query query = new Query(Criteria.where("_id").is(key));
//increase sequence id by 1
Update update = new Update();
update.inc("seq", 1);
//return new increased id
FindAndModifyOptions options = new FindAndModifyOptions();
options.returnNew(true);
//this is the magic happened.
SequenceId seqId =
mongoOperation.findAndModify(query, update, options, SequenceId.class);
//if no id, throws SequenceException
//optional, just a way to tell user when the sequence id is failed to generate.
if (seqId == null) {
throw new SequenceException("Unable to get sequence id for key : " + key);
}
return seqId.getSeq();
}
}
SequenceException.java
package com.mkyong.seq.exception;
public class SequenceException extends RuntimeException {
private static final long serialVersionUID = 1L;
private String errCode;
private String errMsg;
//get, set...
public SequenceException(String errMsg) {
this.errMsg = errMsg;
}
}
4.获取序列 ID
为了获得序列 id,使用sequenceDao.getNextSequenceId("key")
。
HostingBo.java
package com.mkyong.hosting.bo;
import com.mkyong.seq.exception.SequenceException;
public interface HostingBo {
void save(String name) throws SequenceException;
}
HostingBoImpl.java
package com.mkyong.hosting.bo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.mkyong.hosting.dao.HostingDao;
import com.mkyong.hosting.model.Hosting;
import com.mkyong.seq.dao.SequenceDao;
import com.mkyong.seq.exception.SequenceException;
@Service
public class HostingBoImpl implements HostingBo {
private static final String HOSTING_SEQ_KEY = "hosting";
@Autowired
private SequenceDao sequenceDao;
@Autowired
private HostingDao hostingDao;
@Override
public void save(String name) throws SequenceException {
Hosting hosting = new Hosting();
hosting.setId(sequenceDao.getNextSequenceId(HOSTING_SEQ_KEY));
hosting.setName(name);
hostingDao.save(hosting);
System.out.println(hosting);
}
}
5.测试
运行一个简单的测试。
package com.mkyong;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.mkyong.config.AppConfig;
import com.mkyong.hosting.bo.HostingBo;
import com.mkyong.seq.exception.SequenceException;
public class App {
public static void main(String[] args) {
ApplicationContext ctx =
new AnnotationConfigApplicationContext(AppConfig.class);
HostingBo hostingBo = (HostingBo) ctx.getBean("hostingBoImpl");
try {
hostingBo.save("cloud.google.com");
hostingBo.save("heroku.com");
hostingBo.save("cloudbees.com");
} catch (SequenceException e) {
System.out.println(e.getErrMsg());
}
}
}
输出–Java 控制台
Hosting [id=1, name=cloud.google.com]
Hosting [id=2, name=heroku.com]
Hosting [id=3, name=cloudbees.com]
MongoDB 控制台。
>mongo
> db.sequence.find()
{
"_id" : "hosting", "seq" : 3 }
> db.hosting.find()
{
"_id" : NumberLong(1), "_class" : "com.mkyong.hosting.model.Hosting", "name" : "cloud.google.com" }
{
"_id" : NumberLong(2), "_class" : "com.mkyong.hosting.model.Hosting", "name" : "heroku.com" }
{
"_id" : NumberLong(3), "_class" : "com.mkyong.hosting.model.Hosting", "name" : "cloudbees.com" }
>
6.常见问题
q . sequence exception–无法获取密钥的序列 id:hosting?
A .记得创建“序列”集合!
db.sequence.insert({
_id: "hosting",seq: 0})
下载源代码
Download – SpringData-Auto-Sequence-Example.zip (24 KB)
参考
Spring Data MongoDB:删除文档
在 MongoDB 的 Spring data 中,可以使用remove()
和findAndRemove()
从 MongoDB 中删除文档。
- remove()–删除单个或多个文档。
- 删除单个文档,并返回被删除的文档。
Common Mistake
Don’t use findAndRemove()
to perform a batch delete (remove multiple documents), only the first document that matches the query will be removed. Refer query4 below:
1.删除文档示例
查看完整的示例,了解 remove()和 findAndRemove()的用法。
package com.mkyong.core;
import java.util.ArrayList;
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import com.mkyong.config.SpringMongoConfig;
import com.mkyong.model.User;
/**
* Delete example
*
* @author mkyong
*
*/
public class DeleteApp {
public static void main(String[] args) {
ApplicationContext ctx =
new AnnotationConfigApplicationContext(SpringMongoConfig.class);
MongoOperations mongoOperation =
(MongoOperations) ctx.getBean("mongoTemplate");
// insert 6 users for testing
List<User> users = new ArrayList<User>();
User user1 = new User("1001", "ant", 10);
User user2 = new User("1002", "bird", 20);
User user3 = new User("1003", "cat", 30);
User user4 = new User("1004", "dog", 40);
User user5 = new User("1005", "elephant", 50);
User user6 = new User("1006", "frog", 60);
users.add(user1);
users.add(user2);
users.add(user3);
users.add(user4);
users.add(user5);
users.add(user6);
mongoOperation.insert(users, User.class);
Query query1 = new Query();
query1.addCriteria(Criteria.where("name").exists(true)
.orOperator(
Criteria.where("name").is("frog"),
Criteria.where("name").is("dog")
));
mongoOperation.remove(query1, User.class);
Query query2 = new Query();
query2.addCriteria(Criteria.where("name").is("bird"));
User userTest2 = mongoOperation.findOne(query2, User.class);
mongoOperation.remove(userTest2);
// The first document that matches the query is returned and also
// removed from the collection in the database.
Query query3 = new Query();
query3.addCriteria(Criteria.where("name").is("ant"));
User userTest3 = mongoOperation.findAndRemove(query3, User.class);
System.out.println("Deleted document : " + userTest3);
// either cat or elephant is deleted only,
// common mistake, don't use for batch delete.
/*
Query query4 = new Query();
query4.addCriteria(Criteria.where("name") .exists(true)
.orOperator(
Criteria.where("name").is("cat"),
Criteria.where("name").is("elephant")
)
);
mongoOperation.findAndRemove(query4, User.class);
System.out.println("Deleted document : " + userTest4);
*/
System.out.println("\nAll users : ");
List<User> allUsers = mongoOperation.findAll(User.class);
for (User user : allUsers) {
System.out.println(user);
}
mongoOperation.dropCollection(User.class);
}
}
输出
Deleted document : User [id=5162e0153004c3cb0a907370, ic=1001, name=ant, age=10]
All users :
User [id=5162e0153004c3cb0a907372, ic=1003, name=cat, age=30]
User [id=5162e0153004c3cb0a907374, ic=1005, name=elephant, age=50]
下载源代码
Download it – SpringMongoDB-Delete-Example.zip (24 KB) ## 参考
Spring 数据 MongoDB hello world 示例
在本教程中,我们将向您展示如何使用" SpringData for MongoDB "框架,通过 Spring 的注释和 XML schema 在 MongoDB 中执行 CRUD 操作。
Updated on 1/04/2013
Article is updated to use latest SpringData v 1.2.0.RELEASE, it was v1.0.0.M2.
使用的工具和技术:
- spring Data MongoDB–1 . 2 . 0 . release
- 弹簧芯–3 . 2 . 2 .释放
- Java Mongo 驱动程序–2 . 11 . 0
- eclipse–4.2
- jdk 1.6
- maven–3 . 0 . 3
P.S Spring 数据要求 JDK 6.0 及以上,Spring Framework 3.0.x 及以上。
1.项目结构
一个经典的 Maven 风格的 Java 项目目录结构。
2.属国
需要以下库:
spring-data-mongodb
Currently, the “spring-data-mongodb
” jar is only available in “http://maven.springframework.org/milestone“, so, you have to declare this repository also.
更新于 2012 年 13 月 9 日
spring-data-mongodb
在 Maven 中央存储库中可用,不再需要 Spring 存储库。
pom.xml
<project
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.mkyong.core</groupId>
<artifactId>SpringMongoDBExample</artifactId>
<packaging>jar</packaging>
<version>1.0</version>
<name>SpringMongoExample</name>
<url>http://maven.apache.org</url>
<dependencies>
<!-- Spring framework -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.2.2.RELEASE</version>
</dependency>
<!-- mongodb java driver -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>2.11.0</version>
</dependency>
<!-- Spring data mongodb -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>1.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.0</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.9</version>
<configuration>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
</configuration>
</plugin>
</plugins>
</build>
</project>
3.Spring 配置、注释和 XML
在这里,我们向您展示了两种配置 Spring 数据和连接到 MongoDB 的方法,分别是通过注释和 XML schema。
Note
Refer to this official reference Connecting to MongoDB with Spring.
3.1 注释
扩展了AbstractMongoConfiguration
是最快的方式,它有助于配置你需要启动的一切,像mongoTemplate
对象。
SpringMongoConfig.java
package com.mkyong.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.AbstractMongoConfiguration;
import com.mongodb.Mongo;
import com.mongodb.MongoClient;
@Configuration
public class SpringMongoConfig extends AbstractMongoConfiguration {
@Override
public String getDatabaseName() {
return "yourdb";
}
@Override
@Bean
public Mongo mongo() throws Exception {
return new MongoClient("127.0.0.1");
}
}
或者,我更喜欢这个,更灵活地配置一切。
SpringMongoConfig1.java
package com.mkyong.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import com.mongodb.MongoClient;
@Configuration
public class SpringMongoConfig1 {
public @Bean
MongoDbFactory mongoDbFactory() throws Exception {
return new SimpleMongoDbFactory(new MongoClient(), "yourdb");
}
public @Bean
MongoTemplate mongoTemplate() throws Exception {
MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory());
return mongoTemplate;
}
}
并载入AnnotationConfigApplicationContext
:
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringMongoConfig.class);
MongoOperations mongoOperation = (MongoOperations)ctx.getBean("mongoTemplate");
3.2 XML 模式
SpringConfig.xml
<beans
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xsi:schemaLocation="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/data/mongo
http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<mongo:mongo host="127.0.0.1" port="27017" />
<mongo:db-factory dbname="yourdb" />
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
</bean>
</beans>
并将其包含在 Spring 的GenericXmlApplicationContext
:
ApplicationContext ctx = new GenericXmlApplicationContext("SpringConfig.xml");
MongoOperations mongoOperation = (MongoOperations)ctx.getBean("mongoTemplate");
So, XML or Annotation?
Actually, both are doing the same thing, it’s just based on personal preferences. Personally, I like XML to configure things.
4.用户模型
一个用户对象,annotated @ Document——保存哪个集合。稍后,我们将向您展示如何使用 Spring 数据将该对象绑定到 MongoDB 或从 MongoDB 绑定。
User.java
package com.mkyong.model;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Document(collection = "users")
public class User {
@Id
private String id;
String username;
String password;
//getter, setter, toString, Constructors
}
5.演示–CRUD 操作
完整的示例向您展示了如何使用 Spring 数据在 MongoDB 中执行 CRUD 操作。Spring 数据 API 非常简洁,应该是不言自明的。
App.java
package com.mkyong.core;
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import com.mkyong.config.SpringMongoConfig;
import com.mkyong.model.User;
//import org.springframework.context.support.GenericXmlApplicationContext;
public class App {
public static void main(String[] args) {
// For XML
//ApplicationContext ctx = new GenericXmlApplicationContext("SpringConfig.xml");
// For Annotation
ApplicationContext ctx =
new AnnotationConfigApplicationContext(SpringMongoConfig.class);
MongoOperations mongoOperation = (MongoOperations) ctx.getBean("mongoTemplate");
User user = new User("mkyong", "password123");
// save
mongoOperation.save(user);
// now user object got the created id.
System.out.println("1\. user : " + user);
// query to search user
Query searchUserQuery = new Query(Criteria.where("username").is("mkyong"));
// find the saved user again.
User savedUser = mongoOperation.findOne(searchUserQuery, User.class);
System.out.println("2\. find - savedUser : " + savedUser);
// update password
mongoOperation.updateFirst(searchUserQuery,
Update.update("password", "new password"),User.class);
// find the updated user object
User updatedUser = mongoOperation.findOne(searchUserQuery, User.class);
System.out.println("3\. updatedUser : " + updatedUser);
// delete
mongoOperation.remove(searchUserQuery, User.class);
// List, it should be empty now.
List<User> listUser = mongoOperation.findAll(User.class);
System.out.println("4\. Number of user = " + listUser.size());
}
}
输出
1\. user : User [id=516627653004953049d9ddf0, username=mkyong, password=password123]
2\. find - savedUser : User [id=516627653004953049d9ddf0, username=mkyong, password=password123]
3\. updatedUser : User [id=516627653004953049d9ddf0, username=mkyong, password=new password]
4\. Number of user = 0
下载源代码
Download it – SpringMongoDB-HelloWorld-Example.zip (24 KB)
参考
- 【MongoDB 的春季数据
- 用 Spring 连接到 MongoDB】
- 奥雷利春天数据 mongodb 教程
- 又一个好的春季数据 mongodb 教程
Tags : hello world mongodb spring-data