이전 내용
https://velog.io/@limjongheok/%ED%86%A0%EB%B9%84%EC%9D%98-%EC%8A%A4%ED%94%84%EB%A7%81-1.11.3
이전내용을 통해 전략패턴을 통한 효율적인 UserDao 분할을 하며 개방 패쇄 원칙을 간단히나마 알아보았다.
우선 이전내용에서 UserDao 를 효율적이게 분할을해 UseDao 는 Connection을 이용해 mysql 쿼리를 넣는 책임만지는 형식의 코드를 짰었다. (Connection 이 어떻게 생성되는지는 관심도 없고 책임도 없음)
근데 이전 장에서 놓친게 하나 있다. 바로 UserDaoTest
public void UserDao6DTest(){
ConnectionMaker2 connectionMaker2 = new DConnectionMaker2();
UserDao6 userDao6 = new UserDao6(connectionMaker2);
}
다음과 같이 UserTest 는 Connection과 UserDao 를 생성하고 연결시켜주는 역할을 하고있다.
과연 UserTest 과 이 역할을 하는것이 맞을까?
아니다 UserTest는 test 하는 역할이다.
그럼 어떠한 방식으로 해결할 것인가?
팩토리를 통해 객체 생성 및 연결을 하겠다.
크게 다른건 아니고 Factory라는 클래스를 만들고 위 코드를 그대로 쓰는 것이다.
public class Factory{
public UserDao UserDao(){
ConnectionMaker2 connectionMaker2 = new DConnectionMaker2();
UserDao6 userDao = new UserDao6(connectionMaker2);
return userDao;
}
}
그리고 test는 test 의 목적으로만 사용할 것이다.
이럼 객체를 생성하는 쪽과 생성한 것을 사용하는 것 이 깔끔하게 분리된다.
분리된 오브젝트들의 역할과 관계를 분석해 보자면
UserDao 와 ConnectionMaker는 애플리케이션 데이터 로직과 기술 로직을 담당하고 있고ㅓ,
DaoFactory는 이런 애플리케이션 오브젝트를 구성하고 관계를 정의하는 책임을 맡고 있음을 알수있다.
즉 UserDao,ConnectionMaker 는 실질적 로직 담당 컴포넌트
DaoFactory는 애플리케이션 구성 정의하는 설계도

즉 그림을 보듯 client는 설계도에 생성및 설계 요청을 보내 사용하면 된다
그럼 만약 DaoFactory 에 UserDao 가 아닌 다른 Dao 생성 기능을 넣음 어떻게 될까?
public class Factory{
public UserDao UserDao(){
UserDao6 userDao = new UserDao6(new connectionMaker2());
return userDao;
}
public AccountDao accoutDao(){ // AccountDao 추가
return new AccountDao(new DConnectionMaker());
}
}
허나 이럴시 connectionMaker 가 변하면 관련 모든 Dao 메소드가 변해야한다 ** 중복코드 발생
자주 변화하는 코드 분리하기
객체지향 원칙에 따라 자주 변화하는 코드를 분리하였다.
public class Factory{
public UserDao UserDao(){
UserDao6 userDao = new UserDao6(new connectionMaker2());
return new UserDao6(connect());
}
public AccountDao accoutDao(){ // AccountDao 추가
return new AccountDao(connect());
}
public Connection connect(){
return new DConnectionMaker()
}
}
프로그램의 제어 흐름 구조가 뒤바뀌는 것
일반적으로는 main -> UserDao -> ConnectionMaker()
class Main{
pbulic static void main(String[] args){
Dao userDao = new UserDao() //1
}
}
class UserDao implements Dao{
Connection connection = new NConnection(); //2
}
class NConnection implements Connection{}
다음과 같은 구조를 띈다
허나 제어의 역전에서는 오브젝트가 자신이 사용할 오브젝트를 스스로 선택하지 않는다 또 자신도 어떻게 만들어 지고 사용되는지 알 수 없다
class Main{
pbulic static void main(String[] args){
Factory factory = new Factory();
factory.doa();
}
}
class Factory{
public UserDao dao(){
Connection connection = new NConnection();
Dao userDao = new UserDao(connection);
}
} // 팩토리 클래스 추가
class UserDao implements Dao{
Connection connection;
public UserDao(Connection connection){
this.connection = connection;
}
Connection connection = new NConnection(); //2
}
class NConnection implements Connection{}
다음과 같이 UserDao는 Connection 이 어떻게 생성되는지 관심 및 책임이 없음

기존에는 다음과 같았다면

