πΏ
Spring
IoC
&DI
are the foundations ofSpring framework
.
Spring Doc Available at here
IoC
(Inversion of Control
) is a software principle
where the overall management of the object lifecycle
is handled by a framework
than a developer. In Spring
, it is the Spring IoC container
that instantiates, configures, and assembles the Spring object
termed as Spring Beans
.
Following, IoC
, developers can better focus on implementing business logic than managing the Spring Bean objects
and hence increase:
βοΈ code maintainability
π code extensibility
DI
(Dependency Injection
) is a pattern of IoC
in which it strictly refers to a mechanism
where dependencies
required to create Spring Bean objects
are injected externally perhaps by Spring IoC Container
.
DI
often results in:
π low coupling
Dependency Inversion Principle
π¦ code flexibility
π§ testability
Spring DI
can be achieved mostly by the following approaches:
Constructor Dependency Injection
Setter Dependency Injection
@Autowired
XML
the above methods will be discussed with the given code snippet.
Tire classes
// Tire Interface
public interface Tire {
String getBrand();
}
// KoreanTire Class
public KoreanTire implements Tire {
@Override
public String getBrand() {
return "KoreanTire";
}
}
// AmericanTire Class
public AmericanTire implements Tire {
@Override
public String getBrand() {
return "AmericanTire";
}
}
π§± Constructor Dependency Injection
is a mechanism
where the dependencies
are injected by the constructors
.
Below code snippet can be a good example of the Constructor Dependency Injection
.
Car class
public class Car {
private Tire tire;
public Car(Tire tire) {
this.tire = tire;
}
public void checkTire() {
System.out.println(tire.getBrand());
}
}
and given the above code, dependencies can be injected as below at the client level
. Similarly, under the context of Spring framework
, the below DI
is automatically handled by Spring IoC Container
.
Client
public class Client {
public static void main(String[] args) {
Tire koreanTire = new KoreanTire();
Tire americanTire = new AmericanTire();
Car car1 = new Car(koreanTire);
Car car2 = new Car(americanTire);
car1.checkTire(); // "KoreanTire"
car2.checkTire(); // "AmericanTire"
}
}
π Constructor Dependency Injection
is a mechanism
where the dependencies
are injected by the setters
.
Setter Dependency Injection
can be implemented like the below code snippet.
Car class
public class Car {
private Tire tire;
public Car() {
}
public void setTire(Tire tire) {
this.tire = tire;
}
public void checkTire() {
System.out.println(tire.getBrand());
}
}
given the above code, setters()
can be invoked and dependencies
then can be injected.
Client
public class Client {
public static void main(String[] args) {
Tire koreanTire = new KoreanTire();
Tire americanTire = new AmericanTire();
Car car1 = new Car();
Car car2 = new Car();
car1.setTire(koreanTire);
car2.setTire(americanTire);
car1.checkTire(); // "KoreanTire"
car2.checkTire(); // "AmericanTire"
}
}
π @Autowired
is a Spring dependent
feature where the Spring
automatically injects dependencies
on the @Autowired declared
code snippets
.
@Autowired
can be applied at constructors
, methods
, and fields
where the earlier two correspond to the Constructor DI
and Setter DI
. Over the @Autowired
declared fields, however, Spring
internally finds relevant Spring Bean object
and processes DI
.
@Autowired (Constructor)
// Constructor
public class Example {
private Needed needed;
@Autowired
public Example(Needed needed) {
this.needed = needed;
}
}
@Autowired (Setter or Method)
// Constructor
public class Example {
private Needed needed;
public Example() {
}
// Setter
@Autowired
public void setNeeded(Needed needed) {
this.needed = needed;
}
// Method
@Autowired
public void setNecessary(Needed needed) {
this.needed = needed;
}
}
@Autowired (Field)
// Constructor
public class Example {
@Autowired
private Needed needed;
}
Although, @Autowired
provides Spring
automated DI
, implementing DI
via @Autowired
is not desirable if considering its resulting difficulties in unit testing
as there is no way to inject required dependencies
in the context of testing environment
. Hence, Constructor DI
and Setter DI
would be better DI
practices than the @Autowired
field DI
.
Over Constructor DI
and Setter DI
, following Setter DI
's capability to modify dependencies
, it is often not preferred in the industry standards, implying Constructor DI
for non-modifying dependencies
is the best DI practice
with a final keyword
:
@Autowired (Constructor & final)
// Constructor
public class Example {
private final Needed needed;
@Autowired
public Example(Needed needed) {
this.needed = needed;
}
}
π XML
is a legacy approach for Spring DI
, in which a developer externally specifies all the DI configurations
in the XML
files by providing id
, and property
references.
services.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- services -->
<bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
<property name="accountDao" ref="accountDao"/>
<property name="itemDao" ref="itemDao"/>
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions for services go here -->
</beans>
daos.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="accountDao"
class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions for data access objects go here -->
</beans>
DI
in Spring
has priorities and rules, where failing to qualify the rules results in Spring application
to break down.
Spring DI
occurs under the above principles in which the Sping
locates the most relevant unique bean
and failing to find one results in the application
failing.
For multiple beans
candidates, by specifying their priorities or assigning names, developers could gain more control over the Spring DI
.
@Primary
is a good example where it indicates that a specific bean
should be prioritised when multiple beans
are eligible to be autowired
to a single-valued dependency
:
@Primary
@Configuration
public class MovieConfiguration {
@Bean
@Primary
public MovieCatalog firstMovieCatalog() { ... }
@Bean
public MovieCatalog secondMovieCatalog() { ... }
// ...
}
Similarly, @Qualifier
assigns extra layer of Spring Bean
-specific identifiers in which invoking such identifier instructs Spring IoC
to inject such dependency
:
@Qualifier
@Configuration
public class MovieConfiguration {
@Bean
@Qualifier("main")
public MovieCatalog firstMovieCatalog() { ... }
@Bean
@Qualifier("notMain")
public MovieCatalog secondMovieCatalog() { ... }
// ...
}
public class MovieRecommender {
@Autowired
@Qualifier("main")
private MovieCatalog movieCatalog;
// ...
}
μ€νλ§ μ
λ¬Έμ μν μλ° κ°μ²΄ μ§ν₯μ μ리μ μ΄ν΄
Spring Doc
Geeks for Geeks
F-Lab