IoC 개요
DL(Dependency Lookup) : 필요한 의존성을 찾아서 넣는 것
-컨테이너가 lookup context를 통해서 필요한 Resource나 Object를 얻는 방식
-JNDI 이외의 방법을 사용한다면 JNDI 관련 코드를 오브젝트 내에서 일일히 변경해줘야 함
-Lookup 한 Object를 필요한 타입으로 Casting 해주어야 함
-Naming Exception을 처리하기 위한 로직 필요
DI(Dependency Injection) : 의존성을 알아서 주입해주는 것
-Object에 lookup 코드를 사용하지 않고 컨테이너가 직접 의존 구조를 Object에 설정할 수 있도록 지정해주는 방식
-Object가 컨테이너의 존재 여부를 알 필요가 없음
-Lookup 관련된 코드들이 Object 내에서 사라짐
-Setter Injection과 Constructor Inject
IoC의 개념
-객체 제어 방식 : 기존에는 필요한 위치에서 개발자가 필요한 객체를 생성하는 로직을 구현하였으나, IoC는 객체 생성을 Container에게 위임하여 처리함
-Loose Coupling : IoC를 사용하면 객체 간의 결합도를 떨어뜨릴 수 있다는 장점이 있음
객체 간 결합도가 높으면, 해당 클래스가 유지보수 될 때, 그 클래스와 결합된 다른 클래스도 같이 유지보수 되어야 할 가능성이 높습니다. 이를 해결하기 위한 방안을 살펴보겠습니다.
객체 간 강한 결합
-클래스 호출 방식
-클래스 내에 선언과 구현이 모두 되어있기 때문에 다양한 형태로 변화 불가능
-Service 구현체를 Controller에서 직접 생성하여 사용할 경우, Service를 다른 Service 코드로 교체하거나 내부 코드가 변경되면 Controller까지 수정해야 할 수도 있음
public class RedCar {
public String rideRedCar(String color) {
return "Car " + color;
}
}
public class BlueCar {
public String rideBlueCar(String color) {
return "Car " + color;
}
}
public static void main(String[] args) {
RedCar redCar = new RedCar();
BlueCar blueCar = new BlueCar();
String choosed = redCar.rideRedCar("red");
System.out.println(choosed);
}
위 방식의 경우, blueCar로 변경할 경우나 redCar의 메소드명을 변경할 경우 main에서 많은 코드 변경이 이뤄져야 합니다.
1. 인터페이스 호출 방식
- 다형성을 통해 객체 간 강한 결합을 낮춤
- 구현 클래스 교체가 용이하여 다양한 형태로 변화 가능
- 인터페이스 교체 시 호출 클래스도 수정해야 함
public interface Car {
String rideCar(String color);
}
public class RedCar implements Car {
public String rideCar(String color) {
return "Car " + color;
}
}
public class BlueCar implements Car {
public String rideCar(String color) {
return "Car " + color;
}
}
public static void main(String[] args) {
RedCar redCar = new RedCar();
BlueCar blueCar = new BlueCar();
String choosed = redCar.rideCar("red");
System.out.println(choosed);
}
rideCar 메서드를 Car 인터페이스를 통해 구현하였기 때문에 결합도가 낮아졌습니다. 그러나 여전히 redCar와 bluCar를 갈아끼워야 한다는 불편함이 존재합니다. 이를 해결해주기 위해 팩토리 패턴을 사용하겠습니다.
public class CarFactory {
public static Car getCar(String car) {
if(car.equals("red")) {
return new RedCar();
} else if(car.equals("blue")) {
return new BlueCar();
} else {
return null;
}
}
}
public static void main(String[] args) {
Car car1=CarFactory.getCar("red");
Car car2=CarFactory.getCar("blue");
System.out.println(car1);
}
팩토리 패턴을 이용하여 입력값에 적합한 객체를 가져올 수 있게 되었습니다.
2. Assembler 이용
- IoC 호출 방식
- 팩토리 패턴의 장점을 더하여 어떠한 것에도 의존하지 않는 형태가 됨
- 실행 시점(Runtime)에 클래스 간의 관계가 형성됨
- 각 Service의 Life Cycle을 관리하는 Assembler를 사용
- Spring Container가 외부조립기(Assembler) 역할을 함
public static void main(String[] args) {
Car car1=CarFactory.getCar("red");
Car car2=CarFactory.getCar("blue");
System.out.println(car1);
}
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("com/my/car/application.xml");
// Car car=(Car)context.getBean("red");
Car car=context.getBean("red", RedCar.class);
String car1 = car.rideCar("red");
System.out.println(car1);
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<bean id="red" class="com.my.car.RedCar"></bean>
<bean id="blue" class="com.my.car.BlueCar"></bean>
<context:component-scan base-package="com.my.car"></context:component-scan>
</beans>
bean 설정시 default는 singleton입니다.
<bean id="red" class="com.my.car.RedCar" scope="prototype"></bean>
scope를 prototype으로 설정시 서로 다른 객체를 만들 수 있습니다.
'Spring' 카테고리의 다른 글
[FileUpload] 파일 업로드 (0) | 2022.04.22 |
---|---|
[Spring Architecture DI] 의존성 주입(DI) (0) | 2022.04.20 |
[Spring Architecture DI] Container (0) | 2022.04.19 |
[Spring Architecture DI] Spring Framework 구조 (0) | 2022.04.19 |
[Spring Architecture DI] Spring 등장 배경 (0) | 2022.04.19 |