diff --git a/spring-boot-modules/pom.xml b/spring-boot-modules/pom.xml
index 2b4a94a7a5..389dbf2d55 100644
--- a/spring-boot-modules/pom.xml
+++ b/spring-boot-modules/pom.xml
@@ -105,6 +105,7 @@
spring-boot-documentation
spring-boot-3-url-matching
spring-boot-graalvm-docker
+ spring-boot-validations
diff --git a/spring-boot-modules/spring-boot-validations/pom.xml b/spring-boot-modules/spring-boot-validations/pom.xml
new file mode 100644
index 0000000000..13044471af
--- /dev/null
+++ b/spring-boot-modules/spring-boot-validations/pom.xml
@@ -0,0 +1,35 @@
+
+ 4.0.0
+
+ com.baeldung.spring-boot-modules
+ spring-boot-modules
+ 1.0.0-SNAPSHOT
+
+ spring-boot-validations
+ spring-boot-validations
+ Demo of Validations in Spring Boot
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/Application.java b/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/Application.java
new file mode 100644
index 0000000000..c0490d50c6
--- /dev/null
+++ b/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/Application.java
@@ -0,0 +1,12 @@
+package com.baeldung;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+}
diff --git a/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/controller/ValidationController.java b/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/controller/ValidationController.java
new file mode 100644
index 0000000000..d4ea9a6336
--- /dev/null
+++ b/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/controller/ValidationController.java
@@ -0,0 +1,33 @@
+package com.baeldung.controller;
+
+import javax.validation.Valid;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.baeldung.dto.BooleanObject;
+import com.baeldung.service.ValidationService;
+
+@RestController
+public class ValidationController {
+
+ @Autowired
+ ValidationService service;
+
+ @PostMapping("/validateBoolean")
+ public ResponseEntity processBooleanObject(@RequestBody @Valid BooleanObject booleanObj) {
+ return ResponseEntity.ok("BooleanObject is valid");
+ }
+
+ @PostMapping("/validateBooleanAtService")
+ public ResponseEntity processBooleanObjectAtService() {
+ BooleanObject boolObj = new BooleanObject();
+ boolObj.setBoolField(Boolean.TRUE);
+ boolObj.setTrueField(Boolean.FALSE);
+ service.processBoolean(boolObj);
+ return ResponseEntity.ok("BooleanObject is valid");
+ }
+}
diff --git a/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/controlleradvice/GlobalExceptionHandler.java b/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/controlleradvice/GlobalExceptionHandler.java
new file mode 100644
index 0000000000..82f0839acf
--- /dev/null
+++ b/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/controlleradvice/GlobalExceptionHandler.java
@@ -0,0 +1,37 @@
+package com.baeldung.controlleradvice;
+
+import java.util.stream.Collectors;
+
+import javax.validation.ConstraintViolationException;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+
+ @ExceptionHandler(MethodArgumentNotValidException.class)
+ @ResponseStatus(value = HttpStatus.BAD_REQUEST)
+ public String handleValidationException(MethodArgumentNotValidException ex) {
+ return ex.getBindingResult()
+ .getFieldErrors()
+ .stream()
+ .map(e -> e.getDefaultMessage())
+ .collect(Collectors.joining(","));
+ }
+
+ @ExceptionHandler(IllegalArgumentException.class)
+ @ResponseStatus(value = HttpStatus.BAD_REQUEST)
+ public String handleIllegalArugmentException(IllegalArgumentException ex) {
+ return ex.getMessage();
+ }
+
+ @ExceptionHandler(ConstraintViolationException.class)
+ @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
+ public String handleConstraintViolationException(ConstraintViolationException ex) {
+ return ex.getMessage();
+ }
+}
diff --git a/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/deserializer/BooleanDeserializer.java b/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/deserializer/BooleanDeserializer.java
new file mode 100644
index 0000000000..01a8e0eba0
--- /dev/null
+++ b/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/deserializer/BooleanDeserializer.java
@@ -0,0 +1,21 @@
+package com.baeldung.deserializer;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+
+public class BooleanDeserializer extends JsonDeserializer {
+ @Override
+ public Boolean deserialize(JsonParser parser, DeserializationContext context) throws IOException {
+ String value = parser.getText();
+ if (value != null && value.equals("+")) {
+ return Boolean.TRUE;
+ } else if (value != null && value.equals("-")) {
+ return Boolean.FALSE;
+ } else {
+ throw new IllegalArgumentException("Only values accepted as Boolean are + and -");
+ }
+ }
+}
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/dto/BooleanObject.java b/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/dto/BooleanObject.java
new file mode 100644
index 0000000000..750b23fe11
--- /dev/null
+++ b/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/dto/BooleanObject.java
@@ -0,0 +1,56 @@
+package com.baeldung.dto;
+
+import javax.validation.constraints.AssertFalse;
+import javax.validation.constraints.AssertTrue;
+import javax.validation.constraints.NotNull;
+
+import com.baeldung.deserializer.BooleanDeserializer;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+public class BooleanObject {
+
+ @NotNull(message = "boolField cannot be null")
+ Boolean boolField;
+
+ @AssertTrue(message = "trueField must have true value")
+ Boolean trueField;
+
+ @NotNull(message = "falseField cannot be null")
+ @AssertFalse(message = "falseField must have false value")
+ Boolean falseField;
+
+ @JsonDeserialize(using = BooleanDeserializer.class)
+ Boolean boolStringVar;
+
+ public Boolean getBoolField() {
+ return boolField;
+ }
+
+ public void setBoolField(Boolean boolField) {
+ this.boolField = boolField;
+ }
+
+ public Boolean getTrueField() {
+ return trueField;
+ }
+
+ public void setTrueField(Boolean trueField) {
+ this.trueField = trueField;
+ }
+
+ public Boolean getFalseField() {
+ return falseField;
+ }
+
+ public void setFalseField(Boolean falseField) {
+ this.falseField = falseField;
+ }
+
+ public Boolean getBoolStringVar() {
+ return boolStringVar;
+ }
+
+ public void setBoolStringVar(Boolean boolStringVar) {
+ this.boolStringVar = boolStringVar;
+ }
+}
diff --git a/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/service/ValidationService.java b/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/service/ValidationService.java
new file mode 100644
index 0000000000..3fc7160bd5
--- /dev/null
+++ b/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/service/ValidationService.java
@@ -0,0 +1,17 @@
+package com.baeldung.service;
+
+import javax.validation.Valid;
+
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import com.baeldung.dto.BooleanObject;
+
+@Service
+@Validated
+public class ValidationService {
+
+ public void processBoolean(@Valid BooleanObject booleanObj) {
+ // further processing
+ }
+}
diff --git a/spring-boot-modules/spring-boot-validations/src/test/java/com/baeldung/controller/ValidationControllerUnitTest.java b/spring-boot-modules/spring-boot-validations/src/test/java/com/baeldung/controller/ValidationControllerUnitTest.java
new file mode 100644
index 0000000000..f05d76e3f1
--- /dev/null
+++ b/spring-boot-modules/spring-boot-validations/src/test/java/com/baeldung/controller/ValidationControllerUnitTest.java
@@ -0,0 +1,125 @@
+package com.baeldung.controller;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.boot.test.context.TestConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+import org.springframework.test.web.servlet.MockMvc;
+
+import com.baeldung.service.ValidationService;
+
+@ExtendWith(SpringExtension.class)
+@WebMvcTest(controllers = ValidationController.class)
+class ValidationControllerUnitTest {
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @TestConfiguration
+ static class EmployeeServiceImplTestContextConfiguration {
+ @Bean
+ public ValidationService validationService() {
+ return new ValidationService() {
+ };
+ }
+ }
+
+ @Autowired
+ ValidationService service;
+
+ @Test
+ void whenNullInputForBooleanField_thenHttpBadRequestAsHttpResponse() throws Exception {
+ String postBody = "{\"boolField\":null,\"trueField\":true,\"falseField\":false,\"boolStringVar\":\"+\"}";
+
+ mockMvc.perform(post("/validateBoolean").contentType("application/json")
+ .content(postBody))
+ .andExpect(status().isBadRequest());
+ }
+
+ @Test
+ void whenInvalidInputForTrueBooleanField_thenErrorResponse() throws Exception {
+ String postBody = "{\"boolField\":true,\"trueField\":false,\"falseField\":false,\"boolStringVar\":\"+\"}";
+
+ String output = mockMvc.perform(post("/validateBoolean").contentType("application/json")
+ .content(postBody))
+ .andReturn()
+ .getResponse()
+ .getContentAsString();
+
+ assertEquals("trueField must have true value", output);
+ }
+
+ @Test
+ void whenInvalidInputForFalseBooleanField_thenErrorResponse() throws Exception {
+ String postBody = "{\"boolField\":true,\"trueField\":true,\"falseField\":true,\"boolStringVar\":\"+\"}";
+
+ String output = mockMvc.perform(post("/validateBoolean").contentType("application/json")
+ .content(postBody))
+ .andReturn()
+ .getResponse()
+ .getContentAsString();
+
+ assertEquals("falseField must have false value", output);
+ }
+
+ @Test
+ void whenInvalidBooleanFromJson_thenErrorResponse() throws Exception {
+ String postBody = "{\"boolField\":true,\"trueField\":true,\"falseField\":false,\"boolStringVar\":\"plus\"}";
+
+ String output = mockMvc.perform(post("/validateBoolean").contentType("application/json")
+ .content(postBody))
+ .andReturn()
+ .getResponse()
+ .getContentAsString();
+
+ assertEquals("Only values accepted as Boolean are + and -", output);
+ }
+
+ @Test
+ void whenAllBooleanFieldsValid_thenCorrectResponse() throws Exception {
+ String postBody = "{\"boolField\":true,\"trueField\":true,\"falseField\":false,\"boolStringVar\":\"+\"}";
+
+ String output = mockMvc.perform(post("/validateBoolean").contentType("application/json")
+ .content(postBody))
+ .andReturn()
+ .getResponse()
+ .getContentAsString();
+
+ assertEquals("BooleanObject is valid", output);
+ }
+
+ @Test
+ void givenAllBooleanFieldsValid_whenServiceValidationFails_thenErrorResponse() throws Exception {
+ mockMvc.perform(post("/validateBooleanAtService").contentType("application/json"))
+ .andExpect(status().isInternalServerError());
+ }
+
+ @Test
+ void whenNullInputForTrueBooleanField_thenCorrectResponse() throws Exception {
+ String postBody = "{\"boolField\":true,\"trueField\":null,\"falseField\":false,\"boolStringVar\":\"+\"}";
+
+ mockMvc.perform(post("/validateBoolean").contentType("application/json")
+ .content(postBody))
+ .andExpect(status().isOk());
+ }
+
+ @Test
+ void whenNullInputForFalseBooleanField_thenHttpBadRequestAsHttpResponse() throws Exception {
+ String postBody = "{\"boolField\":true,\"trueField\":true,\"falseField\":null,\"boolStringVar\":\"+\"}";
+
+ String output = mockMvc.perform(post("/validateBoolean").contentType("application/json")
+ .content(postBody))
+ .andReturn()
+ .getResponse()
+ .getContentAsString();
+
+ assertEquals("falseField cannot be null", output);
+ }
+}
diff --git a/spring-boot-modules/spring-boot-validations/src/test/java/com/baeldung/dto/BooleanUnitTest.java b/spring-boot-modules/spring-boot-validations/src/test/java/com/baeldung/dto/BooleanUnitTest.java
new file mode 100644
index 0000000000..9ab04794c6
--- /dev/null
+++ b/spring-boot-modules/spring-boot-validations/src/test/java/com/baeldung/dto/BooleanUnitTest.java
@@ -0,0 +1,21 @@
+package com.baeldung.dto;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+class BooleanUnitTest {
+
+ @Test
+ void givenInputAsString_whenStringToBoolean_thenValidBooleanConversion() {
+ assertEquals(Boolean.TRUE, Boolean.valueOf("TRUE"));
+ assertEquals(Boolean.FALSE, Boolean.valueOf("false"));
+ assertEquals(Boolean.TRUE, Boolean.parseBoolean("True"));
+ }
+
+ @Test
+ void givenInputAsboolean_whenbooleanToBoolean_thenValidBooleanConversion() {
+ assertEquals(Boolean.TRUE, Boolean.valueOf(true));
+ assertEquals(Boolean.FALSE, Boolean.valueOf(false));
+ }
+}