Top 10 Common Spring Framework Mistakes and How to Fix Them
This article outlines the ten most frequent errors developers make when using the Spring framework—ranging from over‑focusing on low‑level details to neglecting proper testing—and provides concrete best‑practice solutions with code examples to improve code quality, maintainability, and reliability.
Error 1: Over‑focusing on low‑level details
Developers often rewrite common code because they are obsessed with the internal implementation of libraries; while understanding internals is useful, constantly dealing with low‑level details harms productivity. Embrace Spring's abstraction, search for existing solutions, and use tools like Project Lombok to reduce boilerplate.
@Getter
@NoArgsConstructor
@Entity
public class TopTalentEntity {
@Id
@GeneratedValue
private Integer id;
@Column
private String name;
}When using Lombok, install the appropriate IDE plugin.
Error 2: Leaking internal structure
Exposing entity classes directly through APIs couples the database schema to the service contract. Instead, create a DTO that represents the data needed by the client.
@Entity
@NoArgsConstructor
@Getter
public class TopTalentEntity {
// fields as above
}
@AllArgsConstructor
@NoArgsConstructor
@Getter
public class TopTalentData {
private String name;
}This separation allows database changes without affecting the API layer.
Error 3: Missing separation of concerns
Mixing controller logic with data conversion and repository access violates clean architecture. Refactor by moving business logic to a service and conversion to a dedicated component.
@RestController
@RequestMapping("/toptal")
@AllArgsConstructor
public class TopTalentController {
private final TopTalentService topTalentService;
@RequestMapping("/get")
public List<TopTalentData> getTopTalent() {
return topTalentService.getTopTalent();
}
}
@Service
@AllArgsConstructor
public class TopTalentService {
private final TopTalentRepository topTalentRepository;
private final TopTalentEntityConverter converter;
public List<TopTalentData> getTopTalent() {
return topTalentRepository.findAll()
.stream()
.map(converter::toResponse)
.collect(Collectors.toList());
}
}
@Component
public class TopTalentEntityConverter {
public TopTalentData toResponse(TopTalentEntity e) {
return new TopTalentData(e.getName());
}
}Error 4: Inadequate exception handling
Define a consistent error response format and use @ExceptionHandler to translate exceptions into that format, avoiding raw stack traces and generic 500 responses.
@Value
public class ErrorResponse {
private Integer errorCode;
private String errorMessage;
}
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleInvalid(MethodArgumentNotValidException ex) {
// build and return ErrorResponse
}Error 5: Improper multithreading
Avoid global mutable state, prefer immutable objects, log essential data sparingly, reuse existing concurrency utilities (ExecutorService, CompletableFuture, DeferredResult), and synchronize only when absolutely necessary.
Error 6: Not using annotation‑based validation
Leverage Hibernate Validator with @Valid, @NotNull, @Length, or create custom annotations for reusable constraints.
@RequestMapping("/put")
public void addTopTalent(@Valid @NotNull @RequestBody TopTalentData data) {
topTalentService.addTopTalent(data);
}
public class TopTalentData {
@Length(min = 10, max = 10)
@NotNull
private String name;
}Error 7: Relying on XML configuration
Modern Spring applications should use Java‑based configuration and annotations such as @SpringBootApplication, @Component, @Service, @Repository, and @Configuration instead of XML files.
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}Error 8: Ignoring profiles
Separate environment‑specific settings using Spring profiles (dev, prod) in application.yml and activate the appropriate profile at runtime.
# application.yml
spring:
profiles:
active: dev
# application-dev.yml
spring:
datasource:
url: jdbc:h2:mem:
platform: h2
# application-prod.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/toptal
username: root
password:Error 9: Rejecting dependency injection
Prefer constructor injection over manual instantiation to let Spring manage bean lifecycles and enable easier testing.
// Bad: manual new
public TopTalentController() {
this.topTalentService = new TopTalentService();
}
// Good: constructor injection
@RestController
@AllArgsConstructor
public class TopTalentController {
private final TopTalentService topTalentService;
}Error 10: Lack of testing or poor testing
Write unit tests for business logic and integration tests for web endpoints. Use tools like RestAssuredMockMvc to test controllers with mocked services.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {Application.class, SampleUnitTestConfig.class})
public class RestAssuredTest {
@Autowired
private TopTalentController controller;
@Test
public void shouldGetMaryAndJoel() throws Exception {
MockMvcRequestSpecification spec = RestAssuredMockMvc.given().standaloneSetup(controller);
MockMvcResponse resp = spec.when().get("/toptal/get");
resp.then().statusCode(200);
resp.then().body("name", hasItems("Mary", "Joel"));
}
}Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java High-Performance Architecture
Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
