🍃 Section 5 · Question #25

Difference between Component Service Repository Controller

In Spring, Stereotype Annotations are specialized markers used to designate classes as Spring-managed Beans within specific architectural layers of an enterprise application.


🟢 Junior Level

In Spring, Stereotype Annotations are specialized markers used to designate classes as Spring-managed Beans within specific architectural layers of an enterprise application.

All specialized stereotype annotations inherit directly from the foundational @Component meta-annotation.

                          [@Component] (Root Meta-Annotation)
                                │
        ┌───────────────────────┼───────────────────────┐
        ▼                       ▼                       ▼
   [@Service]             [@Repository]           [@Controller]
  Business Layer         Data Access (DAO)       Spring MVC Views
                                                        │
                                                        ▼
                                                [@RestController]
                                               REST APIs (JSON/XML)

Stereotypes Comparison Matrix

Annotation Architectural Layer Semantic Purpose Special Runtime Behavior
@Component Generic / Any General-purpose managed bean (utilities, helpers, clients) None (standard bean lifecycle)
@Service Service Layer Business logic, calculations, domain orchestration None (semantic marker for AOP pointcuts)
@Repository Persistence (DAO) Data access, database queries, and CRUD operations Yes: Automatic Exception Translation to Spring’s DataAccessException
@Controller Presentation Web controllers returning rendered HTML templates Yes: DispatcherServlet binding and template ViewResolver routing
@RestController REST Web Services HTTP API endpoints returning JSON/XML Yes: Automatic Serialization via @ResponseBody and HttpMessageConverter

🟡 Middle Level

Meta-Annotation Architecture

In the Spring Framework source code, @Service, @Repository, and @Controller are directly meta-annotated with @Component:

// Spring Framework Source Code:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // <-- Declares this annotation as an extension of @Component!
public @interface Service { ... }

Because of this meta-annotation hierarchy, Spring’s @ComponentScan scanner discovers all classes annotated with @Service, @Repository, or @Controller automatically, treating them as valid @Component beans.

@Controller vs @RestController

The distinction between @Controller and @RestController dictates how HTTP responses are processed:

  1. @Controller (Traditional Server-Side MVC):
    • Returns a String representing a template view name.
    • Spring’s ViewResolver (Thymeleaf, FreeMarker, JSP) locates the template on disk and renders HTML to the client:
      @Controller
      public class WebPageController {
        @GetMapping("/dashboard")
        public String showDashboard(Model model) {
            model.addAttribute("username", "Alex");
            return "dashboard"; // Renders src/main/resources/templates/dashboard.html
        }
      }
      
  2. @RestController (Headless RESTful APIs):
    • Composed of @Controller + @ResponseBody.
    • The returned object is never passed to a ViewResolver. Instead, it is serialized directly into the HTTP response body (defaulting to JSON via Jackson’s MappingJackson2HttpMessageConverter):
      @RestController // = @Controller + @ResponseBody on every method
      public class UserApiController {
        @GetMapping("/api/users/{id}")
        public UserDto getUser(@PathVariable Long id) {
            return new UserDto(id, "Alex"); // Serialized directly to: {"id": 1, "name": "Alex"}
        }
      }
      

🔴 Senior Level

The Key Functional Difference: Exception Translation in @Repository

Among all stereotype annotations, @Repository is the only annotation that introduces active runtime behavioral changes.

The Problem: Vendor Lock-In via Exceptions

Different database drivers and ORM frameworks throw distinct, vendor-specific low-level exceptions:

  • JDBC throws checked java.sql.SQLException.
  • Hibernate throws org.hibernate.HibernateException.
  • JPA throws jakarta.persistence.PersistenceException.

If the service layer caught these raw exceptions directly, business logic would be tightly coupled to a specific database implementation.

The PersistenceExceptionTranslationPostProcessor Engine:

  1. When Spring detects @Repository on a bean, the PersistenceExceptionTranslationPostProcessor (a BeanPostProcessor) wraps the DAO bean in a dynamic AOP proxy.
  2. The proxy applies a PersistenceExceptionTranslationAdvisor.
  3. When database calls fail, raw vendor exceptions are intercepted and converted into Spring’s unified, unchecked (RuntimeException) hierarchy: DataAccessException.
Native Exception (java.sql.SQLException / HibernateException / PersistenceException)
                                    │
                                    ▼ [Intercepted by PersistenceExceptionTranslator]
                       org.springframework.dao.DataAccessException
                                    │
         ┌──────────────────────────┼──────────────────────────┐
         ▼                          ▼                          ▼
DuplicateKeyException   DataIntegrityViolationException  CannotAcquireLockException
(Unique constraint)      (Foreign key / NOT NULL)         (Database deadlocks)

[!WARNING] If you replace @Repository with @Component or @Service on a DAO class, the bean will still be registered and injected normally, but automatic exception translation will be completely disabled. Your service layer will be forced to catch raw vendor exceptions.

