Why this matters
- Consistent stereotype usage makes the BookStore codebase navigable — any developer knows where to find controllers vs services.
@Repositoryenables automatic translation of persistence exceptions to Spring'sDataAccessExceptionhierarchy.- Mixing stereotypes incorrectly (business logic in a
@Controller) breaks layering and testability.
Arrows show @Autowired wiring — container injects at startup
The stereotype hierarchy
All stereotypes are meta-annotated with @Component:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface Service { }
Spring treats @Service, @Repository, @Controller, and @RestController identically for bean registration. The difference is semantic and enables specialised processing.
Stereotypes in BookStore
@Component— Generic bean; use for utilities, adapters, and infrastructure classes.@Service— Business logic layer;BookService,OrderService,PricingService.@Repository— Data access layer;BookRepositoryimplementations and Spring Data interfaces.@Controller— MVC controller returning view names (Thymeleaf pages).@RestController— API controller returning serialized bodies (@Controller+@ResponseBody).
@Service in BookStore
@Service
@Transactional(readOnly = true)
public class BookService {
private final BookRepository repository;
public BookService(BookRepository repository) {
this.repository = repository;
}
public List<Book> findByGenre(String genre) {
return repository.findByGenre(genre);
}
@Transactional
public Book create(CreateBookRequest request) {
Book book = new Book(request.title(), request.isbn(), request.price());
return repository.save(book);
}
}
@Service signals "business logic lives here" — no web or persistence concerns.
@Repository and Spring Data
Spring Data JPA repositories are interfaces annotated with @Repository:
@Repository
public interface BookRepository extends JpaRepository<Book, Long> {
List<Book> findByGenre(String genre);
Optional<Book> findByIsbn(String isbn);
}
Spring generates the implementation at runtime. The @Repository stereotype also enables PersistenceExceptionTranslationPostProcessor — JDBC exceptions become DataAccessException subclasses.
@RestController for APIs
@RestController
@RequestMapping("/api/books")
public class BookController {
private final BookService bookService;
public BookController(BookService bookService) {
this.bookService = bookService;
}
@GetMapping
public List<BookResponse> list() {
return bookService.findAll().stream()
.map(BookResponse::from)
.toList();
}
}
Controllers stay thin — delegate to services, map to DTOs, return HTTP responses.
@Component for everything else
@Component
public class IsbnValidator {
public boolean isValid(String isbn) {
return isbn != null && isbn.replace("-", "").matches("\\d{13}");
}
}
Use @Component when no more specific stereotype fits. Examples: validators, event listeners, scheduled task runners.
Component scanning boundaries
Stereotypes are discovered only under the scan root:
@SpringBootApplication // scans com.example.bookstore and below
public class BookStoreApplication { ... }
Add explicit scan paths when integrating a library:
@SpringBootApplication(scanBasePackages = {
"com.example.bookstore",
"com.example.shared.security"
})
Naming and multiple implementations
@Repository("jdbcBookRepository")
public class JdbcBookRepository implements BookRepository { ... }
@Repository("jpaBookRepository")
public class JpaBookRepository implements BookRepository { ... }
Bean names matter when using @Qualifier or @Resource(name = "...").
Stereotype vs @Bean
| Approach | When to use |
|---|---|
| @Service on class | Your own application classes |
| @Bean in @Configuration | Third-party classes, factory methods, conditional creation |
@Service on class
When to useYour own application classes@Bean in @Configuration
When to useThird-party classes, factory methods, conditional creation
@Configuration
public class HttpClientConfig {
@Bean
public RestClient pricingRestClient(BookStoreProperties props) {
return RestClient.builder()
.baseUrl(props.pricingServiceUrl())
.build();
}
}
You cannot add @Service to a library class — @Bean is the alternative.
Quick recall
Everything you need if you only revisit this box.
- All stereotypes (
@Service,@Repository,@Controller) are@Componentspecialisations. @Servicefor business logic,@Repositoryfor data access,@RestControllerfor JSON APIs.@Repositoryenables persistence exception translation toDataAccessException.- Use
@Componentfor utilities and infrastructure without a clearer stereotype. - Component scanning finds stereotypes under the
@SpringBootApplicationpackage tree. - Use
@Beanin@Configurationfor third-party objects you cannot annotate directly.
Test yourself
Answer these before moving on — recall is what makes it stick.