티스토리 뷰
스프링 컨테이너
- ApplicationContext를 스프링 컨테이너라고 한다.
- 스프링 컨테이너는 @Configuration 이 붙은 AppConfig 를 설정(구성) 정보로 사용한다. 여기서 @Bean이라 적힌 메서드를 모두 호출해서 반환된 객체를 스프링 컨테이너에 등록한다. 이렇게 스프링 컨테이너에 등록된 객체를 스프링 빈이라고 한다.
- 스프링 빈은 @Bean 이 붙은 메서드의 명을 스프링 빈의 이름으로 사용한다. (applicationContext.getBean()으로 찾을수있다)
스프링 빈 조회 - 상속관계
- 부모 타입으로 조회하면, 자식 타입도 함께 조회한다.
- 그래서 모든 자바 객체의 최고 부모인 Object 타입으로 조회하면, 모든 스프링 빈을 조회한다.
- 1번 : 1,2,3,4,5,6,7
- 2번 : 2,4,5
- 3번 : 3,6,7
- 4번 :4
- 5번 : 5
- 6번 : 6
- 7번 : 7
BeanFactory와 ApplicationContext
BeanFactory
- 스프링 컨테이너의 최상위 인터페이스다.
- 스프링 빈을 관리하고 조회하는 역할을 담당한다.
- getBean()을 제공한다.
- 지금까지 우리가 사용했던 대부분의 기능은 BeanFactory가 제공하는 기능이다.
ApplicationContext
- BeanFactory 기능을 모두 상속 받아서 제공한다.
- 빈을 관리하고 검색하는 기능을 BeanFactory가 제공해준다.
- BeanFactory나 ApplicationContext를 스프링 컨테이너라 한다.
ApplicationCotext가 제공하는 부가기능
- MessageSource <<inteface>>
- 메세지소스를 활용한 국제화 기능 - 예를 들어서 한국에서 들어오면 한국어로, 영어권에서 들어오면 영어로 출력
- EnvironmentCapable <<interface>>
- 환경변수 : 로컬, 개발, 운영등을 구분해서 처리
- ApplicationEventPublisher <<interface>>
- 애플리케이션 이벤트 : 이벤트를 발행하고 구독하는 모델을 편리하게 지원
- ResourceLoader <<interface>>
- 편리한 리소스 조회 : 파일, 클래스패스, 외부 등에서 리소스를 편리하게 조회
싱글톤
- 클래스의 인스턴스가 딱 1개만 생성되는 것을 보장하는 디자인 패턴
- 그래서 객체 인스턴스를 2개 이상 생성하지 못하도록 막아야한다.
- private 생성자를 사용해서 외부에서 임의로 new 키워드를 사용하지 못하도록 막아야한다.
- 싱글톤 패턴을 적용하면 고객의 요청이 올 때마다 객체를 생성하는 것이 아니라, 이미 만들어진 객체를 공유해서 효율적으로 사용 할 수 있다. 하지만 싱글톤 패턴은 다음과 같은 수 많은 문제점들을 가지고 있다.
싱글톤 패턴 문제점
- 싱글톤 패턴을 구현하는 코드 자체가 많이 들어간다.
- 의존 관계상 클라이언트가 구체 클래스에 의존한다. -> DIP를 위반
- 클라이언트가 구체 클래스에 의존해서 OCP원칙을 위반할 가능성이 높다.
- 테스트하기 어렵다.
- 내부 속성을 변경하거나 초기화 하기 어렵다.
- private 생성자로 자식 클래스를 만들기 어렵다.
- 결론적으로 유연성이 떨어진다.
- 안티패턴으로 불리기도한다.
싱글톤 컨테이너
- 스프링 컨테이너는 싱글톤 패턴의 문제점을 해결하면서, 객체 인스턴스를 싱글톤(1개만 생성)으로 관리한다.
- 지금까지 우리가 학습한 스프링 빈이 바로 싱글톤으로 관리되는 빈이다.
- 스프링 컨테이너는 싱글톤 패턴을 적용하지 않아도, 객체 인스턴스를 싱글톤으로 관리하다.
- 스프링 컨테이너는 싱글톤 컨테이너 역할을 한다. 이렇게 싱글톤 객체를 생성하고 관리하는 기능을 싱글톤 레지스트리라고한다.
- 스프링 컨테이너의 이런 기능 덕분에 싱글턴 패턴의 모든 단점을 해결하면서 객체를 싱글톤으로 유지할 수있다.
- 싱글톤 패턴을 위한 지저분한 코드가 들어가지 않아도 된다.
- DIP, OCP, 테스트 private 생성자로 부터 자유롭게 싱글톤을 사용할 수 있다.
싱글톤 적용 전
싱글톤 적용 후
싱글톤 방식의 주의점
- 싱글톤 패턴이든, 스프링 같은 싱글톤 컨테이너를 사용하든, 객체 인스턴스를 하나만 생성해서 공유하는 싱글톤 방식은 여러 클라이언트가 하나의 같은 객체 인스턴스를 공유하기 때문에 싱글톤 객체는 상태를 유지(stateful)하게 설계하면 안된다.
- 무상태(stateless)로 설계해야한다.
- 특정 클라이언트에 의존적인 필드가 있으면 안된다.
- 특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안된다!
- 가급적 읽기만 가능해야한다.
- 필드 대신에 자바에서 공유되지 않는, 지역변수, 파라미터, ThreadLocal 등을 사용해야한다.
- 스프링 빈의 필드에 공유값을 설정하면 정말 큰 장애가 발생할 수 있다!!
@Configuration과 바이트코드 조작
AppConfig@CGLIB 예상
- @Bean이 붙은 메서드 마다 이미 스프링 빈이 존재하면 존재하는 빈을 반환하고, 스프링 빈이 없으면 생성해서 스프링 빈으로 등록하고 반환하는 코드가 동적으로 만들어진다.
- @Confiquration없이 @Bean만 사용해도 스프링 빈으로 등록되지만, 싱글톤을 보장하지 않는다.
컴포넌트 스캔
컴포넌트 스캔 동작 순서
- @ComponentScan은 @Coponent가 붙은 모든 클래스를 스프링 빈으로 등록한다.
- 빈 이름 기본 전략 : MemberServiceImpl 클래스 -> memberServiceImpl
- 빈 이름 직접 지정 : 이름을 직접 지정하고 싶다면 @Component("memberService2") 이런식으로 지정
- @Autowired 의존관계 자동 주입
- 생성자에 @Autowired를 지정하면, 스프링 컨테이너가 자동으로 해당 스프링 빈을 찾아서 주입한다.
- 생성자에 파라미터가 많아도 다 찾아서 자동으로 주입한다.
컴포넌트 스캔 기본 대상
- @Component : 컴포넌트 스캔에서 사용
- @Controller : 스프링 MVC 컨트롤러에서 사용
- @Service : 스프링 비즈니스 로직에서 사용
- @Repository : 스프링 데이터 접근 계층에서 사용
- @Configuration : 스프링 설정 정보에서 사용
의존관계 주입
의존 관계 주입 방법
생성자 주입
- 생성자 호출 시점에 딱 1번만 호출되는 것이 보장된다.
- 주로 불변, 필수 의존관계에 사용
- 생성자가 딱 1개만 있으면 @Autowired를 생략해도 자동 주입된다. 물론 스프링 빈에만 해당한다.
수정자 주입(setter 주입)
- setter라 불리는 필드의 값을 변경하는 수정자 메서드를 통해서 의존관계를 주입하는 방법이다.
- 선택, 변경 가능성이 있는 의존관계에 사용
- 자바빈 프로퍼티 규약의 수정자 메서드 방식을 사용하는 방법이다.
필드 주입
- 외부에서 변경이 불가능해서 테스트 하기 힘들다는 치명적인 단점이 있다.
- DI 프레임워크가 없으면 아무것도 할 수 없다.
- 사용하지말자 하지만
- 애플리케이션의 실제 코드와 관계없는 테스트 코드
- 스프링 설정을 목적으로 하는 @Configuration 같은 곳에서만 특별한 용도로 사용
일반 메서드 주입
- 한번에 여러 필드를 주입 받을 수 있다.
- 일반적으로 잘 사용하지 않는다.
* 참고 * 의존관계 자동주입은 스프링 컨테이너가 관리하는 스프링 빈이어야 동작한다. 스프링 빈이 아닌 클래스에서 @Autowired 코드를 적용해도 아무 기능도 동작하지 않는다.
생성자 주입을 쓰자!!!
- 불변
- 대부분의 의존관계 주입은 한번 일어나면 애플리케이션 종료시점까지 의존관계를 변경할 일이 없다. 오히려 대부분의 의존관계는 애플리케이션 종료 전까지 변하면 안된다.(불변해야한다.)
- 수정자 주입을 사용하면 setXxx 메서드를 public으로 열어두어야한다.
누군가 실수로 변경할 수 도있고, 변경하면 안되는 메서드를 열어두는 것은 좋은 설계방법이 아니다. - 생성자 주입은 객체를 생성할 때 딱 1번만 호출되므로 이후에 호출되는 일이 없다. 따라서 불변하게 설계 할 수 있다.
- 누락
- 생성자 주입을 사용하면 주입 데이터를 누락했을 때 컴파일 오류가 발생한다.
- final 키워드
- 생성자 주입을 사용하면 final 키워드를 사용할 수 있다. 그래서 생성자에서 혹시라도 값이 설정되지 않는 오류를 컴파일 시점에 막아준다.
- *참고* 수정자 주입을 포함한 나머지 주입방식은 모두 생성자 이후에 호출되므로, 필드에 final키워드를 사용할 수 없다. 오직 생성자 주입방식만 final 키워드를 사용 할수 있다.
정리
- 생성자 주입 방식을 선택하는 이유는 여러가지가 있지만, 프레임워크에 의존하지 않고, 순수한 자바 언어의 특징을 잘 살리는 방법이기도 하다.
- 기본으로 생성자 주입을 사용하고, 필수 값이 아닌 경우에는 수정자 주입 방식을 옵션으로 부여하면 된다. 생성자 주입과 수정자 주입을 동시에 사용할 수 있다.
- 최근에는 생성자를 딱 1개 두고 @Autowired를 생략하는 방법을 주로 사용한다. 여기에 Lombok라이브러리의 @RequiredArgsConstructor 함께 사용하면 기능은 다 제공하면서, 코드는 깔끔하게 사용 할 수 있다.
'Spring(스프링) > 스프링 공부 메모장' 카테고리의 다른 글
스프링부트 공부 일지 3 (0) | 2023.06.14 |
---|---|
스프링부트 공부 일지 1 ( 스프링, 다형성, SOLID, IoC, DI, DI컨테이너) (0) | 2023.05.28 |