Week 3 Day 5 — How Spring Uses OOP
Goal
Today I want Spring as ordinary Java OOP plus a container, so Week 1 of the Spring book is not a new language.
Main questions:
- What is a bean in OOP terms?
- What is DI in OOP terms?
- What is a proxy in OOP terms?
- Why do annotations not replace types?
- What do I say in an interview that ties Java to Spring?
1. A bean is an object
@Service
public class OrderService {
private final OrderRepository orders;
public OrderService(OrderRepository orders) {
this.orders = orders;
}
public Order place(CreateOrderRequest request) {
return orders.save(Order.from(request));
}
}
Strip the annotations. This is a class, a constructor invariant, a private field, a public method. Encapsulation, composition, polymorphism if OrderRepository is an interface.
@Service does not change the bytecode of place. It is metadata for the container: “create one instance, inject collaborators, manage the lifecycle.”
Memory sentence:
A Spring bean is a Java object the container constructs and holds. The annotations are instructions, not the object.
2. Dependency Injection is constructor composition
Without Spring:
OrderRepository orders = new JpaOrderRepository(entityManager);
PaymentGateway payments = new StripeGateway(apiKey);
OrderService service = new OrderService(orders, payments);
With Spring, I delete the new graph. I keep the constructors. The container is a composition root.
That is Dependency Inversion from Day 4: the service still names the abstractions. Something else provides the instances.
IoC (“Inversion of Control”) is the broader idea: I no longer control construction. DI is the technique Spring uses.
3. A proxy is a substitute of the same type
When I put @Transactional on place, Spring typically does not rewrite OrderService. It wraps the object.
Caller
│ place()
▼
Proxy (same type as OrderService or its interface)
│ start transaction
│ delegate
▼
Real OrderService.place()
│ commit / rollback
OOP inventory:
- Substitution: callers depend on the type; they receive a proxy.
- Override / interface implementation: the proxy supplies the method.
- Composition: the proxy has-a target and delegates.
- Polymorphism: the extra behavior runs because the runtime type is the proxy.
Limits that come from Week 2:
privatemethods: not on the type API, not overridden, no proxy.finalclass/method: CGLIB cannot subclass/override.this.place()inside the class:thisis the target, not the proxy. Self-invocation skips the transaction.
Memory sentence:
A Spring proxy is a polymorphic wrapper: same type, extra behavior, then delegate.
4. Annotations are not types
@Autowired, @Service, @Transactional are not OOP. They are markers the container reads.
The OOP design still has to be right:
- If I depend on a concrete
StripeGatewayeverywhere,@Autowiredstill couples me. - If
OrderServicehas a public mutableListof current requests, the singleton still shares it. - If I have no constructor, I have no invariant — field injection will hide that.
Spring Boot auto-configuration creates beans (objects) when conditions match. It does not replace the need to understand classes.
5. The interview bridge
A 90-second answer when they ask “How does Spring relate to OOP?”:
Spring manages objects. A bean is an instance with a lifecycle. I design classes with constructor injection, which is composition and dependency inversion: the service depends on interfaces, the container supplies implementations. Cross-cutting things like transactions use proxies — wrappers of the same type that override methods and delegate. That is why private methods and self-invocation skip
@Transactional. I still needequals/hashCode, immutability, and encapsulation; the container does not fix those.
Then I shut up.
6. Where to go next
I now have enough Java to start the Spring book:
Keep speaking Java:
7. Common traps
Trap 1: “Spring is annotations.”
Annotations are metadata. The runtime is objects, proxies, and a graph.
Trap 2: “IoC means I do not use new anywhere.”
I still new value objects, DTOs, and entities. I do not new services and repositories that should be shared or swapped.
Trap 3: Magical @Transactional without the proxy story.
Trap 4: Treating the ApplicationContext as a service locator (getBean) from random classes.
That undoes Dependency Inversion. Ask for collaborators in the constructor.
Practice Questions and Answers
Question 1
What is a Spring bean in Java terms?
Answer:
An object whose construction, wiring, and lifecycle are managed by the container. The class is ordinary Java. The container decides how many instances and which constructor arguments.
Question 2
How is DI composition?
Answer:
The service has-a repository and has-a gateway. Those fields are set from the constructor. Spring is the code that calls that constructor with other beans. I could do the same new graph by hand in a main method.
Question 3
Why does @Transactional need a proxy?
Answer:
The extra start/commit behavior has to run around the method. A wrapper of the same type intercepts the call. That wrapper is a subclass or an interface proxy. The real object stays focused on business code.
Question 4
Why does this.save() skip the transaction?
Answer:
this is the target instance. The proxy is only on the reference the container injected into other beans. An internal call does not enter the wrapper, so no override runs, so no transaction advice.
Question 5
What should I still new in a Spring app?
Answer:
Values and DTOs: new Money(...), new CreateOrderRequest(...), entities in factories. I do not new @Service / @Repository types in application code. Tests may new a service with fakes.
Memory sentences
A bean is an object the container constructs and holds.
DI is constructor composition with a composition root.
A proxy is a polymorphic wrapper.
thisis not the proxy.
I
newvalues. The containernews services.
Next: Week 3 Review