Skip to content

Commit f834743

Browse files
authored
Optimize Spring samples (#9249)
* Optimize spring-webflux - Upgrade to Spring Boot 3.3.3 - Optimize handler implementations - Optimize the router implementation - Use WebFlux.fn configuration for HttpHandler * Optimize spring - Upgrade to Spring Boot 3.3.3 - Use WebMVC.fn instead of `@Controller` - Refine configuration
1 parent 96d08a7 commit f834743

File tree

16 files changed

+281
-97
lines changed

16 files changed

+281
-97
lines changed

frameworks/Java/spring-webflux/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
<parent>
1414
<groupId>org.springframework.boot</groupId>
1515
<artifactId>spring-boot-starter-parent</artifactId>
16-
<version>3.3.1</version>
16+
<version>3.3.3</version>
1717
</parent>
1818

1919
<properties>

frameworks/Java/spring-webflux/src/main/java/benchmark/App.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,30 @@
11
package benchmark;
22

3+
import benchmark.web.ServerFilter;
4+
35
import org.springframework.boot.SpringApplication;
46
import org.springframework.boot.autoconfigure.SpringBootApplication;
7+
import org.springframework.context.annotation.Bean;
8+
import org.springframework.http.server.reactive.HttpHandler;
59
import org.springframework.scheduling.annotation.EnableScheduling;
10+
import org.springframework.web.reactive.function.server.HandlerStrategies;
11+
import org.springframework.web.reactive.function.server.RouterFunction;
12+
import org.springframework.web.reactive.function.server.RouterFunctions;
13+
import org.springframework.web.reactive.function.server.ServerResponse;
14+
import org.springframework.web.reactive.result.view.ViewResolver;
15+
import org.springframework.web.server.WebHandler;
16+
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
617

718
@SpringBootApplication
819
@EnableScheduling
920
public class App {
1021

22+
@Bean
23+
public HttpHandler httpHandler(RouterFunction<ServerResponse> route, ServerFilter serverFilter, ViewResolver viewResolver) {
24+
WebHandler webHandler = RouterFunctions.toWebHandler(route, HandlerStrategies.builder().viewResolver(viewResolver).build());
25+
return WebHttpHandlerBuilder.webHandler(webHandler).filter(serverFilter).build();
26+
}
27+
1128
public static void main(String[] args) {
1229
SpringApplication.run(App.class, args);
1330
}

frameworks/Java/spring-webflux/src/main/java/benchmark/web/WebfluxHandler.java renamed to frameworks/Java/spring-webflux/src/main/java/benchmark/web/DbHandler.java

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@
22

33
import java.util.Collections;
44
import java.util.List;
5-
import java.util.Optional;
65
import java.util.concurrent.ThreadLocalRandom;
76

87
import benchmark.model.Fortune;
9-
import benchmark.model.Message;
108
import benchmark.model.World;
119
import benchmark.repository.DbRepository;
1210
import reactor.core.publisher.Flux;
@@ -21,26 +19,14 @@
2119
import static java.util.Comparator.comparing;
2220

2321
@Component
24-
public class WebfluxHandler {
22+
public class DbHandler {
2523

2624
private final DbRepository dbRepository;
2725

28-
public WebfluxHandler(DbRepository dbRepository) {
26+
public DbHandler(DbRepository dbRepository) {
2927
this.dbRepository = dbRepository;
3028
}
3129

32-
public Mono<ServerResponse> plaintext(ServerRequest request) {
33-
return ServerResponse.ok()
34-
.contentType(MediaType.TEXT_PLAIN)
35-
.bodyValue("Hello, World!");
36-
}
37-
38-
public Mono<ServerResponse> json(ServerRequest request) {
39-
return ServerResponse.ok()
40-
.contentType(MediaType.APPLICATION_JSON)
41-
.bodyValue(new Message("Hello, World!"));
42-
}
43-
4430
public Mono<ServerResponse> db(ServerRequest request) {
4531
int id = randomWorldNumber();
4632
Mono<World> world = dbRepository.getWorld(id)
@@ -52,7 +38,7 @@ public Mono<ServerResponse> db(ServerRequest request) {
5238
}
5339

5440
public Mono<ServerResponse> queries(ServerRequest request) {
55-
int queries = getQueries(request);
41+
int queries = parseQueryCount(request.queryParams().getFirst("queries"));
5642

5743
Mono<List<World>> worlds = Flux.range(0, queries)
5844
.flatMap(i -> dbRepository.getWorld(randomWorldNumber()))
@@ -64,21 +50,21 @@ public Mono<ServerResponse> queries(ServerRequest request) {
6450
});
6551
}
6652

67-
private static int parseQueryCount(Optional<String> maybeTextValue) {
68-
if (!maybeTextValue.isPresent()) {
53+
private static int parseQueryCount(String maybeTextValue) {
54+
if (maybeTextValue == null) {
6955
return 1;
7056
}
7157
int parsedValue;
7258
try {
73-
parsedValue = Integer.parseInt(maybeTextValue.get());
59+
parsedValue = Integer.parseInt(maybeTextValue);
7460
} catch (NumberFormatException e) {
7561
return 1;
7662
}
7763
return Math.min(500, Math.max(1, parsedValue));
7864
}
7965

8066
public Mono<ServerResponse> updates(ServerRequest request) {
81-
int queries = getQueries(request);
67+
int queries = parseQueryCount(request.queryParams().getFirst("queries"));
8268

8369
Mono<List<World>> worlds = Flux.range(0, queries)
8470
.flatMap(i -> dbRepository.findAndUpdateWorld(randomWorldNumber(), randomWorldNumber()))
@@ -101,10 +87,6 @@ public Mono<ServerResponse> fortunes(ServerRequest request) {
10187
.render("fortunes", Collections.singletonMap("fortunes", result));
10288
}
10389

104-
private static int getQueries(ServerRequest request) {
105-
return parseQueryCount(request.queryParam("queries"));
106-
}
107-
10890
private static int randomWorldNumber() {
10991
return 1 + ThreadLocalRandom.current().nextInt(10000);
11092
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package benchmark.web;
2+
3+
import java.util.Map;
4+
5+
import com.fasterxml.jackson.core.JsonProcessingException;
6+
import com.fasterxml.jackson.databind.ObjectMapper;
7+
import com.fasterxml.jackson.databind.ObjectWriter;
8+
import reactor.core.publisher.Mono;
9+
10+
import org.springframework.core.io.buffer.DataBuffer;
11+
import org.springframework.core.io.buffer.DataBufferFactory;
12+
import org.springframework.http.HttpHeaders;
13+
import org.springframework.http.MediaType;
14+
import org.springframework.stereotype.Component;
15+
import org.springframework.web.reactive.function.server.HandlerFunction;
16+
import org.springframework.web.reactive.function.server.ServerRequest;
17+
import org.springframework.web.reactive.function.server.ServerResponse;
18+
19+
@Component
20+
public class JsonHandler implements HandlerFunction<ServerResponse> {
21+
22+
private final ObjectWriter writer;
23+
24+
public JsonHandler(ObjectMapper objectMapper) {
25+
this.writer = objectMapper.writerFor(Map.class);
26+
}
27+
28+
@Override
29+
public Mono<ServerResponse> handle(ServerRequest request) {
30+
return ServerResponse.ok()
31+
.body((message, context) -> {
32+
DataBuffer buffer = serialize(request, Map.of("message", "Hello, world!"));
33+
HttpHeaders headers = message.getHeaders();
34+
headers.setContentLength(buffer.readableByteCount());
35+
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
36+
return message.writeWith(Mono.just(buffer));
37+
});
38+
}
39+
40+
private DataBuffer serialize(ServerRequest request, Object object) {
41+
try {
42+
byte[] bytes = this.writer.writeValueAsBytes(object);
43+
return bufferFactory(request).wrap(bytes);
44+
}
45+
catch (JsonProcessingException ex) {
46+
throw new RuntimeException(ex);
47+
}
48+
}
49+
50+
private static DataBufferFactory bufferFactory(ServerRequest request) {
51+
return request.exchange().getResponse().bufferFactory();
52+
}
53+
54+
}

frameworks/Java/spring-webflux/src/main/java/benchmark/ServerFilter.java renamed to frameworks/Java/spring-webflux/src/main/java/benchmark/web/ServerFilter.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package benchmark.web;
22

3-
import io.netty.handler.codec.http.HttpHeaderNames;
43
import org.springframework.http.HttpHeaders;
54
import org.springframework.scheduling.annotation.Scheduled;
65
import org.springframework.stereotype.Component;
@@ -28,8 +27,8 @@ public void updateDate() {
2827
@Override
2928
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
3029
HttpHeaders headers = exchange.getResponse().getHeaders();
31-
headers.add(HttpHeaderNames.SERVER.toString(), SERVER_NAME);
32-
headers.add(HttpHeaderNames.DATE.toString(), this.date);
30+
headers.add(HttpHeaders.SERVER, SERVER_NAME);
31+
headers.add(HttpHeaders.DATE, this.date);
3332
return chain.filter(exchange);
3433
}
3534
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package benchmark.web;
2+
3+
import java.nio.charset.StandardCharsets;
4+
5+
import reactor.core.publisher.Mono;
6+
7+
import org.springframework.core.io.buffer.DataBufferFactory;
8+
import org.springframework.http.HttpHeaders;
9+
import org.springframework.http.MediaType;
10+
import org.springframework.stereotype.Component;
11+
import org.springframework.web.reactive.function.server.HandlerFunction;
12+
import org.springframework.web.reactive.function.server.ServerRequest;
13+
import org.springframework.web.reactive.function.server.ServerResponse;
14+
15+
@Component
16+
public class TextHandler implements HandlerFunction<ServerResponse> {
17+
18+
private static final byte[] TEXT_BODY = "Hello, World!".getBytes(StandardCharsets.UTF_8);
19+
20+
private static final String TEXT_BODY_LENGTH = String.valueOf(TEXT_BODY.length);
21+
22+
@Override
23+
public Mono<ServerResponse> handle(ServerRequest request) {
24+
return ServerResponse.ok()
25+
.body((message, context) -> {
26+
HttpHeaders headers = message.getHeaders();
27+
headers.add(HttpHeaders.CONTENT_LENGTH, TEXT_BODY_LENGTH);
28+
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE);
29+
return message.writeWith(Mono.just(bufferFactory(request).wrap(TEXT_BODY)));
30+
});
31+
}
32+
33+
private static DataBufferFactory bufferFactory(ServerRequest request) {
34+
return request.exchange().getResponse().bufferFactory();
35+
}
36+
37+
}
Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,32 @@
11
package benchmark.web;
22

3+
import reactor.core.publisher.Mono;
4+
35
import org.springframework.context.annotation.Bean;
46
import org.springframework.context.annotation.Configuration;
7+
import org.springframework.web.reactive.function.server.HandlerFunction;
58
import org.springframework.web.reactive.function.server.RouterFunction;
6-
import org.springframework.web.reactive.function.server.RouterFunctions;
79
import org.springframework.web.reactive.function.server.ServerResponse;
810

911
@Configuration
1012
public class WebfluxRouter {
1113

1214
@Bean
13-
public RouterFunction<ServerResponse> route(WebfluxHandler handler) {
14-
return RouterFunctions.route()
15-
.GET("/plaintext", handler::plaintext)
16-
.GET("/json", handler::json)
17-
.GET("/db", handler::db)
18-
.GET("/queries", handler::queries)
19-
.GET("/updates", handler::updates)
20-
.GET("/fortunes", handler::fortunes)
21-
.build();
15+
public RouterFunction<ServerResponse> route(
16+
TextHandler textHandler, JsonHandler jsonHandler, DbHandler dbHandler) {
17+
18+
return request -> {
19+
HandlerFunction<ServerResponse> fn = switch (request.uri().getRawPath()) {
20+
case "/plaintext" -> textHandler;
21+
case "/json" -> jsonHandler;
22+
case "/db" -> dbHandler::db;
23+
case "/queries" -> dbHandler::queries;
24+
case "/updates" -> dbHandler::updates;
25+
case "/fortunes" -> dbHandler::fortunes;
26+
default -> r -> ServerResponse.notFound().build();
27+
};
28+
return Mono.just(fn);
29+
};
2230
}
31+
2332
}

frameworks/Java/spring/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<parent>
1212
<groupId>org.springframework.boot</groupId>
1313
<artifactId>spring-boot-starter-parent</artifactId>
14-
<version>3.3.1</version>
14+
<version>3.3.3</version>
1515
</parent>
1616

1717
<properties>

frameworks/Java/spring/spring.dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ RUN java -Djarmode=tools -jar app.jar extract
1212

1313
EXPOSE 8080
1414

15-
CMD ["java", "-XX:+DisableExplicitGC", "-XX:+UseStringDeduplication", "-Dlogging.level.root=OFF", "-jar", "app/app.jar", "--spring.profiles.active=jpa"]
15+
CMD ["java", "-XX:+DisableExplicitGC", "-XX:+UseStringDeduplication", "-Dlogging.level.root=OFF", "-jar", "app/app.jar", "--spring.profiles.active=jdbc"]

frameworks/Java/spring/src/main/java/hello/App.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
import org.springframework.boot.SpringApplication;
66
import org.springframework.boot.autoconfigure.SpringBootApplication;
7-
import org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration;
8-
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
97
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
108
import org.springframework.boot.context.event.ApplicationReadyEvent;
119
import org.springframework.context.annotation.Bean;
@@ -35,4 +33,5 @@ DataSource datasource(DataSourceProperties dataSourceProperties) {
3533

3634
return dataSource;
3735
}
36+
3837
}

0 commit comments

Comments
 (0)