Layer-Targeted AspectJ Pointcuts

Using specific stereotype annotations enables clean, decoupled architecture rules via Aspect-Oriented Programming (AOP):

@Aspect
@Component
public class LayerMonitoringAspect {

    // Matches all public methods in classes annotated with @Service:
    @Around("@within(org.springframework.stereotype.Service)")
    public Object profileServices(ProceedingJoinPoint pjp) throws Throwable {
        return pjp.proceed();
    }

    // Matches all database calls in classes annotated with @Repository:
    @Around("@within(org.springframework.stereotype.Repository)")
    public Object profileDatabaseAccess(ProceedingJoinPoint pjp) throws Throwable {
        return pjp.proceed();
    }
}

4 Tricky Questions

1. Which stereotype annotation introduces actual runtime functional behavior rather than just semantic metadata?

Answer: @Repository.

While @Service and @Component serve exclusively as semantic markers and component scanning targets, @Repository activates the PersistenceExceptionTranslationPostProcessor.

This BeanPostProcessor automatically wraps the repository bean in a dynamic AOP proxy that intercepts raw, low-level database exceptions (such as JDBC’s SQLException or Hibernate’s HibernateException) and translates them into Spring’s database-agnostic, unchecked DataAccessException hierarchy.

2. What happens if a developer replaces @Service with @Component on a business logic class?

Answer: From a basic dependency injection and bean lifecycle perspective, nothing changes: Spring will still discover the class via @ComponentScan, register it in the ApplicationContext, and inject it into callers.

However, replacing @Service with @Component:

  1. Degrades code readability and architectural maintainability by failing to indicate domain layer boundaries.
  2. Disables AOP pointcut rules that target the service layer specifically (e.g. @within(org.springframework.stereotype.Service)).
  3. Breaks architectural enforcement tools (such as ArchUnit) that validate dependency directions between presentation, service, and persistence layers.

3. What is the fundamental architectural difference between @Controller and @RestController?

Answer:

  • @Controller is intended for server-side template rendering in Spring MVC. The return value of a controller method is treated as a logical view name, which is forwarded to a configured ViewResolver (e.g. Thymeleaf, JSP) to render an HTML response.
  • @RestController is a convenience composite annotation consisting of @Controller + @ResponseBody. It instructs Spring’s RequestResponseBodyMethodProcessor to bypass the ViewResolver entirely. The returned domain object is serialized directly into the HTTP response body using an appropriate HttpMessageConverter (defaulting to Jackson for JSON).

4. Why did Spring design the DataAccessException hierarchy to be unchecked (RuntimeException) instead of checked?

Answer: Checked exceptions (like JDBC’s SQLException) force developers to clutter business methods with mandatory try-catch blocks or throws clauses, even though the vast majority of database errors (deadlocks, constraint violations, lost connections) are fatal and unrecoverable at the call site.

By making DataAccessException unchecked (extending RuntimeException), Spring adheres to modern exception handling principles:

  1. Business logic remains clean and decoupled from low-level infrastructure failures.
  2. Unrecoverable database exceptions bubble up automatically to centralized global handlers (such as @ControllerAdvice or transaction managers) for consistent error logging and HTTP 500 mapping.

🎯 Interview Cheat Sheet

30-Second Elevator Pitch

Spring provides four primary stereotype annotations that inherit from @Component:

  • @Component: General-purpose managed bean.
  • @Service: Semantic marker for domain business logic (useful for transaction anchors and AOP pointcuts).
  • @Repository: Persistence layer component. Introduces active runtime behavior: wraps the DAO in an AOP proxy via PersistenceExceptionTranslationPostProcessor to convert vendor SQL/Hibernate exceptions into Spring’s unchecked DataAccessException hierarchy.
  • @Controller: Spring MVC controller returning HTML view names.
  • @RestController: Composite @Controller + @ResponseBody returning JSON/XML directly to HTTP clients.

Stereotype Hierarchy & Capabilities

Stereotype Extends @Component? Special Runtime Proxy Applied? Intended Output
@Component Root No Generic Bean
@Service Yes No Business Result
@Repository Yes Yes (Exception Translation) Database Entities / DTOs
@Controller Yes Yes (DispatcherServlet View Binding) HTML View Name
@RestController Yes Yes (HttpMessageConverter Serialization) JSON / XML Payload

Red Flags (DO NOT Say)

  • ❌ “The @Service annotation automatically makes every method transactional.” (Transactions require explicit @Transactional annotations).
  • ❌ “All four stereotypes are 100% functionally identical under the hood.” (@Repository activates dynamic exception translation).
  • ❌ “@RestController cannot return custom HTTP status codes.” (Status codes are fully customizable via ResponseEntity<T> or @ResponseStatus).
  • ❌ “You must only use @Component because other stereotypes are deprecated.” (Using specific stereotypes is a mandatory architectural standard in Spring).