다음과 같이 생성 과 사용 이 분배되는 것을 볼 수있다.
프레임워크도 제어의 역전 개념이 적용된 대표적인 기술이다.
스프링에서는 스프링이 제어권을 가지고 직접 관계를 부여하는 오브젝트를 빈 이라고 부른다.
오브젝트 단위의 애플리케이션 컴포넌트를 말한다
스프링 빈은 스프링 컨테이너가 생성과 고나계 설정 사용등을 제어해주는 제어의 역전이 적용된 오브젝트를 가리키는 말이다.
스프링에서는 빈의 생성과 관계 설정 같은 제어를 담당하는 Ioc 오브젝트를 빈 팩토리라 부른다. ( 애플리케이션 컨택스트라 주로 불림)
@Configuration // 애플리케이션 컨텍스트 또는 빈 팩토리가 사용할 설정 정보 표시
public class Factory{
@Bean // 오브젝트 생성을 담당하는 Ioc 용 메소드라는 표시
public UserDao UserDao(){
ConnectionMaker connectionMaker2 = new DConnectionMaker2();
UserDao6 userDao = new UserDao6(connectionMaker2);
return userDao;
}
}
public class UserDaoTest{
public static void main(String[] args) throws ClassNotFoundException,SQLException{
ApplicationContext context = new AnnotationConfigApplicationContext(DaoFactory.class);
UserDao dao = context.getBean("userDao",UserDao.class);
}
}
@Bean // 오브젝트 생성을 담당하는 Ioc 용 메소드라는 표시
public specialUserDao UserDao(){
ConnectionMaker connectionMaker3 = new DConnectionMaker3();
UserDao6 userDao = new UserDao6(connectionMaker3);
return userDao;
}
뭐 이런식으로 생성 방식이 다를 수도 있으니 이름을 설정해 getBean() 으로 가져오는 것 같다
기존 방식은 Factory 방식은 오브젝트 팩토리 방식이다 이런 오브젝트 팩토리 방식과 애플리케이션 컨텍스트를 사용한 방식을 비교해 보자
앞서 본 애플리케이션 컨텍스트는 ApplicationContext 인터페이스를 구현하는데 ApplicationContext는 빈 팩토리가 구현하는 BeanFaoctory 인터페이스를 상속했으므로 일종의 빈 팩토리인 샘이다
오브젝트 팩토리는 UserDao 를 비롯한 DAO 오브젝트를 생성하고 DB 생성 오브젝트와 관계를 맺어주는 제한 적인 역할에 반해
애플리케이션 컨텍스트는 애플리케이션에서 IOC 를 적용하는 모든 오브젝트 대한 생성 관계를 담당한다
ApplicationContext에는 DaoFactory 와 달리 직접 오브젝트를 생성하고 관계를 맺어주는 코드가 없고 , 그런 생성정보와 연관 관계 정보를 별도의 설정 정보를 통해 얻는다.
@Configuration이 붙은 DaoFactory는 애플리케이션 컨텍스트가 활용하는 IOC 설정정보다

다음과 같이 DaoFactory 의 userDao() 메소드를 호출해서 오브젝트를 가져온 것을 클라이언트가 getBean()으로 요청할때 전달해 준다.
애플리케이션 컨텍스트는 DaoFactory 클래스를 설정정보로 등록해 두고 @Bean 이 붙은 메소드의 이름을 가져와 빈 목록을 만들어 준다.
클라이언트가 애플리케이션 컨텍스트의 getBean() 메소드를 호출시 자신의 빈 목록에서 요청한 이름이 있는지 찾고 있다면 빈을 생성하는 메소드를 호출해 오브젝트를 생성 후 클라이언트에 돌려준다.
빈
IOC 방식으로 관리하는 오브젝트라는 뜻
스프링에서 직접 생성과 제어를 담당하는 오브젝트만이 빈이라고 한다.
빈 팩토리
IOC 담당하는 핵심 컨테이너
빈을 등록하고 생성하고 조회하고 돌려주고 그외 부가적인 빈 관리 기능 담당
보통 빈팩토리를 바로 사용하지 않고 애플리케이션 컨텍스트를 이용
애플리케이션 컨텍스트
빈팩토리를 확장한 IOC 컨테이너 , 빈을 등록 관리하는 빈 팩토리 기능과 각종 부가 서비스를 추가로 제공
빈 팩토리는 빈 생성 및 제어 관점에서 이야기하고
애플리케이션 컨텍스트는 스프링이 제공하는 애플리케이션 지원 기능 모두 포함해서 이야기하는것
설정 정보/ 메타 정보
애플리케이션 컨텍스트 또는 빈 팩토리가 IOC 를 적용하기 위해 서용하는 메타 정보
configuration 구성 정보 내지 형상정보라는 의미
IOC 컨테이너에 의해 관리되는 애플리케이션 오브젝트를 생성하고 구성할때 사용
애플리케이션 형상 정보라 부른다.
IOC 컨테이너
IOC 방식으로 빈을 관리한다는 의미
애플리케이션 컨텍스트나 빈 팩토리를 컨테이너 혹은 IOC 컨테이너라 부른다.
Ioc 컨테이너 애플리케이션 컨텍스트를 포함해서 스프링이 제공하는 모든 기능을 통틀어 말할때 주로 사용
애플리케이션 컨텍스트는 기존 직접 만들었던 오브젝트 팩토리와 중요한 차이점이 있다.
DaoFactory factory = new DaoFactory();
UserDao dao1 = factory.UserDao();
UserDao dao2 = factory.UserDao();
System.out.println(dao1);
System.out.println(dao2);
이렇게 기존 오브젝트를 각각 생성하여 출력시

