Stereotype Annotations
Stereotype annotations are the markers that tell Spring’s container which classes to manage as beans. They are all built on @Component, so they behave identically during component scanning, but each one communicates a different architectural role. Using the right stereotype makes your code self-documenting and, in one case, adds real behavior.
What stereotypes do
When Spring boots up it performs component scanning: it walks the classpath looking for classes annotated with a stereotype and registers each as a bean in the ApplicationContext. From then on the container can autowire those beans wherever they are needed, as described in Dependency Injection & IoC.
A “stereotype” is simply a conventional role label. @Service does not give a class superpowers — it is @Component with a more specific name. The value is intent: a reader (and Spring tooling, and AOP pointcuts) can immediately see what layer a class belongs to.
The annotations at a glance
| Annotation | Use it for | Adds behavior? |
|---|---|---|
@Component | Any generic Spring-managed bean | No |
@Service | Business / domain logic | No (semantic only) |
@Repository | Data-access objects (DAOs) | Yes — exception translation |
@Controller | MVC web controllers (return views) | Routing via @RequestMapping |
@RestController | REST endpoints (return body) | @Controller + @ResponseBody |
@Configuration | Java-based bean definitions | Bean method proxying |
Tip: When in doubt between
@Componentand@Service, prefer@Servicefor anything holding business logic. Reserve bare@Componentfor utility/support beans that do not fit another layer.
@Component — the base
@Component is the generic stereotype. Every other annotation here is meta-annotated with it.
@Component
public class GreetingFormatter {
public String format(String name) {
return "Hello, " + name + "!";
}
}
@Service — business logic
@Service marks the layer that orchestrates business rules and coordinates repositories. Functionally it is identical to @Component, but it documents the role clearly.
@Service
public class OrderService {
private final OrderRepository orderRepository;
public OrderService(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
public Order place(Order order) {
// business rules live here
return orderRepository.save(order);
}
}
@Repository — data access plus exception translation
@Repository marks data-access components, but unlike the others it is not purely semantic. Spring wraps @Repository beans with a post-processor that performs persistence exception translation: vendor-specific exceptions (such as a JPA PersistenceException or a raw JDBC SQLException) are converted into Spring’s consistent DataAccessException hierarchy.
@Repository
public class JdbcCustomerRepository {
private final JdbcTemplate jdbcTemplate;
public JdbcCustomerRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public int countAll() {
// a SQLException here surfaces as a Spring DataAccessException
return jdbcTemplate.queryForObject("SELECT COUNT(*) FROM customer", Integer.class);
}
}
Note: Spring Data JPA repository interfaces get exception translation automatically, so you rarely annotate those with
@Repositoryyourself. The annotation matters most for hand-written DAOs usingJdbcTemplateor anEntityManager.
@Controller and @RestController — web endpoints
@Controller is the web-layer stereotype. In classic MVC its methods return view names. @RestController is a convenience annotation that combines @Controller with @ResponseBody, so every method’s return value is serialized straight into the HTTP response body — the standard choice for JSON APIs.
@RestController
@RequestMapping("/api/orders")
public class OrderController {
private final OrderService orderService;
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
@PostMapping
public Order create(@RequestBody Order order) {
return orderService.place(order);
}
}
Output:
{
"id": 42,
"item": "Keyboard",
"quantity": 2
}
@Configuration — declaring beans in code
@Configuration is a specialized @Component used for Java-based configuration. Its @Bean methods produce beans for types you cannot (or should not) annotate directly. Configuration classes have proxying semantics that ordinary components do not — see @Configuration & @Bean for the full story.
@Configuration
public class HttpConfig {
@Bean
public RestClient restClient() {
return RestClient.create();
}
}
Component scanning and @ComponentScan
Stereotypes only become beans if a scan reaches them. In a typical Spring Boot app the @SpringBootApplication annotation includes @ComponentScan, which scans the package of the main class and all sub-packages.
@SpringBootApplication // = @Configuration + @EnableAutoConfiguration + @ComponentScan
public class ShopApplication {
public static void main(String[] args) {
SpringApplication.run(ShopApplication.class, args);
}
}
This is why package layout matters: keep your code under the main application package so the default scan finds it. To scan elsewhere, declare the packages explicitly:
@SpringBootApplication
@ComponentScan(basePackages = {"com.shop", "com.shared.payments"})
public class ShopApplication { }
You can also narrow a scan with filters:
@ComponentScan(
basePackages = "com.shop",
excludeFilters = @ComponentScan.Filter(
type = FilterType.ANNOTATION,
classes = Deprecated.class))
public class ScanConfig { }
Warning: If a bean “cannot be found” or autowiring fails with
NoSuchBeanDefinitionException, the most common cause is a class sitting outside the scanned package tree. Move it under the main package or add it tobasePackages.
Custom stereotypes
Because stereotypes are just meta-annotations, you can compose your own to capture a project convention in one place:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Service
public @interface DomainService { }
A class annotated @DomainService is scanned exactly like a @Service. See Annotations for how meta-annotations work in plain Java.
Best Practices
- Choose the most specific stereotype for the class’s role; reserve
@Componentfor true utilities. - Use
@Repositoryfor hand-written DAOs to gain exception translation; Spring Data interfaces get it for free. - Prefer
@RestControllerfor JSON APIs and plain@Controlleronly when returning views. - Keep classes inside the main application package so the default
@ComponentScanfinds them. - Use
excludeFilters/includeFiltersinstead of widening scans across unrelated packages.