IOC (Inversion of control)- It’s a concept where we invert the control of creating object from ‘new’ keyword to configuration files (XML/Annotations).
Let’s write a program without IOC and then with IOC and then we will talk about why we use IOC, ready?
Dependency Injection(DI)-
In spring this concept is achieved by DI(Dependency injection). In dependency injection, rather instantiating and initialing objects, they are described in configuration files that how they should be configured and assembled.
Example program without IOC:
public class Main {
public static void main(String[] args) {
// Without IOC
//using SimpleSpellChecker hardcoded using new keyword (not preferred, tight coupled)
SpellChecker spellChecker = new SimpleSpellChecker();
TextEditor textEditor = new TextEditor(spellChecker);
textEditor.spellCheck();
}
}
public class TextEditor {
private SpellChecker spellChecker;
public TextEditor(SpellChecker spellChecker) {
this.spellChecker = spellChecker;
}
public void spellCheck() {
spellChecker.checkSpelling();
}
}
public interface SpellChecker {
void checkSpelling();
}
public class SimpleSpellChecker implements SpellChecker {
@Override
public void checkSpelling() {
System.out.println("Checking spelling using SimpleSpellChecker...");
}
}
public class AdvancedSpellChecker implements SpellChecker {
@Override
public void checkSpelling() {
System.out.println("Checking spelling using AdvancedSpellChecker...");
}
}
Example program with IOC:
public class Main {
public static void main(String[] args) {
// With IOC
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
TextEditor textEditor = context.getBean("textEditor", TextEditor.class);
textEditor.spellCheck();
}
}
public class TextEditor {
private SpellChecker spellChecker;
public TextEditor(SpellChecker spellChecker) {
this.spellChecker = spellChecker;
}
public void spellCheck() {
spellChecker.checkSpelling();
}
}
public interface SpellChecker {
void checkSpelling();
}
public class SimpleSpellChecker implements SpellChecker {
@Override
public void checkSpelling() {
System.out.println("Checking spelling using SimpleSpellChecker...");
}
}
public class AdvancedSpellChecker implements SpellChecker {
@Override
public void checkSpelling() {
System.out.println("Checking spelling using AdvancedSpellChecker...");
}
}
And here is the Spring configuration file, applicationContext.xml
:
<beans>
<bean id="textEditor" class="com.example.TextEditor">
<constructor-arg ref="advancedSpellChecker"/>
</bean>
<bean id="simpleSpellChecker" class="com.example.SimpleSpellChecker"/>
<bean id="advancedSpellChecker" class="com.example.AdvancedSpellChecker"/>
</beans>
You might think it’s not making any big difference, right? That’s because it’s just one program, when we create website or any other software, there are many classes and their dependencies on third party code which regularly require Maintenace/upgrade and spring makes that part very easy using its way to create objects and handling classes/object (called dependencies) which are required in creating object of other classes.
IOC containers-
They are used to configure and instantiate objects.
Types of IOC containers-
There are 2 types.
Type 1- BeanFactory –
-Implementation of factory pattern that applies inversion of control to separate the configuration and dependencies from application.
A-XmlBeanFactory-
-Implementation of BeanFactory,
-it is used to provide basic configuration to application by loading required beans from metadata.
-uses lazy instantiation of beans, means bean is instatiated when getBean() is called.
-no support for annotation-based DI.
XmlBeanFactory
is a low-level, most lightweight implementation of the Spring container..
Syntax –
Resource resource = new ClassPathResource(“springConfigFile”);
BeanFactory beanFactory= new XmlBeanFactory(resource);
Code Example –
1- applicationContext.xml
<bean id="myBean" class="com.example.MyBean">
<constructor-arg value="Hello, world!" />
</bean>
2- MyMainClass.java
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class MyMainClass {
public static void main(String[] args) {
// Load the Spring configuration file from the classpath
ClassPathResource resource = new ClassPathResource("applicationContext.xml");
// Create a new XmlBeanFactory from the configuration file
XmlBeanFactory factory = new XmlBeanFactory(resource);
// Get an instance of a bean from the factory
MyBean myBean = (MyBean) factory.getBean("myBean");
// Use the bean
myBean.doSomething();
}
}
3- MyBean.java
public class MyBean {
private String message;
public MyBean(String message) {
this.message = message;
}
public void doSomething() {
System.out.println(message);
}
// Getter and setter methods for the message property
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
Type 2-ApplicationContext –
-Extends the Beanfactory
-provides more specific functionalities to apllication.
-uses eager instantion, instantiate all beans at startup.
-supports annatation based DI(@Autowired, @PreDestroy)
A-ClassPathXmlApplicationContext-
implements ApplicationContext interface, Beans loaded from classpth.
The difference between XmlBeanFactory
and ClassPathXmlApplicationContext
is that XmlBeanFactory
is a low-level, lightweight implementation of the Spring container, whereas ClassPathXmlApplicationContext
is a more full-featured implementation that supports additional features like message sources and event handling.
Code Example –
1- applicationContext.xml
<bean id="myBean" class="com.example.MyBean">
<constructor-arg value="Hello, world!" />
</bean>
2- MyMainClass.java
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class MyMainClass {
public static void main(String[] args) {
// Load the Spring configuration file from the classpath
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// Get an instance of a bean from the context
MyBean myBean = (MyBean) context.getBean("myBean");
// Use the bean
myBean.doSomething();
}
}
3- MyBean.java
public class MyBean {
private String message;
public MyBean(String message) {
this.message = message;
}
public void doSomething() {
System.out.println(message);
}
// Getter and setter methods for the message property
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
B-FileSystemXmlApplicationContext-
implements ApplicationContext interface, Beans loaded using full path of file.
The difference with FileSystemXmlApplicationContext
is that it loads the configuration file from the file system rather than from the classpath. This can be useful in certain situations, such as when the configuration file needs to be located in a specific directory outside of the classpath.
Code Example –
1- applicationContext.xml
<bean id="myBean" class="com.example.MyBean">
<constructor-arg value="Hello, world!" />
</bean>
2- MyMainClass.java
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class MyMainClass {
public static void main(String[] args) {
// Load the Spring configuration file from the file system
ApplicationContext context = new FileSystemXmlApplicationContext("/path/to/applicationContext.xml");
// Get an instance of a bean from the context
MyBean myBean = (MyBean) context.getBean("myBean");
// Use the bean
myBean.doSomething();
}
}
3- MyBean.java
public class MyBean {
private String message;
public MyBean(String message) {
this.message = message;
}
public void doSomething() {
System.out.println(message);
}
// Getter and setter methods for the message property
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
C-WebXmlApplicationContext-
implements ApplicationContext interface, Beans loaded using web application context.
The difference with WebXmlApplicationContext
is that it is specifically designed for web applications and allows the Spring context to be loaded from the web.xml
deployment descriptor file. This can be useful for configuring web-related Spring features such as web controllers, view resolvers, and interceptors.
Code Example –
1- applicationContext.xml
<bean id="myBean" class="com.example.MyBean">
<constructor-arg value="Hello, world!" />
</bean>
2- MyMainClass.java
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class MyMainClass {
public static void main(String[] args) {
// Get the ServletContext
ServletContext servletContext = getServletContext();
// Load the Spring context from the web application deployment descriptor
WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);
// Get an instance of a bean from the context
MyBean myBean = (MyBean) context.getBean("myBean");
// Use the bean
myBean.doSomething();
}
}
3- MyBean.java
public class MyBean {
private String message;
public MyBean(String message) {
this.message = message;
}
public void doSomething() {
System.out.println(message);
}
// Getter and setter methods for the message property
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
Types of Dependency Injection-
1-Constructor based DI-
Data members and objects are configured using constructor.
Code Example- here’s an example of using constructor-based dependency injection in Spring:
public class MyBean {
private final Dependency dependency;
public MyBean(Dependency dependency) {
this.dependency = dependency;
}
// other methods that use the dependency
}
public class Dependency {
// properties and methods of the dependency
}
To configure this dependency in Spring, we can use XML configuration or Java-based configuration. Here’s an example of using XML configuration:
<bean id="myBean" class="com.example.MyBean">
<constructor-arg ref="dependency"/>
</bean>
<bean id="dependency" class="com.example.Dependency"/>
2-Setter method based DI-
Data members and objects are configured using setter method of pojo files.
Code Example – here’s an example of using setter-based dependency injection in Spring:
public class MyBean {
private Dependency dependency;
public void setDependency(Dependency dependency) {
this.dependency = dependency;
}
// other methods that use the dependency
}
public class Dependency {
// properties and methods of the dependency
}
To configure this dependency in Spring, we can again use XML configuration or Java-based configuration. Here’s an example of using XML configuration:
<bean id="myBean" class="com.example.MyBean">
<property name="dependency" ref="dependency"/>
</bean>
<bean id="dependency" class="com.example.Dependency"/>
Setter-based dependency injection is generally more flexible than constructor-based dependency injection, as it allows for multiple dependencies to be injected into a single bean and allows for dependencies to be changed at runtime. However, constructor-based injection can be more useful in certain situations, such as when all dependencies must be provided at construction time or when immutability is desired.
3-Interface based DI-
Not supported in spring.
Code Example – here’s an example of using interface-based dependency injection in Spring:
public interface MyDependency {
void doSomething();
}
public class MyBean {
private MyDependency dependency;
public void setDependency(MyDependency dependency) {
this.dependency = dependency;
}
// other methods that use the dependency
}
public class MyDependencyImpl implements MyDependency {
// implementation of the doSomething() method
}
To configure this dependency in Spring, we can again use XML configuration or Java-based configuration. Here’s an example of using XML configuration:
<bean id="myBean" class="com.example.MyBean">
<property name="dependency" ref="myDependency"/>
</bean>
<bean id="myDependency" class="com.example.MyDependencyImpl"/>
Interface-based dependency injection is a powerful feature of Spring, as it allows for loose coupling between components and enables runtime substitution of dependencies with alternative implementations. It also allows for easier testing and mocking of components.
Autowiring –
Wiring is spring shows relationship bw beans, and we have constructor and setter method based DI to establish relationships bw beans.
But in spring we also have autowire attribute to provide automation to wiring.
Types of autowiring-
1-no– by default, no autowiring
Code Example –
public class Person {
private String name;
private Address address;
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
// getters and setters
}
public class Address {
private String street;
private String city;
public Address(String street, String city) {
this.street = street;
this.city = city;
}
// getters and setters
}
and here is bean xml configuration-
<bean id="person" class="com.example.Person">
<constructor-arg name="name" value="John" />
<constructor-arg ref="address" />
</bean>
<bean id="address" class="com.example.Address">
<constructor-arg value="Main St" />
<constructor-arg value="New York" />
</bean>
2- byName – wiring based on instance name
Code Example –
public class Person {
private String name;
private Address address;
public Person(String name) {
this.name = name;
}
public void setAddress(Address address) {
this.address = address;
}
// getters
}
public class Address {
private String street;
private String city;
public void setStreet(String street) {
this.street = street;
}
public void setCity(String city) {
this.city = city;
}
// getters
}
and here is xml bean configuration. –
<bean id="person" class="com.example.Person" autowire="byName">
<constructor-arg name="name" value="John" />
</bean>
<bean id="address" class="com.example.Address">
<property name="street" value="Main St" />
<property name="city" value="New York" />
</bean>
3–byType – wiring based on datatype, there should be only one unique datatype of bean is declared.
Code Example –
public class Person {
private String name;
private Address address;
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
// getters
}
public class Address {
private String street;
private String city;
// getters
}
and here is xml bean configuration. –
<bean id="person" class="com.example.Person" autowire="byType">
<constructor-arg name="name" value="John" />
</bean>
<bean id="address" class="com.example.Address">
<property name="street" value="Main St" />
<property name="city" value="New York" />
</bean>
4-constructor – It’s similar to byType in constructor argument
Code Example –
public class Person {
private String name;
private Address address;
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
// getters
}
public class Address {
private String street;
private String city;
// getters
}
and here is xml bean configuration. –
<bean id="person" class="com.example.Person" autowire="constructor">
<constructor-arg name="name" value="John" />
</bean>
<bean id="address" class="com.example.Address">
<constructor-arg value="Main St" />
<constructor-arg value="New York" />
</bean>
5-autoDetect- chooses between autowiring by constructor or bytype.
public class Person {
private String name;
private Address address;
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
public Person(Address address) {
this.address = address;
}
// getters
}
public class Address {
private String street;
private String city;
// getters
}
and here is xml bean configuration. –
<bean id="person" class="com.example.Person" autowire="autoDetect">
<constructor-arg name="name" value="John" />
</bean>
<bean id="address" class="com.example.Address">
<constructor-arg value="Main St" />
<constructor-arg value="New York" />
</bean>