각기 다른 값을 가진 오브젝트가 생성되는 것을 볼 수있다.
그럼 애플리케이션 컨텍스트는 어떨까
ApplicationContext context =new AnntationConfigApplicationContext(DaoFactory.class);
UserDao dao3 = context.getBean("userDao",UserDao.class);
UserDao dao4 = context.getBean("userDao",UserDao.class);
System.out.println(dao3);
System.out.println(dao4);

아래와 같이 동일하다는 사실을 알수있다.
이것은 오브젝트 팩토리와 애플리케이션 컨텍스트 동작 방식의 차이인데
애플리케이션 컨텍스트는 여러번에 걸쳐 빈을 요청하더라도 매번 동일한 오브젝트를 돌려준다는 것이다.
getBean()을 실행할때마다 userDao() 메소드를 호출하고 매번 new 에 의해 새로운 UserDao가 만들어지지 않는다.
애플리케이션 컨텍스트는 오브젝트 팩토리와 비슷한 방식으로 동작하는 IOC 컨테이너이며 동시에 싱글톤을 저장하고 관리하는 싱글톤 레지스트리 이기도 하다.
스프링은 기본적으로 별다른 설정을 하지 않으면 내부에서 생성하는 빈 오브젝트를 모두 싱글톤으로 만든다.
엔터프라이즈 분야에서는 서비스 오브젝트라는 개념을 일찍부터 사용해 왔다.
서블릿은 자바 엔터프라이즈 기술의 가장 기본이 되는 서비스 오브젝트라 하며 , 스펙에서 강제하진 않지만 서블릿은 대부분 멀티스레드 환경에서 싱글톤으로 동작한다.
서블릿 클래스당 하나의 오브젝트만 만들어 주고 사용자의 요청을 담당하는 여러 스레드에서 하나의 오브젝트를 공유해 동시 사용한다.
싱글톤 패턴 정리
https://velog.io/@limjongheok/%EC%8B%B1%EA%B8%80%ED%86%A4-%ED%8C%A8%ED%84%B4
때문에 싱글톤 패턴은 상속할 수 없고
테스트 하기 힘들다
또한 서버환경(멀티 스레드 환경) 에서는 하나만 만들어지는 것을 보장 못하며
전역상태를 만들 수 있기 때문에 바람직 하지 못하다.
스프링은 서버 환경에서 싱글톤이 만들어져서 서비스 오브젝트 방식으로 사용되는 것은 적극 지지하지만 자바의 기본적인 싱글톤 패턴의 구현 방식은 여러 단점이 있기 때문에 스프링은 직접 싱글톤 형태의 오브젝트를 만들고 관리하는 기능을 제공한다.
이것이 바로 싱글톤 레지스트리
스프링 컨테이너는 싱글톤을 생성하고 관리하고 공급하는 싱글톤 관리 컨테이너이기도 하다.
싱글톤 레지스트리로 private 생성자가 아닌 평범한 자바 클래스를 싱글톤으로 활용하게 해준다.
자바 클래스라도 IOC 방식의 컨테이너를 사용해 관계 설정 제어권을 컨테이너에게 넘기면 손쉽게 싱글톤 방식으로 만들어져 관리할 수 잇게 된다
오브젝트 생성에 관한 모든 권한은 Ioc 기능을 제공하는 애플리케이션 컨텍스트에 있기 때문
가장 중요한 것은 싱글톤 패턴과 달리 스프링이 지지하는 객체지향적 설계 원칙 디자인 패턴 등을 적용하는데 아무런 제약이 없다.
스프링은 Ioc 컨테이너 뿐만 아니라 고전적인 싱글톤 패턴을 대신해 싱글톤을 만들고 관리해주는 레지스트리 이다.
잘읽었어요~~