Core Concepts · Lesson 11 of 95
Inversion of Control
Learn Inversion of Control in Spring Boot: how the container creates and connects your objects, with a small library example and simple diagrams.
Walk into a busy restaurant. You do not go to the kitchen, light the stove and boil your own rice. You sit down, tell the waiter what you want, and the food arrives. The restaurant decides who cooks, when they cook and which pan they use. You only decide what you would like to eat.
Spring works the same way with your Java objects. Your classes stop building the things they need. They wait, and Spring brings those things to them. This idea has a name: Inversion of Control, or IoC.
In this guide you will see what IoC means, what problem it solves, how the Spring container does the work, and how to check it with a small library program.
What is Inversion of Control?
Normally you are in control of your program. Your class needs a helper, so it writes new Helper() and creates one. Your code decides which helper to use and when to make it.
With IoC, that control is turned upside down. Your class no longer creates its helpers. An outside manager creates them and hands them over. In Spring, that manager is called the container.
Think of a hotel. A guest does not go to the market for towels. The hotel keeps towels ready and sends them to the room. The guest is not in control of the towels any more. The hotel is.
Two names are worth knowing:
- IoC container. The Spring part that creates, stores and connects your objects. Its type is
ApplicationContext. - Bean. Any object that the container manages. You will meet beans properly in the next few guides.
Why is it used?
Look at what happens without IoC. Suppose a library desk needs a fine calculator, and the desk creates it on its own.
javapublic class LibraryDesk { private final FineCalculator calculator = new FineCalculator(); }
This looks harmless, but it causes trouble as the program grows.
- Tight coupling.
LibraryDeskis now glued to one exact class. To use a different calculator, say one for the festival season, you must open and editLibraryDesk. - Hard to test. You cannot give the desk a fake calculator, because it always builds the real one.
- Repeated setup. If the calculator itself needs a database or a settings object, every class that uses it must know how to build that too.
- Scattered lifetime. Nobody knows how many calculators exist or who should shut them down.
IoC fixes these problems. Each class says what it needs and does nothing else. One central place, the container, decides how objects are built, how many exist and when they are cleaned up. Changing one piece no longer means editing ten classes.
How it works
The container follows a simple routine every time your app starts.
text+--------------------------------+ | Your classes with annotations | | (@Component and friends) | +--------------------------------+ | v +--------------------------------+ | Container reads them and | | makes a list of beans | +--------------------------------+ | v +--------------------------------+ | Container creates each bean, | | helpers first | +--------------------------------+ | v +--------------------------------+ | Container passes helpers into | | the classes that need them | +--------------------------------+ | v +--------------------------------+ | App runs. Container closes | | beans when the app stops | +--------------------------------+
First, Spring reads the classes you marked, for example with @Component. It records what each class is and what it needs. Then it creates the objects. It is smart about order: if LibraryDesk needs FineCalculator, the calculator is built first. Finally it hands the ready calculator to the desk and starts your app. When the app stops, the container also tidies up the objects it created.
Here is the difference in who calls whom.
textWithout IoC: LibraryDesk --new--> Calculator With IoC: Container --makes--> Calculator Container --makes--> LibraryDesk Container --gives--> Calculator to LibraryDesk
Without IoC the desk reaches out and builds its own helper. With IoC the arrows start at the container. The desk never mentions how the calculator is made. This is why people say the control is "inverted".
Real-Life Example
Think about a school canteen that serves lunch. The canteen owner used to buy vegetables, hire a cook and arrange the plates every morning. Every change hurt: a new vegetable seller meant new phone numbers and new plans.
Now a catering company runs the canteen. The owner writes a short note: "I need rice, dal and one cook." The company delivers exactly that each morning. If the dal supplier changes, the owner never hears about it. The note stays the same.
The owner is your class. The note is the list of things it needs. The catering company is the Spring container.
Code Example
Let's build a small program for a town library. A LibraryDesk needs a FineCalculator to work out late fees. We will never write new for either of them. Spring will do it. Use the same pom.xml as the first CineGo example, changing only the groupId and artifactId.
textlibrary-desk/ └─ src/main/java/ └─ com/townlibrary/desk/ ├─ DeskApplication.java ├─ FineCalculator.java ├─ LibraryDesk.java └─ DeskRunner.java
File: DeskApplication.java in package com.townlibrary.desk
javapackage com.townlibrary.desk; import org.springframework.boot.Banner; import org.springframework.boot.SpringApplication; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DeskApplication { public static void main(String[] args) { SpringApplication app = new SpringApplication(DeskApplication.class); app.setWebApplicationType(WebApplicationType.NONE); app.setBannerMode(Banner.Mode.OFF); app.setLogStartupInfo(false); System.out.println("main: asking Spring to start"); app.run(args); System.out.println("main: finished"); } }
File: FineCalculator.java in package com.townlibrary.desk
javapackage com.townlibrary.desk; import org.springframework.stereotype.Component; @Component public class FineCalculator { public FineCalculator() { System.out.println("Spring created FineCalculator"); } public int fineFor(int daysLate) { return daysLate * 5; } }
File: LibraryDesk.java in package com.townlibrary.desk
javapackage com.townlibrary.desk; import org.springframework.stereotype.Component; @Component public class LibraryDesk { private final FineCalculator calculator; public LibraryDesk(FineCalculator calculator) { this.calculator = calculator; System.out.println("Spring created LibraryDesk"); } public String returnBook(String title, int daysLate) { return title + ": fine is Rs " + calculator.fineFor(daysLate); } }
File: DeskRunner.java in package com.townlibrary.desk
javapackage com.townlibrary.desk; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; @Component public class DeskRunner implements CommandLineRunner { private final LibraryDesk desk; public DeskRunner(LibraryDesk desk) { this.desk = desk; } @Override public void run(String... args) { System.out.println(desk.returnBook("Malgudi Days", 4)); } }
Build and run it:
bashmvn -q package java -jar target/desk-0.0.1-SNAPSHOT.jar
Output:
textmain: asking Spring to start Spring created FineCalculator Spring created LibraryDesk Malgudi Days: fine is Rs 20 main: finished
Look at the order. Spring built FineCalculator first, because the desk needs it. Only then did it build LibraryDesk. Nobody wrote that order in code. The container worked it out.
Code Explained
@Componenttells Spring, "please manage objects of this class". Each annotated class becomes a bean.LibraryDeskasks for aFineCalculatorin its constructor. It never says how to make one. That request is the whole contract.DeskRunnerimplementsCommandLineRunner. Spring calls itsrunmethod once the container is ready. It gets the desk the same way: through its constructor.- In
main, we switch off the web server and the banner. That keeps the console limited to our own lines. It also shows that IoC has nothing to do with the web. It works in any Spring program. - Nowhere do we write
new FineCalculator()ornew LibraryDesk(...). That is the inversion.
Common Mistakes
- Forgetting the annotation. A class without
@Component(or a similar annotation) is invisible to Spring. The container then says it cannot find a bean of that type. - Mixing both styles. Creating one helper with
newinside a bean, while the rest are injected, brings back the tight coupling IoC was meant to remove. - Thinking IoC means Spring only. IoC is a general idea. Spring is one popular way to use it.
Interview Questions
What is Inversion of Control?
Ans:It is a principle where a framework creates and connects objects instead of your code doing it. Your classes only declare what they need.
What is the Spring IoC container?
Ans:It is the part of Spring, represented by ApplicationContext, that creates beans, injects their dependencies and manages their life.
How are IoC and Dependency Injection related?
Ans:IoC is the principle. Dependency Injection is the technique Spring uses to apply it, by passing ready-made objects into the classes that need them.
Key Points to Remember
- With IoC, the container creates objects. Your classes do not use
newfor their helpers. - The container is the
ApplicationContext, and the objects it manages are called beans. - IoC reduces coupling, so you can change or fake one piece without editing many classes.
- The container works out the creation order for you: helpers first, then the classes that need them.
- Dependency Injection is how IoC is carried out.
Frequently Asked Questions
Is Inversion of Control the same as Dependency Injection?
Not exactly. Inversion of Control is the general idea of handing control to a framework. Dependency Injection is one specific way to do it, and it is the way Spring uses.
Can I use Spring without IoC?
Not in any useful way. The container is the heart of Spring. Almost everything else, such as web controllers and database access, is built on top of it.
Does IoC make my program slower?
The container does a little extra work when the app starts, because it must build and connect the beans. After that, the cost is tiny. Most beans are created only once and reused.
Who controls the objects when Spring runs the app?
The container does. It creates them, gives them to the classes that need them and closes them when the app ends. Your code just uses them.
Related Topics
- Dependency Injection: see how the container passes helpers into your classes.
- Spring Beans: learn what a bean is and how to find one.
- Component Scan: find out how Spring discovers your classes.
- Spring vs Spring Boot: compare the framework with the tool built on top.
Practice Problems
Try each problem on your own first. Both use the same pom.xml as the CineGo example; only change the groupId and artifactId. Both programs switch off the web server so the console shows only your lines.
Easy: Clinic Appointment Desk
Sunrise Clinic wants a small program. An AppointmentDesk must book a visit and print a reminder text made by a ReminderMaker. The desk must not create the maker with new. Let Spring do it. Booking "Meera" for "10:30" should print Reminder: Meera, visit at 10:30.
Show answerHide answer
File: ClinicApplication.java in package com.sunrise.clinic
javapackage com.sunrise.clinic; import org.springframework.boot.Banner; import org.springframework.boot.SpringApplication; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ClinicApplication { public static void main(String[] args) { SpringApplication app = new SpringApplication(ClinicApplication.class); app.setWebApplicationType(WebApplicationType.NONE); app.setBannerMode(Banner.Mode.OFF); app.setLogStartupInfo(false); app.run(args); } }
File: ReminderMaker.java in package com.sunrise.clinic
javapackage com.sunrise.clinic; import org.springframework.stereotype.Component; @Component public class ReminderMaker { public String make(String patient, String time) { return "Reminder: " + patient + ", visit at " + time; } }
File: AppointmentDesk.java in package com.sunrise.clinic
javapackage com.sunrise.clinic; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; @Component public class AppointmentDesk implements CommandLineRunner { private final ReminderMaker reminderMaker; public AppointmentDesk(ReminderMaker reminderMaker) { this.reminderMaker = reminderMaker; } @Override public void run(String... args) { System.out.println(reminderMaker.make("Meera", "10:30")); } }
Running the jar prints:
textReminder: Meera, visit at 10:30
Medium: Swap the Notification Sender
A hostel office sends notices through a NoticeSender interface. Today it uses NoticeBoardSender, which prints Notice board: <text>. Build a HostelOffice that depends only on the interface. Then show that changing the sender means editing one class, not the office. Make the office publish "Mess closed Sunday".
Show answerHide answer
File: HostelApplication.java in package com.campusnest.hostel
javapackage com.campusnest.hostel; import org.springframework.boot.Banner; import org.springframework.boot.SpringApplication; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class HostelApplication { public static void main(String[] args) { SpringApplication app = new SpringApplication(HostelApplication.class); app.setWebApplicationType(WebApplicationType.NONE); app.setBannerMode(Banner.Mode.OFF); app.setLogStartupInfo(false); app.run(args); } }
File: NoticeSender.java in package com.campusnest.hostel
javapackage com.campusnest.hostel; public interface NoticeSender { void send(String text); }
File: NoticeBoardSender.java in package com.campusnest.hostel
javapackage com.campusnest.hostel; import org.springframework.stereotype.Component; @Component public class NoticeBoardSender implements NoticeSender { @Override public void send(String text) { System.out.println("Notice board: " + text); } }
File: HostelOffice.java in package com.campusnest.hostel
javapackage com.campusnest.hostel; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; @Component public class HostelOffice implements CommandLineRunner { private final NoticeSender sender; public HostelOffice(NoticeSender sender) { this.sender = sender; } @Override public void run(String... args) { sender.send("Mess closed Sunday"); } }
Running the jar prints:
textNotice board: Mess closed Sunday