Dependency
Dependeny
Refers to the relationship that arises when one object uses another.
public class OrderService {
// ģģ”“ķź³ ģė PaymentProcessor ź°ģ²“넼 ģ§ģ ģģ±
private PaymentProcessor paymentProcessor = new PaymentProcessor();
}
- Ex) If an
OrderService
uses aPaymentProcessor
, we say thatOrderService
depends onPaymentProcessor
. - Tight coupling
- Object is directly created leads to tight coupling
- Itās difficult to change implementations or use substitute objects (Mocks) for testing
Dependency Injection
Dependency
Instead of an object creating its own dependencies, its dependencies are given to it from the outside.
- A specific technique (a design pattern) that implements the Inversion of Control (IoC) principle.
Without DI
public class MenuController {
public static void(String[] args) {
MenuService menuService = new MenuService();
List<Menu> menuList = menuService.getMenuList();
}
}
//
public class MenuService {
public List<Menu> getMenuList() {
return null;
}
}
- There is a dependency relationship established because the
MenuController
class creates and references another class object
With DI
public class CafeClient {
public static void main(String[] args) {
MenuService menuService = new MenuService();
MenuController controller = new MenuController(menuService); //
List<Menu> menuList = menuService.getMenuList();
}
}
public class MenuController {
private MenuService menuService;
/// HERE<<<<
public MenuController(MenuService menuService) {
this.menuService = menuService;
}
public List<Menu> getMenus() {
return menuService.getMenuList();
}
}
MenuController
receives theMenuService
object through its constructor- This is DI - passing an object as a constructor parameter = injecting an object from an external source (
CafeClient
) CafeClient
(the external source) passesmenuService
as a constructor parameter
- This is DI - passing an object as a constructor parameter = injecting an object from an external source (
Loose coupling
DI AVOIDS TIGHT COUPLING
- Tight coupling
- When you use the
new
keyword inside a class to create an object it depends on, you create a strong, direct link (tight coupling) to a specific implementation of that object. - U need to change all parts of the code that uses it if u edit the class
- When you use the
- Loose coupling
- DI allows loose coupling, which allows you to easily substitute different implementations (e.g., a real one for production, a stub for testing) without ever changing the dependent classās internal code.
MenuController
depends only on theMenuService
interface.- The concrete implementations (
MenuServiceImpl
,MenuServiceStub
) are injected at runtime.
In Spring
- The Spring Frameworkās container streamlines dependency injection by handling object creation and injection on your behalf.
- Uses annotations like
@Component
,@Service
,@Repository
for automatic component scanning where Spring directly instantiates and manages objects. - Uses
@Configuration
and@Bean
methods when you want to explicitly define how an object is created and configured within Springās container.
- Uses annotations like
@Configuration
public class Config {
@Bean
public MenuService menuService() {
return new MenuServiceStub();
}
@Bean
public MenuController menuController() {
return new MenuController(menuService());
}
}
- Spring uses the container to manage the
MenuServiceStub
instance (created by themenuService()
method) as a Bean. - When creating the
menuController
Bean, Spring supplies the managedMenuServiceStub
instance as theMenuService
dependency toMenuController
ās constructor.**
Methods
Method | Description | Recommendation |
---|---|---|
ā Constructor Injection | Injects via the constructor | ā Most Recommended |
Field Injection | Injects via fields | ā Not Recommended (for testing/immutability) |
Setter Injection | Injects via setter methods | ā ļø Use for optional dependency injection |
Constructor Injection
- Constructor Injection is the most recommended
public class OrderService {
private final PaymentProcessor paymentProcessor;
// ģģ±ģ 주ģ
@Autowired
public OrderService(PaymentProcessor paymentProcessor) {
this.paymentProcessor = paymentProcessor;
}
}
- Advantage: You can use the
final
keyword- Means that you can guarantee that it will NEVER be
null
!! (you can avoidNullPointerException
)
- Means that you can guarantee that it will NEVER be
- When you have 2 or more constructors, you must use the
@Autowired
annotation on top of the constructor
Field Injection
public class OrderService {
// Field Injection - Not recommended for critical dependencies
@Autowired
private PaymentProcessor paymentProcessor;
// No constructor needed to inject PaymentProcessor
// ... other methods using paymentProcessor
}
- Not recommended
- You CANNOT use
final
keyword, there is a chance of it beingnull
and causingNullPointerException
- It relies on reflection (a Spring mechanism) to set the field, which can make testing harder as you canāt easily instantiate the object without Springās container.
Setter Injection
public class OrderService {
private PaymentProcessor paymentProcessor;
// Setter Injection
@Autowired
public void setPaymentProcessor(PaymentProcessor paymentProcessor) {
this.paymentProcessor = paymentProcessor;
}
// ... other methods using paymentProcessor
}
- When to Use: Itās useful for optional dependencies or for dependencies that might change during the objectās lifecycle (though this is less common in typical Spring applications).
- Also not recommended for similar reasons like the field injection above
Dependency in Gradle
The project has dependencies to other libraries/modules
- Your software project isnāt built from scratch in isolation. Instead, it relies on, or depends on, pre-written code or functionalities provided by other separate pieces of software
- Gradle - Dependency configuration