@Component
Marks a class for component scanning so Spring creates one instance of it, injects its dependencies and makes it available to other beans.
@Component | @Component("beanName")- Added in Spring Boot Spring Framework 2.5
Parameters
| Parameter | Type | Description |
|---|---|---|
$valueoptional | String= "" | Explicit bean name. When empty, the name is the simple class name with a lower-case first letter. |
Return value
n/aRegisters the annotated class as a bean when component scanning finds it. By default one shared (singleton) instance is created at startup.
Errors
Startup fails with ConflictingBeanDefinitionException if two scanned classes end up with the same bean name.
@Component is the plain way to say "Spring, create and manage this class". When component scanning (switched on by @SpringBootApplication) finds an annotated class, it registers a bean definition. At startup Spring calls the constructor, passing in the other beans it asks for, and keeps that one instance for the whole run of the application.
@Service, @Repository, @Controller, @RestController and @Configuration are all meta-annotated with @Component. They are found the same way; they differ in intent and, for @Repository and the controllers, in extra behaviour. Use @Component for everything that is not one of those roles: a slug generator, a clock wrapper, a mapper, an event listener, a scheduler.
Use constructor injection
Give the class one constructor that takes its collaborators. Spring uses it without any @Autowired. Fields can then be final, the class can be built in a unit test with new, and a missing dependency fails at startup instead of throwing a NullPointerException later.
Singletons are shared
One instance serves every request on every thread. A mutable field such as a HashMap used as a per-request scratchpad is a data race in production that never shows up in a single-user test. Keep components stateless, or use thread-safe structures for state that really is shared, such as a cache.
Bean names
The default name is the class name with a lower-case first letter, so PriceFeed becomes priceFeed. Two classes with the same simple name in different packages collide and startup fails. Give one of them an explicit name with @Component("legacyPriceFeed"), or rename the class.
When not to use it
You cannot put @Component on a class you do not own, such as a client from a third-party library. Declare it with a @Bean method in a @Configuration class instead. Also avoid it on classes that need different settings per use: a @Bean method can build two configured instances of the same type, scanning cannot.
Annotating a class does nothing if the scan never reaches it. If a component seems to be ignored, check its package first, then check that nothing calls new on it: an instance you construct yourself gets no injection and none of Spring's proxies.
Example
import java.text.Normalizer;
import java.util.Locale;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
// A small helper with no business role: a plain @Component.
class SlugGenerator {
String slugFor(String title) {
String ascii = Normalizer.normalize(title, Normalizer.Form.NFD)
.replaceAll("\\p{M}", "");
return ascii.toLowerCase(Locale.ROOT)
.replaceAll("[^a-z0-9]+", "-")
.replaceAll("(^-|-$)", "");
}
}
// The service receives the component through its constructor.
public class ArticleService {
private final SlugGenerator slugs;
public ArticleService(SlugGenerator slugs) {
this.slugs = slugs;
}
public String publish(String title) {
return "/articles/" + slugs.slugFor(title);
}
}When to use it
- Registering a mapper or slug generator that several services share
- Building a plug-in style set of notification channels injected as a List
- Defining a project-wide @UseCase stereotype that is still picked up by scanning
Frequently asked questions
What is the difference between @Component and @Service?
Technically nothing: @Service is meta-annotated with @Component and is scanned the same way. The difference is intent. Use @Service for classes that hold business logic and @Component for supporting pieces that have no specific role.
Do I need @Autowired on the constructor?
No. When a class has a single constructor Spring uses it automatically. @Autowired is only needed to pick one when there are several constructors.
More examples
Collecting every implementation of an interface
Adding a new channel is now one new @Component class; Notifier does not change. Injecting a List works because no single bean is being chosen, so there is no ambiguity.
import java.util.List;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
interface NotificationChannel {
boolean supports(String type);
void send(String recipient, String message);
}
@Component
class EmailChannel implements NotificationChannel {
public boolean supports(String type) { return "email".equals(type); }
public void send(String recipient, String message) { /* send via SMTP */ }
}
@Component
class SmsChannel implements NotificationChannel {
public boolean supports(String type) { return "sms".equals(type); }
public void send(String recipient, String message) { /* call the SMS provider */ }
}
@Service
public class Notifier {
private final List<NotificationChannel> channels;
// Spring injects every NotificationChannel bean it knows about.
public Notifier(List<NotificationChannel> channels) {
this.channels = channels;
}
public void notify(String type, String recipient, String message) {
channels.stream()
.filter(c -> c.supports(type))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("No channel for " + type))
.send(recipient, message);
}
}Giving a component an explicit name
With two beans of the same type, a single PriceFeed parameter would fail startup as ambiguous. Explicit names give the qualifier something stable to point at, even if the classes are renamed later.
import java.math.BigDecimal;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
interface PriceFeed {
BigDecimal priceOf(String sku);
}
@Component("legacyPriceFeed")
class CsvPriceFeed implements PriceFeed {
public BigDecimal priceOf(String sku) { return new BigDecimal("9.99"); }
}
@Component("livePriceFeed")
class HttpPriceFeed implements PriceFeed {
public BigDecimal priceOf(String sku) { return new BigDecimal("10.49"); }
}
@Service
public class QuoteService {
private final PriceFeed feed;
public QuoteService(@Qualifier("livePriceFeed") PriceFeed feed) {
this.feed = feed;
}
public BigDecimal quote(String sku, int quantity) {
return feed.priceOf(sku).multiply(BigDecimal.valueOf(quantity));
}
}A custom stereotype annotation
Meta-annotation is how @Service and @Repository work too. A project-specific stereotype documents the architecture and gives architecture tests something to check, without changing how the bean is created.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.stereotype.Component;
// Classes marked @UseCase are picked up by component scanning
// because the annotation itself carries @Component.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface UseCase {
}
@UseCase
class CancelOrder {
void cancel(long orderId) {
// business logic for cancelling an order
}
}
Discussion