Skip to content

Java & Spring Boot Patterns

Master enterprise Java development with Cursor IDE and Claude Code. This guide covers Spring Boot applications, microservices architectures, database integration with JPA/Hibernate, testing strategies, and AI-assisted enterprise patterns for building scalable Java applications.

  1. Initialize Java Project

    Terminal window
    # Spring Boot with Spring Initializr
    Ask: "Create a new Spring Boot project with Web, JPA, Security, and Actuator dependencies"
    # Or use Agent mode
    Agent: "Set up a Spring Boot microservice with REST API, PostgreSQL, and Docker support"
  2. Configure Development Environment

    application.yml
    spring:
    profiles:
    active: dev
    datasource:
    url: jdbc:postgresql://localhost:5432/myapp
    username: ${DB_USER}
    password: ${DB_PASSWORD}
    jpa:
    hibernate:
    ddl-auto: validate
    show-sql: true
  3. Set Up AI Rules

    Terminal window
    # .cursorrules or CLAUDE.md
    - Use Java 21 features (records, pattern matching, virtual threads)
    - Follow Spring Boot best practices
    - Implement proper exception handling with @ControllerAdvice
    - Use constructor injection over field injection
    - Write tests for all public methods
    - Follow clean architecture principles
// AI Prompt
Agent: "Create a complete REST API for Product management with:
- CRUD operations
- Pagination and sorting
- Search functionality
- Input validation
- Exception handling
- OpenAPI documentation"
// The agent will generate:
@RestController
@RequestMapping("/api/v1/products")
@RequiredArgsConstructor
@Tag(name = "Product Management")
public class ProductController {
private final ProductService productService;
@GetMapping
@Operation(summary = "Get all products with pagination")
public Page<ProductDTO> getAllProducts(
@PageableDefault(size = 20) Pageable pageable,
@RequestParam(required = false) String search) {
return productService.findAll(search, pageable);
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
@Operation(summary = "Create a new product")
public ProductDTO createProduct(@Valid @RequestBody CreateProductRequest request) {
return productService.create(request);
}
}
// AI Prompt: "Implement service layer with transaction management and caching"
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
@Slf4j
public class UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final EventPublisher eventPublisher;
@Transactional
@CacheEvict(value = "users", key = "#result.id")
public UserDTO createUser(CreateUserRequest request) {
log.debug("Creating user with email: {}", request.email());
if (userRepository.existsByEmail(request.email())) {
throw new DuplicateResourceException("Email already exists");
}
User user = User.builder()
.email(request.email())
.password(passwordEncoder.encode(request.password()))
.roles(Set.of(Role.USER))
.build();
user = userRepository.save(user);
eventPublisher.publish(new UserCreatedEvent(user.getId()));
return UserMapper.toDTO(user);
}
@Cacheable(value = "users", key = "#id")
public UserDTO findById(Long id) {
return userRepository.findById(id)
.map(UserMapper::toDTO)
.orElseThrow(() -> new ResourceNotFoundException("User", id));
}
}
// AI Prompt
Ask: "Create JPA entities for an e-commerce system with:
- Proper relationships (OneToMany, ManyToMany)
- Audit fields
- Soft delete functionality
- Custom repository methods
- Specifications for complex queries"
// Generated Entity
@Entity
@Table(name = "orders")
@EntityListeners(AuditingEntityListener.class)
@SQLDelete(sql = "UPDATE orders SET deleted = true WHERE id = ?")
@Where(clause = "deleted = false")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "customer_id", nullable = false)
private Customer customer;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<OrderItem> items = new ArrayList<>();
@Enumerated(EnumType.STRING)
private OrderStatus status;
@CreatedDate
private LocalDateTime createdAt;
@LastModifiedDate
private LocalDateTime updatedAt;
private boolean deleted = false;
}
// AI Prompt: "Implement resilient service communication with Circuit Breaker"
@Component
@RequiredArgsConstructor
public class PaymentServiceClient {
private final WebClient webClient;
@CircuitBreaker(name = "payment-service", fallbackMethod = "fallbackPayment")
@Retry(name = "payment-service")
@TimeLimiter(name = "payment-service")
public Mono<PaymentResponse> processPayment(PaymentRequest request) {
return webClient.post()
.uri("/api/payments")
.bodyValue(request)
.retrieve()
.bodyToMono(PaymentResponse.class)
.doOnError(error -> log.error("Payment failed", error));
}
public Mono<PaymentResponse> fallbackPayment(PaymentRequest request, Exception ex) {
log.warn("Payment service unavailable, using fallback", ex);
return Mono.just(PaymentResponse.pending(request.getOrderId()));
}
}
// AI Prompt
Agent: "Implement event-driven architecture with:
- Kafka integration
- Event sourcing pattern
- Saga orchestration
- Dead letter queue handling"
// Event Publisher
@Component
@RequiredArgsConstructor
public class OrderEventPublisher {
private final KafkaTemplate<String, OrderEvent> kafkaTemplate;
@EventListener
@Async
public void handleOrderCreated(OrderCreatedEvent event) {
OrderEvent kafkaEvent = OrderEvent.builder()
.eventId(UUID.randomUUID().toString())
.eventType("ORDER_CREATED")
.aggregateId(event.getOrderId())
.payload(event)
.timestamp(Instant.now())
.build();
kafkaTemplate.send("order-events", kafkaEvent)
.addCallback(
result -> log.info("Event sent: {}", kafkaEvent.getEventId()),
error -> log.error("Failed to send event", error)
);
}
}
// AI Prompt: "Generate comprehensive unit tests with Mockito"
@ExtendWith(MockitoExtension.class)
class ProductServiceTest {
@Mock
private ProductRepository productRepository;
@Mock
private EventPublisher eventPublisher;
@InjectMocks
private ProductService productService;
@Test
@DisplayName("Should create product and publish event")
void createProduct_Success() {
// Given
CreateProductRequest request = CreateProductRequest.builder()
.name("Test Product")
.price(BigDecimal.valueOf(99.99))
.build();
Product savedProduct = Product.builder()
.id(1L)
.name(request.name())
.price(request.price())
.build();
when(productRepository.save(any(Product.class))).thenReturn(savedProduct);
// When
ProductDTO result = productService.create(request);
// Then
assertThat(result).isNotNull();
assertThat(result.name()).isEqualTo(request.name());
verify(eventPublisher).publish(any(ProductCreatedEvent.class));
}
}
// AI Prompt
Ask: "Create integration tests with:
- @SpringBootTest
- TestContainers for PostgreSQL
- MockMvc for API testing
- Test data builders
- Database cleanup between tests"
@SpringBootTest
@AutoConfigureMockMvc
@Testcontainers
@ActiveProfiles("test")
class ProductIntegrationTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test");
@Autowired
private MockMvc mockMvc;
@Test
@Sql("/test-data/products.sql")
void getProducts_ReturnsPagedResults() throws Exception {
mockMvc.perform(get("/api/v1/products")
.param("page", "0")
.param("size", "10"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.content").isArray())
.andExpect(jsonPath("$.content[0].name").value("Test Product"));
}
}
// AI Prompt: "Implement JWT-based authentication with Spring Security"
@EnableWebSecurity
@EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final JwtAuthenticationFilter jwtAuthFilter;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.disable())
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling(exceptions -> exceptions
.authenticationEntryPoint(new JwtAuthenticationEntryPoint())
.accessDeniedHandler(new CustomAccessDeniedHandler())
)
.build();
}
}
// AI Prompt
Agent: "Optimize application for high concurrency using:
- Virtual threads
- Connection pooling
- Query optimization
- Response caching
- Async processing"
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean
public ExecutorService virtualThreadExecutor() {
return Executors.newVirtualThreadPerTaskExecutor();
}
@Bean
public AsyncTaskExecutor applicationTaskExecutor() {
return new TaskExecutorAdapter(virtualThreadExecutor());
}
}
// AI Prompt: "Set up comprehensive monitoring with Micrometer"
@RestController
@RequestMapping("/api/orders")
@Timed
public class OrderController {
private final MeterRegistry meterRegistry;
private final Counter orderCounter;
public OrderController(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
this.orderCounter = Counter.builder("orders.created")
.description("Number of orders created")
.register(meterRegistry);
}
@PostMapping
@Timed(value = "orders.create.time", description = "Time to create order")
public OrderDTO createOrder(@RequestBody CreateOrderRequest request) {
return Metrics.timer("order.creation.timer").record(() -> {
OrderDTO order = orderService.create(request);
orderCounter.increment();
return order;
});
}
}

Enterprise Java Guidelines

  1. Clean Architecture - Separate business logic from frameworks
  2. SOLID Principles - Design for maintainability
  3. Domain-Driven Design - Model complex business domains
  4. Test Pyramid - Unit > Integration > E2E tests
  5. 12-Factor App - Build cloud-native applications
  6. API-First Design - Start with OpenAPI specification
// AI: "Generate builder pattern with validation"
@Builder(toBuilder = true)
@Value
public class Order {
@NotNull Long id;
@NotNull Long customerId;
@NotEmpty List<OrderItem> items;
@NotNull OrderStatus status;
@NotNull Instant createdAt;
public static class OrderBuilder {
public Order build() {
Order order = new Order(id, customerId, items, status, createdAt);
ValidationUtils.validate(order);
return order;
}
}
}