Lesson 3 · Spring Core

Component Scanning & Autowiring

The other way to define beans — annotate your own classes and let Spring find and wire them.

Where this fits Lesson 2 was Java config (you write @Bean methods). This is the flip side: put a stereotype annotation on your class, and component scanning turns it into a bean automatically. Then @Autowired wires the dependencies. Expect several questions on stereotypes, how @Autowired resolves, and @PostConstruct/@PreDestroy.

Stereotype annotations

A stereotype is a specialised @Component. All of them are meta-annotated with @Component, so component scanning detects them all identically — the different names carry intent (and sometimes extra behaviour):

AnnotationMarksExtra behaviour
@ComponentAny Spring-managed component
@ServiceA service / business-logic beanNone (semantic only)
@RepositoryA persistence / DAO beanException translation — vendor SQLExceptions become Spring DataAccessExceptions
@ControllerA web MVC controllerHandler mapping (Lesson 11)
@RestControllerA REST controller= @Controller + @ResponseBody
Two facts the exam checks (1) @Repository is the only stereotype that adds real behaviour — persistence exception translation. (2) @Configuration is also a @Component, which is why component scanning finds your config classes.

Turning it on: @ComponentScan

@Configuration
@ComponentScan("com.example.app")   // scan this package + sub-packages
public class AppConfig { }

// elsewhere — this class becomes a bean automatically
@Service
public class TransferService {
  private final AccountRepository repo;
  // single constructor → @Autowired is optional (Spring 4.3+)
  public TransferService(AccountRepository repo) { this.repo = repo; }
}

With no argument, @ComponentScan scans the package of the annotated class and downward — which is why @SpringBootApplication (it includes @ComponentScan) sits in a top-level package.

How @Autowired resolves a dependency

This is the mental model to memorise — resolution happens in order:

  1. By type. Spring looks for a bean assignable to the required type.
  2. If exactly one match → inject it. Done.
  3. If several match → narrow by @Qualifier → then @Primary → then by bean name matching the field/parameter name.
  4. If still ambiguousNoUniqueBeanDefinitionException. If none and it's required → NoSuchBeanDefinitionException.
Exam trap: "injecting by type" Two beans of the same type and no @Qualifier/@Primary → the context fails to start with NoUniqueBeanDefinitionException. The fixes: mark one @Primary, or select with @Qualifier("beanName") at the injection point, or name the parameter to match a bean id.

Disambiguation cheat-sheet

ToolGoes whereEffect
@PrimaryOn a bean definitionMakes it the default winner when multiple match
@Qualifier("id")At the injection pointExplicitly picks that bean — beats @Primary
Parameter/field nameInjection pointFalls back to matching a bean whose id equals the name

Where @Autowired can go

Make a dependency optional with @Autowired(required = false), Optional<T>, or @Nullable. Inject all beans of a type with a List<T> or Map<String,T>.

@PostConstruct and @PreDestroy

These JSR-250 annotations (javax.annotation.*) hook the bean's lifecycle without coupling your class to a Spring interface:

@Service
public class Cache {
  @PostConstruct            // runs AFTER dependencies are injected, before use
  void warmUp() { ... }

  @PreDestroy               // runs before the bean is destroyed
  void flush() { ... }
}
Initialization order (memorise) Constructor → dependencies injected → @PostConstructInitializingBean.afterPropertiesSet() → custom init-method.
Destruction mirrors it: @PreDestroyDisposableBean.destroy() → custom destroy-method.

Best practice — which style when?

Primary source — read this
Spring Framework 5.3 — Core: Annotation-based Container Configuration

Read "Using @Autowired", "Fine-tuning… with @Primary/@Qualifier", and "@PostConstruct and @PreDestroy". Mirrors this lesson exactly.

Check yourself

The @Autowired resolution-order question and the multiple-matching-beans ambiguity trap are the high-value ones. Options shuffle on every load.

Then drill the book Container section: Q6, Q18, Q34–35, Q40 (@Value, choosing which bean to inject, stereotypes, @Autowired placement, what component scanning detects). Re-run the Container Drill — the stereotype, component-scan and "which bean to inject" questions should now feel easy.
I'm your teacher — ask me anything. If the @Autowired resolution order or the @PostConstruct timing is fuzzy, ask me to trace a concrete example. Say "continue" for Lesson 4 — Bean Scopes.
← Lesson 2 · Java Configuration Lesson 4 · Bean Scopes → (coming next)