Skip to main content
rule

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.

Install into Cursor · Manually: put the files in place All methods and platforms ↓
.cursor/rules/spring.mdc
Open on GitHub

What 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.mdc

Frontmatter: description, globs, alwaysApply

Step 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

Similar IDE rules

All IDE rules for Backend and APIs →

From the same set240

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor