spring — Cursor rule: what it does, how to install
This rule guides Spring Boot code structure, dependency injection, and common pitfalls. Use it when asking an agent to write or review Java code in a project.
.cursor/rules/spring.mdcWhat spring does
The rule covers package layout, dedicated configuration classes, constructor-based dependency injection, and immutable data models. It also shows how to fetch related records without N+1 queries and validate user input at the API boundary.
For exception handling, it recommends a shared handler using @ControllerAdvice; auto-configuration should be disabled only when needed. Its examples include Java record DTOs and specify Java 16+. The rule targets **/*.java files, so it focuses on code guidance rather than project-wide setup.
What it helps with
- split bean definitions into focused configuration classes
- replace field injection with constructor injection
- avoid N+1 queries when loading related records
- validate request data at the API boundary
- handle exceptions through a shared controller advice
Example prompts for the agent
- Review this Java code against the Spring Boot rule and suggest specific changes.
- Refactor this service to use constructor injection and avoid N+1 queries.
- Check this user-creation endpoint for input validation and shared exception handling.
How to install spring
Manually: put the files in place
.cursor/rules/spring.mdcFrontmatter: description, globs, alwaysApply
Manually: put the files in place
.claude/rules/spring.mdThe paths frontmatter field limits the files: move globs from the source into paths
AGENTS.md
Codex reads AGENTS.md: paste the rule text there
Manually: put the files in place
.github/instructions/spring.instructions.mdThe applyTo field sets which files it applies to: move globs from the source into applyTo
Manually: put the files in place
.devin/rules/spring.mdManually: put the files in place
.clinerules/spring.mdStep by step, with commands for each platform: Cursor rules: .mdc files, four modes and what replaced .cursorrules
Questions about spring
How do I install spring in Cursor?
Add the rule file at .cursor/rules/spring.mdc.
What does the spring rule do?
It gives guidance on Spring Boot structure, dependency injection, request validation, query patterns, and exception handling.
Can I use record for DTOs?
The examples recommend immutable DTOs with record and identify Java 16+ as the requirement.
Source text
spring.mdc · CC0-1.0
---
description: Definitive guidelines for building robust, maintainable, and performant Spring Boot applications using modern best practices.
globs: **/*.java
---
# Spring Best Practices
This guide outlines essential best practices for developing with Spring Boot, ensuring your applications are well-structured, performant, secure, and easily testable.
## 1. Code Organization and Structure
Maintain a clean, logical package structure.
### 1.1. Base Package and `@SpringBootApplication`
Place your main application class at the root of a well-named base package. All components should reside within this base package or its sub-packages.
❌ BAD:
```java
// com.example.Application.java
package com.example; // Too generic, or in default package
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);
}
}
```
✅ GOOD:
```java
// com.company.project.MyApplication.java
package com.company.project; // Specific base package
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication { // Clear application entry point
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
```
### 1.2. Configuration Classes
Group related bean definitions into dedicated `@Configuration` classes.
❌ BAD:
```java
// MyApplication.java (monolithic config)
@SpringBootApplication
public class MyApplication {
// ...
@Bean
public DataSource dataSource() { /* ... */ }
@Bean
public RestTemplate restTemplate() { /* ... */ }
}
```
✅ GOOD:
```java
// com.company.project.config.DataSourceConfig.java
@Configuration
public class DataSourceConfig {
@Bean
public DataSource dataSource() { /* ... */ }
}
// com.company.project.config.WebClientConfig.java
@Configuration
public class WebClientConfig {
@Bean
public WebClient webClient(WebClient.Builder builder) {
return builder.baseUrl("http://api.example.com").build();
}
}
```
## 2. Common Patterns and Anti-patterns
### 2.1. Constructor Injection
Always prefer constructor injection for mandatory dependencies. It ensures immutability and testability.
❌ BAD:
```java
@Service
public class UserService {
@Autowired // Field injection is discouraged
private UserRepository userRepository;
public User findById(Long id) {
return userRepository.findById(id).orElse(null);
}
}
```
✅ GOOD:
```java
@Service
public class UserService {
private final UserRepository userRepository; // Immutable
@Autowired // Optional for single constructor in Spring 4.3+
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User findById(Long id) {
return userRepository.findById(id).orElse(null);
}
}
```
### 2.2. Immutable Data Models
Use immutable classes for DTOs and entities where possible, especially for value objects.
❌ BAD:
```java
public class UserDto { // Mutable
private Long id;
private String name;
// Getters and Setters
}
```
✅ GOOD:
```java
public record UserDto(Long id, String name) {} // Immutable record (Java 16+)
// Or with Lombok:
@Value // All-args constructor, getters, equals, hashCode, toString
public class UserDto {
Long id;
String name;
}
```
## 3. Performance Considerations
### 3.1. N+1 Query Problem
Avoid N+1 queries by eagerly fetching related data when necessary.
❌ BAD:
```java
@Service
public class OrderService {
@Autowired private OrderRepository orderRepository;
public List<Order> getOrdersWithItems() {
List<Order> orders = orderRepository.findAll();
orders.forEach(order -> order.getItems().size()); // N+1 queries for items
return orders;
}
}
```
✅ GOOD:
```java
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
@Query("SELECT o FROM Order o JOIN FETCH o.items") // Eagerly fetch items
List<Order> findAllWithItems();
}
@Service
public class OrderService {
@Autowired private OrderRepository orderRepository;
public List<Order> getOrdersWithItems() {
return orderRepository.findAllWithItems(); // Single query
}
}
```
## 4. Common Pitfalls and Gotchas
### 4.1. Disabling Auto-configuration
Only disable auto-configuration when strictly necessary to avoid unexpected behavior.
❌ BAD:
```java
// Always disabling specific auto-configs without understanding impact
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
public class MyApplication { /* ... */ }
```
✅ GOOD:
```java
// Only disable when you provide your own explicit configuration
// and the auto-config conflicts or is unwanted.
// For example, if you manage DataSource manually:
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class MyApplication { /* ... */ }
```
## 5. Security Best Practices
### 5.1. Input Validation
Always validate user input at the API boundary. Use Spring's validation features.
❌ BAD:
```java
@PostMapping("/users")
public ResponseEntity<UserDto> createUser(@RequestBody UserDto userDto) {
// No validation, directly use userDto
userService.save(userDto);
return ResponseEntity.ok(userDto);
}
```
✅ GOOD:
```java
@PostMapping("/users")
public ResponseEntity<UserDto> createUser(@Valid @RequestBody UserDto userDto) {
// @Valid triggers validation based on JSR-303 annotations in UserDto
userService.save(userDto);
return ResponseEntity.ok(userDto);
}
// In UserDto:
public record UserDto(@NotNull Long id, @NotBlank @Size(min = 2, max = 50) String name) {}
```
## 6. Error Handling
### 6.1. Global Exception Handling
Use `@ControllerAdvice` for consistent global error handling.
❌ BAD:
```java
@RestController
public class UserController {
// ...
@GetMapping("/{id}")
public UserDto getUser(@PathVariable Long id) {
try {
return userService.findById(id);
} catch (UserNotFoundException e) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found", e);
}
}
}
```
✅ GOOD:
```java
@RestControllerAdvice // Global exception handler
public class GlobalExceptionHandler {
@ExceptionHandler(UserNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ErrorResponse handleUserNotFound(UserNotFoundException ex) {
return new ErrorResponse("USER_NOT_FOUND", ex.getMessage());
}
}
// In UserController:
@RestController
public class UserController {
// ...
@GetMapping("/{id}")
public UserDto getUser(@PathVariable Long id) {
return userService.findById(id); // Let exception propagate
}
}
// Custom exception:
public class UserNotFoundException extends RuntimeException { /* ... */ }
public record ErrorResponse(String code, String message) {}
```
## 7. API Design
### 7.1. RESTful Principles and DTOs
Design REST APIs following standard principles and use DTOs to decouple internal models from external API contracts.
❌ BAD:
```java
@RestController
@RequestMapping("/products")
public class ProductController {
@Autowired private ProductService productService;
@GetMapping("/{id}")
public Product getProduct(@PathVariable Long id) { // Exposing internal entity
return productService.findById(id);
}
}
```
✅ GOOD:
```java
@RestController
@RequestMapping("/api/v1/products") // Versioned API path
public class ProductController {
@Autowired private ProductService productService;
@GetMapping("/{id}")
public ProductResponseDto getProduct(@PathVariable Long id) {
Product product = productService.findById(id);
return ProductMapper.toDto(product); // Map to DTO
}
}
public record ProductResponseDto(Long id, String name, BigDecimal price) {}
// ProductMapper is a utility to convert Product entity to ProductResponseDto
```
## 8. Testing Approaches
### 8.1. Layered Testing
Use specific Spring Boot test annotations for different layers.
❌ BAD:
```java
@SpringBootTest // Heavy for simple unit tests
public class UserServiceTest {
@Autowired private UserService userService;
// ...
}
```
✅ GOOD:
```java
// Unit test for service layer (mocking dependencies)
@ExtendWith(MockitoExtension.class)
public class UserServiceUnitTest {
@Mock private UserRepository userRepository;
@InjectMocks private UserService userService;
@Test
void findById_shouldReturnUser() {
// ...
}
}
// Integration test for web layer (no full context)
@WebMvcTest(UserController.class)
public class UserControllerWebMvcTest {
@Autowired private MockMvc mockMvc;
@MockBean private UserService userService; // Mock service dependency
@Test
void getUser_shouldReturnOk() throws Exception {
// ...
}
}
// Integration test for data layer
@DataJpaTest
public class UserRepositoryIntegrationTest {
@Autowired private TestEntityManager entityManager;
@Autowired private UserRepository userRepository;
@Test
void findById_shouldReturnProduct() {
// ...
}
}
// Full application context test (use sparingly)
@SpringBootTest
@AutoConfigureMockMvc
public class MyApplicationIntegrationTest {
@Autowired private MockMvc mockMvc;
// ...
}
```
© sanjeed5 · CC0-1.0 · sanjeed5/awesome-cursor-rules-mdc