Why IoC
Normal Java does:
UserService userService = new UserService();
OrderService orderService = new OrderService(userService);
So user has to:
- Manually create each object
- Manually connect dependencies
- Handle object lifecycle on their own
- Deal with difficult testing and maintenance
It’s a lot to maintain π€―β
Therefore SpringBoot uses IoC
What is IoC
IoC (Inversion of Control) is one of the core ideas behind the Spring Framework. With IoC:
- Spring creates the objects (beans)
- Spring injects the dependencies
- Spring manages the lifecycle
- User simply declare what you need
The name of Inversion of Control means SpringBoot manage all of these ranther than user does
ApplicationContext
ApplicationContext is the implementation of IoC container
Spring publishes ContextRefreshedEvent to signal that the BeanFactory’s creation phase is complete.
ApplicationContext creates and manages a simple object called bean π«. When user annotates a class with @Service, @Component, @Controller or define a @Bean method, Spring turns those into beans.
Workflow of Bean Creation while SpringBoot starts
1. When the application launches, Spring Boot does:
SpringApplication.run("MyApp.class");
This triggers Spring to create an ApplicationContext instance, which will hold all beans
2. Spring scans your packages
Spring looks for classes annotated with:
@Component@Service@Repository@Controller/@RestController@Configuration- and any class discovered under component scan
This is called classpath scanning.
3. Spring registers BeanDefinitions
When Spring scans your application for components, it does not create objects immediately.
Instead, Spring creates metadata called a BeanDefinition.
4. Spring creates bean instances
After analyzing all definitions, Spring starts creating beans:
- It looks at constructor parameters
- Resolves dependencies
- Creates the object
- Stores it in the ApplicationContext
5. Spring injects dependencies
Next, Spring performs dependency wiring:
- If Bean A needs Bean B β B is injected
- If the bean uses fields with
@Autowiredβ reflection injects - If the bean uses constructor injection β resolved in the step 4
- If the bean uses setter injection β setter is called
Dependencies are now fully wired.
6. Spring calls lifecycle callbacks
If a bean implements:
InitializingBeanDisposableBean@PostConstruct@PreDestroy
Spring calls these hooks at the appropriate time.
7. The ApplicationContext is fully initialized
At this point:
- All beans are created
- All dependencies are resolved
- All configurations are applied
- Filters, providers, and security components are registered
- The application is ready to serve requests
Best Practise for CommandLineRunner/ ApplicationRunner
1. Use it to run one-off tasks
β Typical use cases include:
- Seeding initial data (e.g. default admin user, basic configuration)
- Verifying that external dependencies are reachable (e.g. third-party APIs, storage services)
- Performing lightweight warm-up tasks (e.g. pre-loading caches)
β TheyΒ should notΒ contain:
- Infinite loops or long-running background tasks, such as while(True)
- Core business workflows that are expected to run repeatedly
@Component
public class AdminUserInitializer implements CommandLineRunner {
private final UserService userService;
public AdminUserInitializer(UserService userService) {
this.userService = userService;
}
@Override
public void run(String... args) {
// create admin account if it does not exist
userService.createAdminUserIfMissing();
}
}
A runner should mainly act as aΒ trigger, not as a place to implement business logic.
2. Control where it runs (profiles / environments)
Spring Boot automatically loads configuration files based on theΒ active profile.
For example, if Spring Boot has:
src/main/resources/
βββ application.yml
βββ application-dev.yml
βββ application-prod.yml
application.yml
application-dev.yml β overrides same properties
And CommandLineRunner files know when they should execute by @Profile
@Component
@Profile({"dev", "local"})
public class DevAdminInitializer implements CommandLineRunner {
// ...
}
We can tell Spring Boot which environment we are in different ways:
spring:
profiles:
active: dev
Option B: JVM parameter (recommended for prod deploy)
java -jar app.jar –spring.profiles.active=prod
Option C: Environment variable
SPRING_PROFILES_ACTIVE=prod
3. Avoid hard-coding secrets
We can use @value annotation in the CommandLineRunner file
@Component
public class AdminInitializer implements CommandLineRunner {
@Value("${app.admin.username}")
private String username;
@Value("${app.admin.password}")
private String rawPassword;
@Override
public void run(String... args) {
userService.createAdminIfMissing(username, rawPassword);
}
}
and application.yml
app:
admin:
username: admin
password: ${APP_ADMIN_PASSWORD:changeme}
Leave a Reply