Spring is perhaps one of the most popular Java development platforms. This is a powerful, but rather difficult to learn tool. Its basic concepts are fairly easy to understand and learn, but it takes time and effort to become an experienced Spring developer.
In this article, we will look at some of the most common mistakes made when working in Spring and related, in particular, to the development of web applications and the use of the Spring Boot platform. As noted on the Spring Boot website , Spring Boot takes a standardized approach to creating ready-to-use applications, and this article will follow this approach. It will give a number of recommendations that can be effectively used in the development of standard web applications based on Spring Boot.
In case you are not very familiar with the Spring Boot platform, but want to experiment with the examples in this article, I created a GitHub repository with additional materials for this article . If at some point you are a little confused reading this article, I would advise you to create a clone of this repository and experiment with the code on your computer.
@RequestMapping("/put") public void addTopTalent(@RequestBody TopTalentData topTalentData) { boolean nameNonExistentOrHasInvalidLength = Optional.ofNullable(topTalentData) .map(TopTalentData::getName) .map(name -> name.length() == 10) .orElse(true); if (nameNonExistentOrInvalidLength) { // } topTalentService.addTopTalent(topTalentData); }
TopTalentData
TopTalentData
TopTalentData.name
addTopTalent
@RequestMapping("/put") public void addTopTalent(@Valid @NotNull @RequestBody TopTalentData topTalentData) { topTalentService.addTopTalent(topTalentData); } @ExceptionHandler @ResponseStatus(HttpStatus.BAD_REQUEST) public ErrorResponse handleInvalidTopTalentDataException(MethodArgumentNotValidException methodArgumentNotValidException) { // }
TopTalentData
public class TopTalentData { @Length(min = 10, max = 10) @NotNull private String name; }
@Length
@Length
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented @Constraint(validatedBy = { MyAnnotationValidator.class }) public @interface MyAnnotation { String message() default "String length does not match expected"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; int value(); } @Component public class MyAnnotationValidator implements ConstraintValidator<MyAnnotation, String> { private int expectedLength; @Override public void initialize(MyAnnotation myAnnotation) { this.expectedLength = myAnnotation.value(); } @Override public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) { return s == null || s.length() == this.expectedLength; } }
(s == null
isValid
@NotNull
public class TopTalentData { @MyAnnotation(value = 10) @NotNull private String name; }
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
@Configuration
# set default profile to 'dev' spring.profiles.active: dev # production database details spring.datasource.url: 'jdbc:mysql://localhost:3306/toptal' spring.datasource.username: root spring.datasource.password:
spring.datasource.url: 'jdbc:h2:mem:' spring.datasource.platform: h2
-Dspring.profiles.active=prod
public class TopTalentController { private final TopTalentService topTalentService; public TopTalentController() { this.topTalentService = new TopTalentService(); } }
public class TopTalentController { private final TopTalentService topTalentService; public TopTalentController(TopTalentService topTalentService) { this.topTalentService = topTalentService; } }
TopTalentService
TopTalentService
@Configuration public class SampleUnitTestConfig { @Bean public TopTalentService topTalentService() { TopTalentService topTalentService = Mockito.mock(TopTalentService.class); Mockito.when(topTalentService.getTopTalent()).thenReturn( Stream.of("Mary", "Joel").map(TopTalentData::new).collect(Collectors.toList())); return topTalentService; } }
SampleUnitTestConfig
SampleUnitTestConfig
@ContextConfiguration(classes = { SampleUnitTestConfig.class })
DispatcherServlet
HttpServletRequest
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { Application.class, SampleUnitTestConfig.class }) public class RestAssuredTestDemonstration { @Autowired private TopTalentController topTalentController; @Test public void shouldGetMaryAndJoel() throws Exception { // given MockMvcRequestSpecification givenRestAssuredSpecification = RestAssuredMockMvc.given() .standaloneSetup(topTalentController); // when MockMvcResponse response = givenRestAssuredSpecification.when().get("/toptal/get"); // then response.then().statusCode(200); response.then().body("name", hasItems("Mary", "Joel")); } }
SampleUnitTestConfig
TopTalentService
TopTalentController
RestAssuredMockMvc
GET
/toptal/get