spring-boot-application
About
This skill helps developers build enterprise Spring Boot applications using annotations, dependency injection, and Spring Boot conventions. It is designed for creating REST APIs, implementing services, managing beans, and configuring data persistence with JPA and security. Use it when developing, configuring, or managing Spring Boot projects and microservices.
Documentation
Spring Boot Application
Overview
Develop production-ready Spring Boot applications with proper annotation-based configuration, dependency injection, REST controllers, JPA data persistence, service layers, and security implementation following Spring conventions.
When to Use
- Building Spring Boot REST APIs
- Implementing service-oriented architectures
- Configuring data persistence with JPA
- Managing dependency injection
- Implementing Spring Security
- Building microservices with Spring Boot
Instructions
1. Spring Boot Project Setup
<!-- pom.xml -->
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>api-service</artifactId>
<version>1.0.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.0</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.12.3</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2. Entity Models with JPA Annotations
// User.java
package com.example.model;
import jakarta.persistence.*;
import java.time.LocalDateTime;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
@Entity
@Table(name = "users", indexes = {
@Index(name = "idx_email", columnList = "email", unique = true),
@Index(name = "idx_role", columnList = "role")
})
public class User implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private String id;
@Column(unique = true, nullable = false)
private String email;
@Column(nullable = false)
private String passwordHash;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private Role role = Role.USER;
@Column(name = "is_active")
private Boolean isActive = true;
@Column(name = "created_at", nullable = false, updatable = false)
private LocalDateTime createdAt = LocalDateTime.now();
@Column(name = "updated_at")
private LocalDateTime updatedAt = LocalDateTime.now();
@OneToMany(mappedBy = "author", cascade = CascadeType.ALL, orphanRemoval = true)
private Collection<Post> posts;
// Getters and setters
@Override
public String getUsername() {
return email;
}
@Override
public String getPassword() {
return passwordHash;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return java.util.List.of(new SimpleGrantedAuthority(role.name()));
}
@Override
public boolean isEnabled() {
return isActive;
}
public enum Role {
USER, ADMIN
}
}
// Post.java
@Entity
@Table(name = "posts")
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private String id;
@Column(nullable = false)
private String title;
@Column(nullable = false, columnDefinition = "TEXT")
private String content;
@Column(nullable = false)
private Boolean published = false;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "user_id")
private User author;
@Column(name = "created_at", nullable = false, updatable = false)
private LocalDateTime createdAt = LocalDateTime.now();
@Column(name = "updated_at")
private LocalDateTime updatedAt = LocalDateTime.now();
// Getters and setters
}
3. Repository Layer with Spring Data JPA
// UserRepository.java
package com.example.repository;
import com.example.model.User;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface UserRepository extends JpaRepository<User, String> {
Optional<User> findByEmail(String email);
@Query("SELECT u FROM User u WHERE u.email LIKE %:search% OR u.firstName LIKE %:search%")
Page<User> searchUsers(String search, Pageable pageable);
Page<User> findByRole(User.Role role, Pageable pageable);
}
// PostRepository.java
@Repository
public interface PostRepository extends JpaRepository<Post, String> {
Page<Post> findByAuthorAndPublishedTrue(User author, Pageable pageable);
@Query(value = "SELECT p FROM Post p WHERE p.published = true ORDER BY p.createdAt DESC",
countQuery = "SELECT COUNT(p) FROM Post p WHERE p.published = true")
Page<Post> findPublishedPosts(Pageable pageable);
Long countByAuthorId(String authorId);
}
4. Service Layer with Business Logic
// UserService.java
package com.example.service;
import com.example.model.User;
import com.example.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Optional;
@Service
@RequiredArgsConstructor
@Transactional
public class UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
public User createUser(String email, String password, String firstName, String lastName) {
if (userRepository.findByEmail(email).isPresent()) {
throw new IllegalArgumentException("Email already exists");
}
User user = new User();
user.setEmail(email);
user.setPasswordHash(passwordEncoder.encode(password));
user.setFirstName(firstName);
user.setLastName(lastName);
return userRepository.save(user);
}
@Transactional(readOnly = true)
public Page<User> getUsers(String search, Pageable pageable) {
if (search != null && !search.isBlank()) {
return userRepository.searchUsers(search, pageable);
}
return userRepository.findAll(pageable);
}
@Transactional(readOnly = true)
public Optional<User> getUserById(String id) {
return userRepository.findById(id);
}
public User updateUser(String id, String firstName, String lastName) {
User user = userRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("User not found"));
if (firstName != null) user.setFirstName(firstName);
if (lastName != null) user.setLastName(lastName);
return userRepository.save(user);
}
public void deleteUser(String id) {
userRepository.deleteById(id);
}
}
// PostService.java
@Service
@RequiredArgsConstructor
@Transactional
public class PostService {
private final PostRepository postRepository;
private final UserRepository userRepository;
public Post createPost(String userId, String title, String content) {
User author = userRepository.findById(userId)
.orElseThrow(() -> new IllegalArgumentException("User not found"));
Post post = new Post();
post.setTitle(title);
post.setContent(content);
post.setAuthor(author);
return postRepository.save(post);
}
@Transactional(readOnly = true)
public Page<Post> getPublishedPosts(Pageable pageable) {
return postRepository.findPublishedPosts(pageable);
}
public Post publishPost(String postId) {
Post post = postRepository.findById(postId)
.orElseThrow(() -> new IllegalArgumentException("Post not found"));
post.setPublished(true);
return postRepository.save(post);
}
}
5. REST Controllers with Request/Response Handling
// UserController.java
package com.example.controller;
import com.example.model.User;
import com.example.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@GetMapping
public ResponseEntity<Page<User>> getUsers(
@RequestParam(required = false) String search,
Pageable pageable) {
Page<User> users = userService.getUsers(search, pageable);
return ResponseEntity.ok(users);
}
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable String id) {
return userService.getUserById(id)
.map(ResponseEntity::ok)
.orElseGet(() -> ResponseEntity.notFound().build());
}
@PostMapping
public ResponseEntity<User> createUser(@RequestBody CreateUserRequest request) {
User user = userService.createUser(
request.getEmail(),
request.getPassword(),
request.getFirstName(),
request.getLastName()
);
return ResponseEntity.status(HttpStatus.CREATED).body(user);
}
@PatchMapping("/{id}")
@PreAuthorize("@securityService.isCurrentUser(#id)")
public ResponseEntity<User> updateUser(
@PathVariable String id,
@RequestBody UpdateUserRequest request) {
User user = userService.updateUser(id, request.getFirstName(), request.getLastName());
return ResponseEntity.ok(user);
}
@DeleteMapping("/{id}")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<Void> deleteUser(@PathVariable String id) {
userService.deleteUser(id);
return ResponseEntity.noContent().build();
}
}
// DTO classes
@Data
class CreateUserRequest {
private String email;
private String password;
private String firstName;
private String lastName;
}
@Data
class UpdateUserRequest {
private String firstName;
private String lastName;
}
6. Spring Security Configuration
// SecurityConfig.java
package com.example.config;
import com.example.security.JwtAuthenticationFilter;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final JwtAuthenticationFilter jwtAuthenticationFilter;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeHttpRequests()
.requestMatchers("/api/auth/**").permitAll()
.requestMatchers("/api/users/**").authenticated()
.anyRequest().authenticated()
.and()
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
// JwtAuthenticationFilter.java
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtTokenProvider tokenProvider;
private final UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
try {
String token = getJwtFromRequest(request);
if (token != null && tokenProvider.validateToken(token)) {
String userId = tokenProvider.getUserIdFromToken(token);
UserDetails userDetails = userDetailsService.loadUserByUsername(userId);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception ex) {
logger.error("Could not set user authentication", ex);
}
filterChain.doFilter(request, response);
}
private String getJwtFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}
7. Application Configuration
# application.yml
spring:
application:
name: api-service
datasource:
url: jdbc:postgresql://localhost:5432/mydb
username: ${DB_USER}
password: ${DB_PASSWORD}
driver-class-name: org.postgresql.Driver
jpa:
hibernate:
ddl-auto: validate
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
security:
jwt:
secret: ${JWT_SECRET}
expiration: 86400000
server:
port: 8080
servlet:
context-path: /
Best Practices
✅ DO
- Use dependency injection for loose coupling
- Implement service layer for business logic
- Use repositories for data access
- Leverage Spring Security for authentication
- Use @Transactional for transaction management
- Validate input in controllers
- Return appropriate HTTP status codes
- Use DTOs for request/response mapping
- Implement proper exception handling
- Use Spring's @Async for async operations
❌ DON'T
- Put business logic in controllers
- Access database directly in controllers
- Store secrets in configuration files
- Use eager loading for large relationships
- Ignore transaction boundaries
- Return database entities in API responses
- Implement authentication in controllers
- Use raw SQL without parameterized queries
- Forget to validate user input
Complete Example
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@RestController
@RequestMapping("/api")
public class SimpleController {
@GetMapping("/health")
public ResponseEntity<String> health() {
return ResponseEntity.ok("OK");
}
}
Quick Install
/plugin add https://github.com/aj-geddes/useful-ai-prompts/tree/main/spring-boot-applicationCopy and paste this command in Claude Code to install this skill
GitHub 仓库
Related Skills
evaluating-llms-harness
TestingThis Claude Skill runs the lm-evaluation-harness to benchmark LLMs across 60+ standardized academic tasks like MMLU and GSM8K. It's designed for developers to compare model quality, track training progress, or report academic results. The tool supports various backends including HuggingFace and vLLM models.
langchain
MetaLangChain is a framework for building LLM applications using agents, chains, and RAG pipelines. It supports multiple LLM providers, offers 500+ integrations, and includes features like tool calling and memory management. Use it for rapid prototyping and deploying production systems like chatbots, autonomous agents, and question-answering services.
Algorithmic Art Generation
MetaThis skill helps developers create algorithmic art using p5.js, focusing on generative art, computational aesthetics, and interactive visualizations. It automatically activates for topics like "generative art" or "p5.js visualization" and guides you through creating unique algorithms with features like seeded randomness, flow fields, and particle systems. Use it when you need to build reproducible, code-driven artistic patterns.
webapp-testing
TestingThis Claude Skill provides a Playwright-based toolkit for testing local web applications through Python scripts. It enables frontend verification, UI debugging, screenshot capture, and log viewing while managing server lifecycles. Use it for browser automation tasks but run scripts directly rather than reading their source code to avoid context pollution.
