Hook: Interviewers love this question because it quickly shows whether you see Spring annotations as random labels or as clear role markers.
Question: Difference between @Component, @Service and @Repository.
Answer: @Component is the general Spring stereotype for any managed bean. @Service is a specialized @Component for business logic, and @Repository is a specialized @Component for persistence or data access. All three can be found by component scanning, but @Repository can also activate exception translation so low-level persistence errors become Spring DataAccessException types.
Interview-Ready Answer: I’d say all three create Spring beans, but they communicate different intent. @Component is the generic choice for any Spring-managed class. @Service marks business logic, and @Repository marks the persistence layer and gives one extra benefit: Spring can translate database and JPA exceptions into its own unchecked data-access exceptions. So in practice, I use @Service for orchestration and rules, @Repository for DB access, and @Component for shared helpers.
@Component, @Service, and @Repository are all stereotype annotations. A stereotype annotation is a label that tells Spring what role a class plays. @Service and @Repository are meta-annotated with @Component, which means they are built on top of the generic bean annotation.
@SpringBootApplication and walks the subpackages.@Component, @Service, or @Repository, it registers a bean definition in the application context.@Service does not add special runtime behavior by default; it mainly tells humans, tests, and tooling that this class holds business logic.@Repository is the special one: Spring can apply exception translation so persistence exceptions from JPA, Hibernate, or JDBC are converted into Spring’s unchecked data-access exception hierarchy.new is needed.@Component: use for generic reusable helpers, mappers, formatters, adapters, or infrastructure code that does not clearly fit service or repository.@Service: use for business operations and orchestration, such as placeOrder, calculateDiscount, or registerUser.@Repository: use for persistence code, such as custom DAO classes, query objects, or adapters that talk to the database.| Annotation | Main meaning | Extra behavior | Typical place |
|---|---|---|---|
@Component | Generic bean | None | Utilities |
@Service | Business layer | Semantic only | Use-case logic |
@Repository | Data access | Exception translation | DAO / persistence |
At runtime, the annotation choice itself has almost no cost. The main cost happens at startup during component scanning, which is roughly O(n) in the number of candidate classes Spring must inspect. Bean lookup after startup is effectively O(1) because the container keeps bean definitions in maps. In a small app, scanning is usually fast; in a very large monolith with thousands of classes and broad package scanning, startup can add noticeable time, sometimes from hundreds of milliseconds into seconds. Also remember that if a bean gets proxied for transactions or repository advice, there is a small extra startup and memory cost, but still not a per-request penalty worth worrying about in normal systems.
@Repository does not magically fix every exception; it helps when persistence exceptions escape the repository boundary and Spring’s translation post-processor can intercept them.@Service does not mean transactional by itself. You still need @Transactional when you want a transaction boundary.@Component and @Service is redundant; use the most specific stereotype that matches the role.@PaymentService built on @Service, you keep the same scanning behavior and improve readability.Imagine a checkout service in an e-commerce app. The team has a PricingComponent that formats prices, an OrderService that calculates totals and applies coupons, and an OrderRepository that writes orders to the database. This separation makes testing much easier: the business rules can be tested without a database, and the repository can be tested with a smaller persistence-focused test.
Now the bug story: a developer put the database write logic directly inside @Service code and used low-level JPA calls there. When duplicate order IDs started appearing during a flash sale, the app threw a raw ConstraintViolationException and returned a generic 500 to users. Logs were noisy, retries were triggered, and some customers saw failed checkouts even though the payment had already been authorized. After the team moved the persistence code into @Repository, kept business validation in @Service, and used the formatter as a plain @Component, the duplicate-key problem became a clean Spring data-access exception that the API layer could map to a sensible client error.
What went wrong: the team blurred layers. The result was harder testing, poor exception mapping, and unclear ownership of business rules versus database rules.
package com.example.demo;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityManager;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean
HibernatePropertiesCustomizer hibernateCustomizer() {
// This keeps the demo runnable without an external schema file.
return props -> props.put("hibernate.hbm2ddl.auto", "create-drop");
}
@Bean
PersistenceExceptionTranslationPostProcessor exceptionTranslationPostProcessor() {
// This is what lets @Repository turn persistence errors into Spring's DataAccessException types.
return new PersistenceExceptionTranslationPostProcessor();
}
@Bean
CommandLineRunner demo(RegistrationService registrationService) {
return args -> {
System.out.println("First registration succeeds");
AppUser saved = registrationService.register("alice@example.com", "alice");
System.out.println("Saved user id=" + saved.getId() + ", email=" + saved.getEmail());
System.out.println();
System.out.println("Duplicate email shows repository exception translation");
try {
registrationService.register("alice@example.com", "alice clone");
} catch (DataIntegrityViolationException ex) {
System.out.println("Caught translated Spring exception: " + ex.getClass().getSimpleName());
System.out.println("Most specific cause: " + ex.getMostSpecificCause().getClass().getSimpleName());
}
System.out.println();
System.out.println("Invalid input is handled in the service layer");
try {
registrationService.register(" ", "bad");
} catch (IllegalArgumentException ex) {
System.out.println("Service validation failed: " + ex.getMessage());
}
};
}
}
@Component
class NameFormatter {
// Generic reusable helper: not business logic, not persistence.
String format(String rawName) {
if (rawName == null || rawName.isBlank()) {
return "Guest";
}
String cleaned = rawName.trim();
return cleaned.substring(0, 1).toUpperCase() + cleaned.substring(1);
}
}
@Service
class RegistrationService {
private final AppUserRepository repository;
private final NameFormatter formatter;
RegistrationService(AppUserRepository repository, NameFormatter formatter) {
this.repository = repository;
this.formatter = formatter;
}
@Transactional
AppUser register(String email, String rawName) {
// Business rule belongs here so controllers stay thin and repositories stay focused.
if (email == null || email.isBlank()) {
throw new IllegalArgumentException("Email must not be blank");
}
AppUser user = new AppUser();
user.setEmail(email.trim().toLowerCase());
user.setDisplayName(formatter.format(rawName));
return repository.save(user);
}
}
@Repository
class AppUserRepository {
@PersistenceContext
private EntityManager entityManager;
AppUser save(AppUser user) {
// Force the insert now so the duplicate-key failure appears inside this method.
entityManager.persist(user);
entityManager.flush();
return user;
}
}
@Entity
@Table(name = "app_users", uniqueConstraints = @UniqueConstraint(name = "uk_app_users_email", columnNames = "email"))
class AppUser {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String email;
@Column(nullable = false)
private String displayName;
protected AppUser() {
// JPA needs a no-arg constructor.
}
Long getId() {
return id;
}
String getEmail() {
return email;
}
void setEmail(String email) {
this.email = email;
}
String getDisplayName() {
return displayName;
}
void setDisplayName(String displayName) {
this.displayName = displayName;
}
}Follow-up & Tricky Questions:
@Component? @Repository.@Service required for @Transactional? @Transactional works on any Spring bean, not only on services. Teams usually put it on services because that is where business operations naturally start and end.@Component or one of its specializations to keep intent clear across a large codebase.@Component and @Service? @Service says this bean belongs to the business layer, which helps humans and tooling.@Repository translate every exception automatically? @Service, does Spring create the implementation? Common Mistakes:
@Service has special runtime behavior. Correction: It is mostly a semantic specialization of @Component.@Repository for any random helper that happens to call a database once. Correction: Use it for the persistence boundary, where exception translation and data access concerns belong.Memory Hook: Think of a company building: @Component is any office worker, @Service is the team that solves customer problems, and @Repository is the secure archive that stores records and handles storage errors.
Cheat Sheet:
@Component = generic Spring bean.@Service = business layer stereotype.@Repository = persistence layer stereotype.@Repository adds exception translation.@Service is mainly for clarity and design, not extra runtime magic.Practice Tasks:
@Component to @Service; explain why the name is more accurate.@Repository changes the exception type.