Şöyle yaparız
List<String> list = Arrays.asList("vins", "guru");Flux<String> stringFlux = Flux.fromIterable(list).map(String::toUpperCase);
List<String> list = Arrays.asList("vins", "guru");Flux<String> stringFlux = Flux.fromIterable(list).map(String::toUpperCase);
Örnek - delayElements ile KullanımFlux<Integer> flux = Flux.just(1);//Observer 1flux.subscribe(i -> System.out.println("Observer-1 : " + i));//Observer 2flux.subscribe(i -> System.out.println("Observer-2 : " + i));//OutputObserver-1 : 1Observer-2 : 1
System.out.println("Starts");//flux emits one element per secondFlux<Character> flux = Flux.just('a', 'b', 'c', 'd').delayElements(Duration.ofSeconds(1));//Observer 1 - takes 500ms to processflux.map(Character::toUpperCase).subscribe(i -> {sleep(500);System.out.println("Observer-1 : " + i);});//Observer 2 - process immediatelyflux.subscribe(i -> System.out.println("Observer-2 : " + i));System.out.println("Ends");StartsEndsObserver-2 : aObserver-1 : AObserver-2 : bObserver-1 : BObserver-2 : cObserver-2 : dObserver-1 : CObserver-1 : D
There is always a question, is there a difference between flatMap and map? Well yes there is a difference.- flatMap is used for non blocking operation in this case an operation that will return a Mono or Flux.- map is used for blocking operation that can be done in fixed time. An example would be transforming an object.
return Mono.just(Person("name", "age:12")).map { person ->EnhancedPerson(person, "id-set", "savedInDb")}.flatMap { person ->reactiveMongoDb.save(person)};
//Create a person object with names and age Person obj = new Person("firstname", "lastname", 20); return Mono.just(obj) .map { person -> AdminPerson(person, "admin") // blocking transform the person to an admin }.flatMap { person -> personRepository.save(person) // Non blocking save to database }
@GetMapping("/students/course/{studentID}/{courseID}") Mono<CourseWork> addNewCourseNoChain(Long studentID, @PathVariable Long courseID) { return studentsRepository.findById(studentID) .flatMap(students -> { //Update student details students.setUpdated_on(System.currentTimeMillis()); return studentsRepository.save(students); }).flatMap(updatedStudent -> { //Create a new course for student CourseWork courseWork = getCoursework(updatedStudent.getId(), courseID); return courseWorkRepository.save(courseWork); }); }
Düzeltmek için şöyle yaparız. Bu sefer çalışır çünkü flatMap() verilen lambda'nın döndürdüğü Mono'ya subscribe olur.interface Service {Mono<String> create(String s);Mono<Void> update(String s);}class Foo {private final Service service;Mono<Void> problem() {return service.create("foo").doOnNext(service::update).then();}}
interface Service {Mono<String> create(String s);Mono<Void> update(String s);}class Foo {private final Service service;Mono<Void> problem() {return service.create("foo").flatMap(service::update)}}
Mono::doOnNext triggers when the data is emitted successfully, which means the data is available and present.Mono::doOnSuccess triggers when the Mono completes successfully - result is either T or null, which means the processing itself successfully finished regardless the state of data and is executed although the data are not available or present but the pipeline itself succeed.Mono::then as the end of the methods chain returns Mono<Void> on complete and error signals.
Mono.just(5)
.doOnNext(i -> logger.info(i + "")) // <-- is called and prints '5'
.flatMap(i -> Mono.empty())
.doOnNext(i -> logger.info(i + "")) // <-- is NOT called
.doOnSuccess(i -> logger.info("Done")) // <-- is called and prints 'Done' (i is null)
.block();
Örnek
Düzeltmek için şöyle yaparızinterface Service {Mono<String> create(String s);Mono<Void> update(String s);}class Foo {private final Service service;Mono<Void> problem() {return service.create("foo").doOnNext(service::update).then();}}
Açıklaması öyleinterface Service {Mono<String> create(String s);Mono<Void> update(String s);}class Foo {private final Service service;Mono<Void> problem() {return service.create("foo").doOnNext(foo -> service.update(foo).block()).then();}}
The take away here is to only use doOn* methods for side-effects, e.g. logging, uploading metrics.
spring.cache.type=hazelcast spring.hazelcast.config=classpath:cache.yaml
spring.hazelcast.config=classpath:config/demo-config.xml
# hazelcast.yamlhazelcast:network:join:multicast:enabled: true
hazelcast:monitoring: true maxSize: 10000 namespace: hazelcast tcp: enabled: true members: "localhost:5701"
hazelcast:initialBackoffMillis: 1000maxBackoffMillis: 6000multiplier: 2.0clusterConnectTimeoutMillis: 50000jitter: 0.2asyncStartClient: truenamespace: hazelcastuserCodeDeploymentEnabled: trueclientProperties:hazelcast.client.invocation.timeout.seconds: 5executorServiceName: trader-clitcp:enabled: truemembers: localhost:5701
public class FormatNameRequest {@NotNull(message = "Title cannot be null")private String title;@NotNull@Size(message = "First name must be between 2 and 25 characters", min = 2, max = 25)private String firstName;private String middleName;@NotBlank(message = "Last name cannot be empty")private String lastName;...}
Test için şöyle yaparız@RestControllerpublic class ValidationDemoController {@PostMapping("/format")public Mono<ResponseEntity<FormattedNameResponse>> format(@Valid @RequestBody
Mono<FormatNameRequest> request) {return request.map(res -> ResponseEntity.status(HttpStatus.OK).body(alo
FormattedNameResponseMapper.fromFormatNameRequest(res))).onErrorResume(WebExchangeBindException.class,ex -> Mono.just(ResponseEntity.status(HttpStatus.BAD_REQUEST).body(FormattedNameResponseMapper.fromWebExchangeBindException(ex))));}}public class FormattedNameResponseMapper {public static FormattedNameResponse fromWebExchangeBindException(
WebExchangeBindException ex) {FormattedNameResponse res = new FormattedNameResponse();List<Error> errors = ex.getFieldErrors().stream().map(fieldError -> new Error(fieldError.getField(), fieldError.getDefaultMessage())).collect(Collectors.toList());res.setErrors(errors);return res;}public static FormattedNameResponse fromFormatNameRequest(FormatNameRequest req) {String s = String.format("%s %s %s %s", req.getTitle(), req.getFirstName(),
req.getMiddleName(), req.getLastName());FormattedNameResponse res = new FormattedNameResponse();res.setFormattedName(s);return res;}}
RequestPOST http://host:port/formatContent-Type: application/json{"title":"Mr","firstName":"Jhon","middleName": "Martin","lastName": "Smith"}Succesful Response200 OKContent-Type: application/json{"formattedName": "Mr Jhon Martin Smith"}Error Response400 Bad RequestContent-Type: application/json{"errors": [{"code": "erroCode1","message": "errorMessage1"},{"code": "erroCode2","message": "errorMessage2"},{"code": "erroCode3","message": "errorMessage13"}]}
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; import org.springframework.boot.actuate.endpoint.annotation.WriteOperation; import org.springframework.boot.actuate.endpoint.annotation.Selector;
Following are the steps to create a new endpoint.
- Create a new class. This class should be annotated with @Endpoint annotation.
- Provide an id attribute to this annotation. This will be the id of the endpoint or the name with which it will be accessed.
- Create a method in this class. This method should have @ReadOperation annotation.
- When the endpoint is accessed, the method with @ReadOperation will be invoked. Thus, this method should return the data that you want to send when the endpoint is accessed.
- Finally, this class should be a spring bean so it is annotated with @Component annotation.
- Register this endpoint id in application.properties to enable it as shown below.management.endpoints.web.exposure.include = info,env,health,getusers
application.properties dosyasına şöyle yaparız@Configurationpublic class CustomActuatorConfiguration {@Beanpublic HelloWorldEndpoint helloWorldEndpoint() {return new HelloWorldEndpoint();}}@Endpoint(id = "helloworld")public class HelloWorldEndpoint {@ReadOperationpublic String helloWorld() {return "Hello World";}}
management.endpoints.web.exposure.include=helloworldSonucu görmek için şöyle yaparız
> curl 'http://localhost:8080/actuator/helloworld'
Hello World
@Component
@Endpoint(id="getusers")
public class LoggedInUserFinder {
List<User> users = new ArrayList<>();
@ReadOperation
public List<User> getUsers() {
User userOne = new User();
userOne.setName("Mark Twain");
User userTwo = new User();
userTwo.setName("Robinhood");
users.add(userOne);
users.add(userTwo);
return users;
}
@ReadOperation
public User getUser(@Selector String userName) {
// return user matching name
for (User user : users) {
if(user.getName().equals(userName)) {
return user;
}
}
return new User();
}
@DeleteOperation
public User removeUser(@Selector String userName) {
// remove user matching name
for (User user : users) {
if(user.getName().equals(userName)) {
users.remove(user);
return user;
}
}
return new User();
}
@WriteOperation
public User removeUser(@Selector String userName) {
// create user with name
User user = new User();
// add user
users.add(user);
}
// User model class
static class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}management.endpoints.web.exposure.include = info,env,health,getusershttp://localhost:8080/actuator/getusers adresine gidersek çıktı şöyle
[
{"name":"Mark Twain"},
{"name":"Robinhood"}
]