IoC (Inversion of Control, 제어의 역행)
객체 제어 방식
- 기존 : 필요한 위치에서 개발자가 필요한 객체 생성 로직을 구현했다.
- IoC : 객체 생성을 Container에게 위임해 처리한다. 객체지향 언어에서 Object 간의 연결 관계를 런타임에 결정한다.
IoC 사용에 따른 장점
- 객체 간의 결합도를 떨어뜨릴 수 있다. 객체 간 결합도가 높으면 해당 클래스가 유지보수될 때 그 클래스와 결합된 다른 클래스도 같이 유지보수돼야할 가능성이 높아진다.
IoC 유형
1. DL : Dependency Lookup -> JNDL Lookup
2. DI : Dependency Injection -> 1) Setter Injection, 2) Constructor Injection, 3) Method Injection
* Spring에서는 주로 3)방법을 사용한다.
Dependency Lookup
- 컨테이너가 lookup context를 통해서 필요한 Resource나 Object를 얻는 방식
- JNDI 이외의 방법을 사용한다면 JNDI 관련 코드를 오브젝트 내에서 일일이 변경해줘야 한다.
- Lookup한 Object를 필요한 타입으로 캐스팅해야한다.
- Naming Exception을 처리하기 위한 로직이 필요하다.
- 주로 Connection Pool을 얻어올 때 사용한다. (밑의 예시 참고)
//FactoryDao.java
public Connection getConnection() throws SQLException {
try {
Context context = new InitialContext();
Context rootContext = (Context) context.lookup("java:comp/env");
DataSource dataSource = (DataSource) rootContext.lookup("jdbc/____");
return dataSource.getConnection();
} catch (NamingException e) {
e.printStackTrace();
}
return null;
}
<!-- META-INF/context.xml -->
<Context>
<Resource name="jdbc/____" auth="Container" type="javax.sql.DataSource"
maxTotal="100" maxIdle="30" maxWaitMillis="10000"
username="_____" password="____" driverClassName="com.mysql.cj.jdbc.Driver"
url="jdbc:mysql://localhost:3306/_____?serverTimezone=UTC&useUniCode=yes&characterEncoding=UTF-8"/>
<WatchedResource>WEB-INF/web.xml</WatchedResource>
</Context>
JNDI
- 필요한 자원을 미리 key-value 쌍으로 저장한 후 필요할 때 키를 이용해 값을 얻는 방법이다.
Dependency Injection
- Object에 lookup코드를 사용하지 않고 컨테이너가 직접 의존구조를 Object에 설정할 수 있도록 지정하는 방식
- Object가 컨테이너의 존재여부를 알 필요 없다.
- Lookup 관련된 코드들이 Object 내에서 사라진다.
- Spring에서의 DI글은 [이 글]에 정리했다.
IoC Container
- 오브젝트의 생성과 관계설정, 사용, 제거 등의 작업을 애플리케이션 코드 대신 독립된 컨테이너가 담당한다.
- 컨테이너가 코드 대신 오브젝트에 대한 제어권을 가지고 있어 IoC라고 부른다.
- 그래서 스프링 컨테이너를 IoC컨테이너라고 부르기도 한다.
- 스프링에서 IoC를 담당하는 컨테이너에는 BeanFactory, ApplicationContext가 있다.
Spring DI Container
- Spring DI Container가 관리하는 객체를 Bean이라고 하고, 이 빈들의 라이프사이클을 관리하는 의미로 BeanFactory라고 한다.
- BeanFactory에 여러 가지 컨테이너 기능을 추가하여 ApplicationContext라고 한다.
Container
- 객체의 생성, 사용, 소멸에 해당하는 라이프사이클을 담당
- 라이프사이클을 기본으로 애플리케이션 사용에 필요한 주요 기능을 제공
Container 기능
- 라이프사이클 관리
- Dependency 객체 제공
- Thread 관리
- 기타 애플리케이션 실행에 필요한 환경
Container 필요성
- 비즈니스 로직 외에 부가적인 기능에 대해서 독립적으로 관리되도록 한다.
- 서비스 look up이나 Configuration에 대한 일관성을 갖기 위한다.
- 서비스 객체를 사용하기 위해 각각 Factory 또는 Singleton 패턴을 직접 구현하지 않아도 된다.
객체 간 결합도를 낮추는 단계
1. 강한 결합
클래스 호출 방식. 클래스 내에 선언과 구현이 모두 되어있기 때문에 다양한 형태로 변화가 불가능하다.
public class HomeController{
private KorServiceImpl korService = new KorServiceImpl();
private EngServiceImpl engService = new EngServiceImpl();
}
2. 다형성 이용
인터페이스 호출 방식. 구현 클래스 교체가 용이해 다양한 형태로 변화 가능하다.
하지만 인터페이스 교체 시 호출 클래스도 수정해야한다.
public class HomeController{
private CommonService korService = new KorServiceImpl();
private CommonService engService = new EngServiceImpl();
}
3. Factory 이용
팩토리 호출 방식. 팩토리가 구현 클래스를 생성하므로 클래스는 팩토리를 호출한다.
인터페이스 변경 시 팩토리만 수정하면 되므로 호출 클래스에는 영향을 미치지 않는다.
하지만 클래스에 팩토리를 호출하는 소스가 들어가야한다. 그것 자체가 팩토리에 의존함을 의미한다.
public class HomeController{
private CommonService korService = ServiceFactory.getKorServiceImpl();
private CommonService engService = ServiceFactory.getEngServiceImpl();
}
4. Assembler 이용
IoC 호출 방식. 팩토리 패턴의 장점을 더하여 어떤 것에도 의존하지 않는 상태가 된다.
실행시점에 클래스 간의 관계가 형성된다.
<!-- context.xml -->
<bean id="korService" class="com.___.___.service.KorServiceImpl"/>
<bean id="engService" class="com.___.___.service.EngServiceImpl"/>
public class HomeController{
ApplicationContext context = new ClassPathXmlApplicationContext("context.xml주소");
private CommonService korService = context.getBean("korService", KorService.class);
private CommonService engService = context.getBean("engService", EngService.class);
}
'기록 > BACKEND' 카테고리의 다른 글
[Spring] DI (0) | 2022.04.20 |
---|---|
[Spring] JNDI 설정하기 (0) | 2022.04.19 |
[Spring] DI 주입하기 - java (0) | 2022.04.16 |
[Spring] DI 주입하기 - xml (0) | 2022.04.15 |
[WEB] 파라미터와 어트리뷰트 (0) | 2022.04.04 |
댓글