우리는 흔히 스프링 컨테이너가 관리하는 자바 객체를 Bean 이라고 한다.
Hello hello = new Hello();
이렇게 new 키워드를 사용해서 객체를 생성했다면, Bean은 new 대신
private Hello hello
요로코롬 쓴다는거
Spring에서는 IoC(제어의 역전)을 사용하고 있기 때문에 Spring이 직접 자바 객체를 생성하고 관리한다. 우리는 Spring이 관리하는 자바 객체를 가져와 사용하는 것이다. 이 객체가 Bean인것임
근데 왜 스프링에서는 빈을 사용하는 걸까.
물론 개발자가 직접 자바객체를 생성하게 되면 직접 생명주기를 관리해야하는 대신, 스프링 빈은 스프링이 라이프사이클 관리를 해주기때문에 편하다! 라는 것도 이유 중 하나다.
하지만 가장 큰 이유는 특정 객체를 의존하는 다른 객체들에게 의존성을 주입하기 위함에 가장 큰 목적이 있다. 객체간 의존관계를 등록할 때 스프링 컨테이너에서 해당하는 빈을 찾고, 그 빈과 의존성을 만든다.
🤷♂️ 만약 객체를 빈으로 등록하지 않고 의존성 주입을 하게 된다면 어떻게 될까??
코드로 보면서 이해해보자.
public class Service {
private final Dao dao;
public Service(Dao dao){
this.dao = dao;
}
}
Service service = new Service(new JdbcDao());
위 코드는 Service를 초기화하는데 Dao 구현체를 생성해서 넣어주고 있다. Dao 구현체가 바뀐다면 어떻게 될까?
Service service = new Service(new DifferentDao()); // 변경
Service를 생성하는 곳에서도 변경이 일어난다. 왜 그런걸까?
Service service = new Service(new JdbcDao(), new DifferentDao, new OtherDao());
Service를 생성할때 Dao를 초기화함과 동시에 어떤 Dao를 선택할건지에 대한 책임이 있기 때문이다.
객체를 직접 생성하고 주입해줘야할 의존성이 여러개일때를 보자.
위의 그림에서 A객체를 생성하려면 어떻게 해야할까?
B,C,D 객체를 먼저 생성해야 하는데 B를 생성하려면 E,F가 필요하다. 또 C를 생성하기 위해서는 E가 필요하다. D를 생성하려면 또 G가 필요함.
최대한의 인내심과 끈기를 끌어올려도 바로 퇴사 각임
이렇게 직접 의존성을 주입하기 위해서는 의존 관계를 모두 파악해야하는 것이다.
그래서 의존성 주입이 필요한 객체를 Bean으로 등록하여 스프링 컨테이너가 관리하도록 해야한다. Spring은 의존성을 자동 주입해서 개발자들이 의존성을 사용하는 로직에만 집중할 수 있게 해주는 것이다.
또한, 직접 객체를 생성하는 경우, 객체간의 결합도가 매우 높아져서 좋지 못하다.
new연산자를 이용해 직접 객체를 생성할 경우, A클래스가 B,C객체에 강한 의존 관계가 성립된다. 이런 강한 의존 관계는 추후 변경이 필요할때에 사이드이펙트의 가능성이 높아 유지 보수가 어렵다.
객체지향적 설계의 목표는 코드의 재사용성, 원활한 유지보수(변경 용이), 중복된 코드 제거라는 것을 잊지말쟈
💡응집도와 결합도
- 응집도 : 모듈에 포함된 내부 요소들이 연관되어 있는 정도
하나의 변경을 수용하기 위해 모듈 전체가 함께 변경 -> 응집도가 높은 것- 결합도 : 의존성의 정도, 다른 모듈에 대해 얼마나 많이 관여하고 있는지에 대한 척도
다른 모듈에 대해 자세히 알수록 결합도가 높은 것 -> 서로 연관되어 있기 때문에 결합도가 높을 수록 변경 사항을 수용하기 어렵다.
웹 어플리케이션은 동시에 여러 요청이 들어올 수 있다.
이런 요청이 들어올때마다 새로운 객체를 만들게 되면 당연하게 트래픽이 과도하게 발생되고 이건 성능 저하로 이어질 수 있다.
이런 문제를 해결하려면 객체를 싱글톤(Singleton)으로 관리하면 된다.
🤷♂️ 근데 또 스프링이 아닌 우리가 객체를 싱글톤으로 만들어 사용하게 된다면 어떻게 될까??
객체를 싱글톤으로 만든다 == 객체에 싱글톤 패턴을 적용한다
✔️ 싱글톤(Singleton) 패턴이란?
싱글톤 패턴은 특정 클래스의 인스턴스를 1개만 생성되는 것을 보장하는 디자인 패턴이다.
즉, 생성자를 통해서 여러 번 호출이 되더라도 인스턴스를 새로 생성하지 않고 최초 호출 시에 만들어두었던 인스턴스를 재활용하는 패턴이다.
싱글톤 패턴에는 몇가지 단점이 있는데,
다형성을 이용하지 못한다.
싱글톤 패턴을 사용하면 생성자의 접근 지정자를 private으로 설정해야한다. 왜냐믄 최초로 생긴 인스턴스를 재사용하니까 private을 사용해서 객체 인스턴스를 2개이상 생성하지 못하게 막음.
근데 private 객체는 상속 못하자너 => 다형성을 적용하지 못하게 됨
클래스를 강제 딩크로 만들어버림
단위 테스트가 어렵다.
객체를 싱글톤 패턴으로 구현할 경우, 해당 객체는 공유 객체가 된다. 이렇게 되면 단위테스트를 실행할 때 테스트의 순서에 따라 결과가 달라지게된다.
이런 단점이 있는 싱글톤패턴을 스프링은 당최 어케 사용하고 있는 걸까??
IoC 컨테이너가 빈의 라이프사이클을 관리하는 과정을 알아보면서 이해해보자.
일단 스프링 컨테이너가 생성이 되면, Bean scope가 싱글톤인 객체를 생성한다.
이때 빈으로 등록하기 위해서 어노테이션 기반 혹은 Java 설정 클래스 기반
혹은 xml 기반의 다양한 Configuration 메타데이터를 이용하여 통일된 Bean Definition을 생성한다.
그리고 빈으로 등록할 POJO(Java로 생성하는 순수한 객체)와 Bean Definition 정보를 이용하여 빈을 생성한다.
이 과정에서 싱글톤 패턴을 사용하는 것이 아닌, 평범한 자바 클래스를 이용하여 객체를 생성한다.
IoC 컨테이너는 싱글톤 레지스트리라는 기능도 가지고 있는데, 레지스트리는 Key와 Value 형태로 데이터를 저장하는 방법을 뜻한다. (이건 Spring 뿐만 아니라 CS 전반적으로 쓰이는 개념)
Spring IoC 컨테이너는 Bean scope가 싱글톤인 객체에 빈의 이름을 Key로 객체를 value로 저장한다.
그래서 의존성이 주입되어야하는 객체가 빈으로 등록되어 있을 때, 스프링은 빈의 이름을 이용하여 항상 동일한 Single Object를 반환하게 되는 것이다. 객체가 항상 동일함을 보장하는 것!!
이렇게 빈 객체가 생성이 되면 IOC 컨테이너는 의존 설정을 하는데, 여기서 의존성 자동주입이 일어나게 된다.
그리고 나서 커넥션풀 같은 초기화 과정이 필요한 객체들은 초기화 과정을 거친다.
이 과정이 끝나면 비로소 빈을 사용할 수 있게 된다.
이렇게 Bean scope가 싱글톤인 객체는 스프링 컨테이너가 종료되면 같이 사라진다.
✔️스프링 빈이 왜 필요한가?
✔️스프링 IoC 컨테이너는 왜 빈을 관리 하는 것일까?