IoC라는 약자로 많이 사용되는 제어의 역전은 간단히 제어권의 이전을 통해 프로그램의 제어 흐름 구조가 뒤바뀌는 것이라고 설명할 수 있다.
객체의 생성 방법을 결정하고 그렇게 만들어진 오브젝트를 돌려조는 오브젝트를 흔히 팩토리 라고 부른다.
오브젝트를 생성하는 쪽과 생성된 오브젝트를 사용하는 쪽의 역활과 책임을 깔끔하게 분리하려는 목적으로 사용이 된다.
(팩토리의 뜻은 공장이고, 오브젝트를 찍어내는 오브젝트를 공장처럼 생각하면 된다.)
이전에 작성했던 UserDao 클래스에서도 역할을 분리해 팩토리클래스를 생성할 수 있다.
UserDao, ConnectionMaker 관련 생성 작업을 DaoFactory로 옮기고, 타 클래스에서 DaoFactory 에 요청해서 미리 만들어진 userDao 오브젝트를 가져와 사용하게 만들 수 있다.
public class DaoFactory{
public UserDao userDao(){
ConnectionMaker connectionMaker = new DConnectionMaker();
UserDao userDao = new UserDao(connectionMaker);
return userDao;
말그대로 제어권의 이전을 통해 프로그램의 제어 흐름 구조가 뒤바뀌는걸 볼 수 있다.
이전과는 다르게 팩토리로부터 원하는 오브젝트를 가져와서 활용하면 된다. (이전에는 해당 서비스의 커넥션 메이커를 따로 인스턴스 객체로 생성해 사용한 반면에, 이번에는 팩토리의 메소드를 호출만 하면 원하는 커넥션을 가진 Dao객체를 생성할 수 있게 된것이다.)
UserDao와 ConnectionMaker 는 각각 애플리케이션의 핵심적인 데이터 로직과 기술 로직을 담당하고 있고, DaoFactory 는 이런 애플리케이션의 오브젝트들을 구성하고 그 관계를 정의하는 책임을 맡고 있다.
전자가 로직을 담당하는 컴포넌트(각각의 독립된 모듈) 이라면 후자는 애플리케이션을 구성하는 컴포넌트의 구조와 관계를 정의한 설계도 같은 역할을 한다고 볼 수 있다.
새로운 ConnectionMaker 구현 클래스로 변경이 필요하면 DaoFactory를 수정해서 변경된 클래스를 생성해 설정해주도록 코드를 수정해주면 된다.
제어의 역전이란 이렇게 모든 오브젝트가 능동적으로 자신이 사용할 클래스를 결정하고, 언제 어떻게 그 오브젝트를 만들지를 스스로 관장한다. 모든 종류의 작업을 사용하는 쪽에서 제어하는 구조로 제어 흐름의 개념을 거꾸로 뒤집는 것이다.
오브젝트는 자신이 사용할 오브젝트를 스스로 선택/생성 하지 않고 어떻게 만들어지고 어디서 사용되는지 알 수 없다. 모든 제어 권한을 자신이 아닌 다른 대상에게 위임하기 때문이다.
위의 코드를 보면 이전과는 다르게 UserDao가 점차 역할을 분리하며 나중에는 매번 UserDao 에서 입력해줘야 했던 커넥션 설정을 따로 커넥션을 가져오는 객체를 생성하여 가져올 수 있게 되었고
해당 객체에서 또 설정해야 하는 부분을 또다시 분리하여 완전히 다른 클래스에서 원하는 커넥션의 설정을 갖고있는 userDao 객체를 얻을 수 있게 되었다.
스프링에서는 스프링이 제어권을 가지고 직접 만들고 관계를 부여하는 오브젝트를 빈 이라고 부른다. -> 오브젝트 단위의 애플리케이션 컴포넌트를 말한다.
스프링에서는 DaoFactory 처럼 빈의 생성과 관계설정 같은 제어를 담당하는 IoC 오브젝트를 빈 팩토리 라고 부른다.
또다른 명칭으로 빈 팩토리를 좀 더 확장한 애플리케이션 컨텍스트 를 주로 사용하기도 한다. -> IoC방식을 따라 만들어진 일종의 빈 팩토리 라고 생각하면 된다.
애플리케이션 컨텍스트는 별도의 정보를 참고해서 빈의 생성, 관계설정 등의 제어 작업을 총괄한다.
DaoFactory 로 예를 들자면 UserDao는 하나의 장치 , 즉 모듈 이라고 할 수 있겠고, 이전에 설정했던 원하는 서비스의 Connection 을 만드는 ConnectionMaker 객체를 생성하는 부분이 관계설정을 한다고 생각해도 되겠다.
@Configuration //->빈 팩토리가 사용할 설정정보라는 표기
public class DaoFactory{
@Bean //-> 오브젝트 생성을 담당하는 IoC용 메소드 라는 표시
public UserDao userDao(){
ConnectionMaker connectionMaker = new DConnectionMaker();
UserDao userDao = new UserDao(connectionMaker);
return userDao;
이 두가지 어노테이션 만으로 스프링 프레임워크의 빈 팩토리가 IoC방식의 기능을 제공할 때 사용할 완벽한 설정정보가 된다.
이제 이 DaoFactory를 설정정보로 사용하는 AnnotationConfigApplicationContext 를 사용하면 된다.
ApplicationContext context = new AnnotationConfigApplicationContext(DaoFactory.class);
UserDao dao = context.getBean("userDao",UserDao.class);
이렇게 기존의 DaoFactory 에서 UserDao 객체를 생성하는것과 같이 스프링에서 빈팩토리를 이용해 등록한 빈을 객체로 가져올 수 있다.
애플리케이션 컨텍스트는 애플리케이션에서 IoC를 적용해서 관리할 모든 오브젝트에 대한 생성과 관계설정을 담당한다.
DaoFactory와 달리 직접 오브젝트를 생성하고 관계를 맺어주는 코드가 없고, 설정정보를 통해 얻는다. 때로는 외부의 오브젝트 팩토리에 그 작업을 위임하고 그 결과를 사용하기도 한다.
애플리케이션 컨텍스트는 DaoFactory 클래스를 설정정보로 등록해두고 @Bean이 붙은 메소드의 이름을 가져와 빈 목록을 만들어둔다.
클라이언트는 getBean()을 호출하면 빈을 생성하는 메소드를 호출해서 오브젝트를 생성시킨 후 클라이언트에 돌려준다.
이렇게 애플리케이션 컨텍스트를 사용하는 이유는
범용적이고 유연한 방법으로 IoC기능을 확장하기 위해서이다.
DaoFactory 와 빈팩토리의 동작방식에는 스프링은 여러 번에 걸쳐 빈을 요청하더라도 매번 동일한 오브젝트를 돌려주고, DaoFactory는 새로운 오브젝트를 생성해서 돌려준다는 차이가 있다.