Spring Boot @Service Annotation Explained

🚀 Introduction: What is @Service in Spring Boot?

In Spring Boot, the @Service annotation is used to define service-layer components that contain business logic. It is a specialized version of @Component, making it easier to recognize service classes in a Spring application.

Key Features of @Service:
✔ Defines business logic in a Spring Boot application.
✔ Works with dependency injection to manage service beans.
✔ Helps separate the service layer from controllers and repositories.
✔ Provides better readability and maintainability in large projects.

📌 In this guide, you’ll learn:
How @Service works in Spring Boot.
How it differs from @Component and @Repository.
Best practices for structuring the service layer.

1️⃣ Basic Example: Using @Service in a Spring Boot Application

📌 Example: Creating a UserService to Manage Users

1. Service Layer (UserService.java)

@Service
public class UserService {

    private final Map<Integer, String> users = new HashMap<>(Map.of(
        1, "Ramesh",
        2, "Suresh"
    ));

    public String getUserById(int id) {
        return users.getOrDefault(id, "User not found");
    }
}

2. Controller Layer (UserController.java)

@RestController
@RequestMapping("/api/users")
public class UserController {

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/{id}")
    public String getUserById(@PathVariable int id) {
        return userService.getUserById(id);
    }
}

📌 GET Request (GET /api/users/1)
📌 Response:

Ramesh

The @Service annotation tells Spring that UserService is a service-layer component.
Spring Boot automatically manages the bean and injects it into the controller.

2️⃣ Difference Between @Service, @Component, and @Repository


📌 Example: Using @Component Instead of @Service (Valid but Not Recommended)
@Component
public class UserService {  
    public String getUserById(int id) {
        return "User " + id;
    }
}

@Component works, but @Service is preferred for clarity.
Spring Boot doesn’t treat @Service differently, but using it improves code readability.

3️⃣ Injecting Dependencies into @Service Using @Autowired

There are three ways to inject dependencies into a service:

@Service
public class UserService {

    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

Best practice since Spring 4.3 (no need for @Autowired if there’s only one constructor).

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;
}

Not recommended because it makes unit testing harder.

3. Setter-Based Injection (Use Only When Needed)

@Service
public class UserService {

    private UserRepository userRepository;

    @Autowired
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

Useful if dependency injection is optional or needs to be changed dynamically.

4️⃣ Using @Service with a Repository (Spring Data JPA)

📌 Example: Connecting UserService with a Database

1. Repository Layer (UserRepository.java)

public interface UserRepository extends JpaRepository<User, Integer> {
}

2. Service Layer (UserService.java)

@Service
public class UserService {

    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public User getUserById(int id) {
        return userRepository.findById(id)
                .orElseThrow(() -> new UserNotFoundException("User not found with ID: " + id));
    }
}

3. Controller Layer (UserController.java)

@RestController
@RequestMapping("/api/users")
public class UserController {

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/{id}")
    public ResponseEntity<User> getUserById(@PathVariable int id) {
        return ResponseEntity.ok(userService.getUserById(id));
    }
}

📌 GET Request (GET /api/users/1)
📌 Response (HTTP 200 OK with JSON Data):

{
  "id": 1,
  "name": "Ramesh",
  "email": "ramesh@example.com"
}

The service layer retrieves data from the repository and sends it to the controller.
This ensures a clean separation of concerns in the application.

5️⃣ Handling Exceptions in @Service Classes

📌 Example: Throwing a Custom Exception from a Service

@ResponseStatus(HttpStatus.NOT_FOUND)
public class UserNotFoundException extends RuntimeException {
    public UserNotFoundException(String message) {
        super(message);
    }
}

📌 Using the Exception in UserService

public User getUserById(int id) {
    return userRepository.findById(id)
            .orElseThrow(() -> new UserNotFoundException("User not found with ID: " + id));
}

📌 GET Request (GET /api/users/99 - Non-Existing User)
📌 Response (HTTP 404 Not Found):

{
  "error": "User not found with ID: 99"
}

Ensures clean error handling with meaningful HTTP status codes.

🎯 Summary: Best Practices for Using @Service

Use @Service for business logic instead of @Component.
Follow the layered architecture: Controller → Service → Repository.
Use constructor injection for dependencies (avoid field injection).
Handle exceptions properly using @ResponseStatus and @ControllerAdvice.
Ensure service methods follow single-responsibility and reusable logic.

🚀 Following these best practices ensures scalable, maintainable Spring Boot applications!

Comments

Spring Boot 3 Paid Course Published for Free
on my Java Guides YouTube Channel

Subscribe to my YouTube Channel (165K+ subscribers):
Java Guides Channel

Top 10 My Udemy Courses with Huge Discount:
Udemy Courses - Ramesh Fadatare