🌱 Spring, Reflection & @Autowired — Cheatsheet¶
Mental model¶
Spring uses reflection to discover, instantiate, and connect your beans at runtime. It’s not doing anything “mystical” — just a highly organized, optimized use of Java’s reflection and classloader system.
🧭 The Big Picture¶
At startup, Spring Boot performs this sequence:
- Scans your classpath for annotated classes.
- Loads those
.classdefinitions into memory via the JVM’s ClassLoader. - Creates
Class<?>objects in metaspace. -
Uses reflection to:
-
instantiate beans (
newInstance()) - read annotations (
@Component,@Autowired,@Configuration, etc.) - set fields or call methods to wire dependencies
🪞 Step-by-step breakdown¶
1️⃣ Classpath scanning¶
Spring starts with @SpringBootApplication → triggers component scanning.
At runtime:
- Spring’s scanner walks through your classpath entries (JARs, folders).
-
It finds classes annotated with:
-
@Component @Service@Repository@Controller- It loads those
.classfiles and keeps metadata in memory.
No beans are created yet — only metadata is registered.
2️⃣ Bean instantiation (reflection)¶
Spring now instantiates each discovered bean reflectively.
Class<?> beanClass = PaymentService.class;
Object instance = beanClass.getDeclaredConstructor().newInstance();
Then it wraps it in a BeanDefinition — a metadata container that stores the class type, scope, and dependencies.
This is done without you writing any new keyword.
That’s reflection in action.
3️⃣ Dependency resolution (@Autowired)¶
When Spring encounters a field like:
it uses reflection to perform field injection:
Field f = PaymentService.class.getDeclaredField("auditService");
f.setAccessible(true);
f.set(paymentServiceInstance, auditServiceInstance);
That’s literally what happens behind the scenes.
4️⃣ Other injection methods¶
| Type | Example | Reflection action |
|---|---|---|
| Field injection | @Autowired private Repo repo; |
Field.setAccessible(true); field.set(bean, value); |
| Constructor injection | @Autowired public Service(Repo repo) |
Read constructor metadata → invoke with arguments |
| Setter injection | @Autowired public void setRepo(Repo r) |
Call method reflectively with dependency |
All use reflection; only the injection point changes.
5️⃣ @Configuration and @Bean¶
When you define beans in config classes:
@Configuration
public class AppConfig {
@Bean
public UserService userService() {
return new UserService();
}
}
Spring creates an instance of AppConfig reflectively, then uses reflection again to call the @Bean method and register its return value as another bean.
🧩 Example — Full Reflection Cycle in Spring¶
@Service
public class PaymentService {
@Autowired
private AuditService audit;
}
@Service
public class AuditService {}
At runtime:
- Spring scans → finds
PaymentServiceandAuditService. - For each, it loads the class via ClassLoader.
- Reflection creates bean instances.
- Finds
@Autowired→ retrieves the matching dependency. - Uses
Field.setAccessible(true)→ injects the dependency. - The fully-wired bean enters the ApplicationContext.
After that, all beans behave like normal Java objects — reflection is no longer involved in normal execution.
🧠 Where Class<?> fits in¶
Spring keeps a Class<?> reference for every bean definition:
This allows it to:
- read annotations dynamically
- call constructors or methods reflectively
- create new instances via the
Class<?>handle
That’s why Class<?> is the “entry ticket” for all reflective operations in the Spring container.
⚙️ Internals peek — pseudo code¶
for (Class<?> clazz : scannedClasses) {
if (clazz.isAnnotationPresent(Component.class)) {
Object bean = clazz.getDeclaredConstructor().newInstance();
beans.put(clazz.getSimpleName(), bean);
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(Autowired.class)) {
Object dependency = findMatchingBean(field.getType());
field.setAccessible(true);
field.set(bean, dependency);
}
}
}
}
Simplified, but this captures the essence of what Spring’s IoC container does with reflection.
🧩 ClassLoader in Spring Boot¶
Spring Boot uses a custom classloader called LaunchedURLClassLoader
to load nested JARs from inside your fat JAR (app.jar).
That’s how it can run with:
without unpacking — the custom loader reads .class bytes directly from the JAR.
🔍 Related annotations and reflection roles¶
| Annotation | Purpose | Reflection action |
|---|---|---|
@Component |
Marks class as Spring-managed bean | Discovered via reflection scan |
@Autowired |
Marks dependency to inject | Field or method reflection |
@Configuration |
Marks class providing @Bean methods |
Reflection to call config methods |
@Bean |
Defines bean factory method | Reflection invocation |
@Value |
Injects property value | Reads field or setter reflectively |
@PostConstruct |
Runs method after injection | Reflective method call |
🧩 Common pitfalls¶
| Problem | Root cause | Fix |
|---|---|---|
NoSuchBeanDefinitionException |
No matching bean type found for @Autowired |
Check package scanning / qualifiers |
BeanCurrentlyInCreationException |
Circular dependency between beans | Refactor to constructor or setter injection |
IllegalAccessException |
Private field without setAccessible(true) |
Spring handles this internally — but shows up in proxies if security manager blocks it |
ClassNotFoundException |
Bean class missing from classpath | Add dependency or correct package name |
⚡ Performance tricks¶
- Spring caches every reflective lookup (constructor, field, annotation).
- It never scans the classpath on every request — only once at startup.
- Once the ApplicationContext is built, runtime overhead is negligible.
You can confirm this in stack traces — reflection appears only during context bootstrap.
🧩 Reflection in proxies (bonus)¶
When you use @Transactional, @Async, or @Cacheable, Spring builds proxy objects around your beans using reflection and bytecode generation (via CGLIB or JDK proxies).
Those proxies override methods dynamically to insert logic like transactions or logging — again, pure runtime reflection & bytecode magic.
🧭 TL;DR Summary¶
| Concept | Description |
|---|---|
| Classpath scanning | Finds annotated classes |
| ClassLoader | Loads .class bytes into JVM |
| Reflection | Creates and wires bean objects |
@Autowired |
Dependency injection using reflection |
| ApplicationContext | Registry of fully-wired beans |
| Performance | Reflection used only at startup; cached after |
🧩 One-sentence anchor¶
Spring doesn’t break Java’s rules — it just uses reflection masterfully to discover, create, and connect your objects at runtime.