스프링 핵심 원리 - 기본편2
아래 글은 김영한님의 인프런 강의를 참고해서 만들었습니다.
1. 싱글톤 컨테이너
1.1 웹 애플리케이션과 싱글톤
- 기존 환경에서는 고객 요청이 들어올때마다 새로운 객체를 반환한다. → 비효율적
- 싱글톤으로 작성이 필요하다.
1.2 싱글톤 패턴
- 클래스의 인스턴스가 딱 1개가 생성되는 것을 보장하는 디자인 패턴
- 만드는 방법
static
영역에 객체를 미리 하나 생성해 둔다.- 이 객체 인스턴스가 필요하면
getter
로 조회를 한다. ← 항상 같은 객체 반환 - 생성자를
private
으로 막아서 외부에서new
키워드로 생성되지 않게 한다.
- 싱글톤 패턴의 문제점
- 싱글톤 패턴을 구현하는 코드가 많다.
- 의존관계에서 클라이언트가 구체 클래스에 의존 → DIP 위반
- OCP 위반할 가능성이 높다.
- 테스트하기 어렵다.
- 내부 속성 변경이나 초기화하기 어렵다.
private
생성자를 사용하므로 자식 클래스를 만들기 어렵다.- 유연성이 떨어진다.
1.3 싱글톤 컨테이너
- 스프링 컨테이너는 싱글톤 패턴의 문제점을 해결하면서, 객체 인스턴스를 싱글톤으로 관리한다.
- → How? 스프링 빈
- 싱글톤 레지스트리 : 싱글톤 객체를 생성하고 관리하는 기능(스프링 컨테이너)
1.4 싱글톤 방식의 주의점
- 싱글톤 방식은 여러 클라이언트가 하나의 같은 객체 인스턴스를 공유하기 때문에 상태를 유지(stateful)하게 설계하면 안된다.
- 무상태(stateless)로 설계해야 한다.
- 특정 클라이언트에 의존적인 필드가 있으면 안된다.
- 특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안된다.
- 가급적 읽기만 가능해야 한다.
- 필드 대신 자바에서 공유되지 않는 지역변수, 파라미터, ThreadLocal등을 사용해야 한다.
- 스프링 빈의 필드에 공유 값을 설정하면 안된다.
- 무상태(stateless)로 설계해야 한다.
1.5 @Configuration
과 싱글톤
- 스프링은 어떤 방법으로 싱글톤을 보장을 할까?
@Configuration
을 사용하여, CGLIB라는 바이트코드 조작 라이브러리를 이용한다.- 우리가 만든
AppConfig
에 바이트코드 조작 라이브러리를 사용하여AppConfig@CGLIB
를 만들어 스프링 빈으로 등록을 한다.
- 우리가 만든
@Bean
만 사용해도 스프링 빈으로 등록이 되지만, 싱글톤은 보장하지 않는다.
2. 컴포넌트 스캔
2.1 컴포넌트 스캔과 의존관계 자동 주입
- 설정 정보가 적을 때는
@Bean
을 일일이 입력해도 괜찮지만, 양이 많아지면 문제→ 의존관계도 자동으로 주입해주는@Autowired
기능 제공 - → 스프링은 설정 정보가 없어도 자동으로 스프링 빈을 등록해주는 컴포넌트 스캔 기능을 제공
2.1.1 컴포넌트 스캔과 의존관계 주입 과정
@ComponentScan
은@Component
가 붙은 모든 클래스를 스프링 빈으로 등록
- 빈 이름은 클래스명에서 맨 앞글자를 소문자로 바꿔서 사용한다.
- 이름 변경이 필요할 땐
@Component("이름")
을 사용하면 된다.
- 생성자에
@Autowired
를 붙이면, 스프링 컨테이너가 자동으로 해당 스프링 빈을 찾아서 주입한다.
2.2 컴포넌트 스캔의 탐색 위치와 기본 스캔 대상
2.2.1 탐색 위치
basePackages
: 탐색할 패키지의 시작 위치를 지정한다, 지정하지 않으면@ComponentScan
이 붙은 설정 정보 클래스의 패키지가 시작 위치가 된다.- 설정 정보 클래스의 위치를 프로젝트 최상단에 두고 따로 시작 위치를 지정하지 않고 사용하는것을 권장한다.
2.2.2 기본 스캔 대상
- 컴포넌트 스캔은
@Component
,@Controller
,@Service
,@Repository
,@Configuration
을 스캔 대상으로 포함한다.@Controller
: 스프링 MVC 컨트롤러로 인식@Repository
: 스프링 데이터 접근 계층으로 인식, 데이터 계층의 예외를 스프링 예외로 변환@Service
: 특별한 처리 없음, 개발자에게 비즈니스 로직이 존재한다고 알리는 역할
2.3 필터
includeFilters
: 컴포넌트 스캔 대상을 추가로 지정excludeFilters
: 컴포넌트 스캔에서 제외할 대상을 지정- ex)
@ComponentScan(includeFilters = @FilterType.타입, classes = 클래스명)
- ex)
FilterType
옵션ANNOTATION
: 기본값, 애노테이션을 인식ASSIGNABLE_TYPE
: 지정한 타입과 자식 타입을 인식ASPECTJ
: AspectJ 패턴 사용REGEX
: 정규 표현식 사용CUSTOM
:TypeFilter
라는 인터페이스를 구현해서 처리
2.4 중복 등록과 충돌
- 자동 빈 등록 vs 자동 빈 등록
ConflictingBeanDefinitionException
예외 발생
- 수동 빈 등록 vs 자동 빈 등록
- 수동 빈 등록이 우선권을 가지게 된다. → 수동 빈이 자동 빈을 오버라이딩 한다.
- 스프링 부트는 오류가 나게 설정되어있다.
3. 의존관계 자동 주입
3.1 다양한 의존관계 주입 방법
- 생성자 주입
- 수정자(setter) 주입
- 필드 주입
- 일반 메서드 주입
3.1.1 생성자 주입
- 생성자를 통해서 의존 관계를 주입하는 방법
- 생성자는 호출시점에 1번만 호출되기 때문에 불변, 필수일 때 사용한다.
- 컴포넌트 스캔을 해서 클래스를 스프링 빈으로 등록을 하려 할 때, 생성자에
@Autowired
가 붙어있는 경우- 생성자가 1개가 있으면
@Autowired
생략가능
- 생성자가 1개가 있으면
3.1.2 수정자(setter) 주입
- 수정자를 통해서 의존 관계를 주입하는 방법
- 생성자 주입 후에 수정자 주입이 발생한다.
- 선택, 변경일 때 사용
- 선택일 때
@Autowired(required = false)
로 설정한다.
- 선택일 때
setter
에@Autowired
가 붙은 경우
3.1.3 필드 주입
- 필드에 바로 주입하는 방법
@Autowired
를 인스턴스 변수에 붙이는 방법- 코드가 간결해 진다.
- 외부에서 변경하기 힘들어서, 테스트하기가 어렵다. → 사용X(테스토 코드에서는 사용 가능)
3.1.4 일반 메서드 주입
- 일반 메서드를 통해서 주입을 받을 수 있다. → 스프링 빈이어야지 동작
- 한번에 여러 필드를 주입 가능
- 일반적으로 잘 사용하지 않는다.
@Autowired
가 일반 메서드에 붙어 있는 경우
3.2 옵션 처리
- 옵션 처리는 주입할 스프링 빈이 없어도 동작해야 할 때 사용한다.
@Autowired(required = false)
: 자동 주입할 대상이 없으면(스프링 빈이 아니면) 메서드 호출X@Nullable
: 자동 주입 대상이 없으면 null이 입력Optional<>
: 자동 주입할 대상이 없으면 Optional.empty가 입력- @Nullable, Optional<>은 모두 메서드 매개변수 앞에서 선언
3.3 생성자 주입이 필요한 이유
- 불변하기 때문이다.
- 수정자 주입과 달리 객체를 생성할 때 1번만 호출되므로 불변하게 설계할 수 있다.
- 생성자 누락도 막아줄수 있다.(
final
)
- 누락되지 않게 설계가 가능하다.
- 프레임워크 없이 순수한 자바 코드를 단위 테스트 하는 경우에 누락이 있으면 IDE에서 컴파일 오류를 발생시킨다.
따라서 생성자 주입을 쓰되, 필요하면 수정자 주입을 사용한다. 필드 주입은 X
3.4 롬복
- 라이브러리로써 생성자, GETTER, SETTER등을 어노테이션으로 삽입할 수 있게 도와준다.
- 적용하는 법
- build.gradle에 라이브러리와 환경 추가
//lombok 설정 추가 시작 configurations { compileOnly { extendsFrom annotationProcessor } } //lombok 설정 추가 끝 //dependencies에 추가, lombok 라이브러리 추가 시작 compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' testCompileOnly 'org.projectlombok:lombok' testAnnotationProcessor 'org.projectlombok:lombok' //lombok 라이브러리 추가 끝
@Getter
,@Setter
를 통해서 Getter와 Setter 생성@RequiredArgsConstructor
:final
이 붙은 인스턴스 변수의 생성자를 만들어 줌.
3.5 조회 빈이 2개 이상일 때 해결법
@Autowired
가 기본적으로 type으로 조회를 하기 때문에 같은 타입의 빈이 2개가 있으면 예외(NoUniqueBeanDefinitionException
)가 발생한다.
해결방법
@Autowired
필드 명 매칭@Qualifier
→@Qualifier
끼리 매칭 : 빈 이름 배칭@Primary
사용
3.5.1 @Autowired
필드 명 매칭
@Autowired
는 처음에 타입 매칭을 시도하고, 여러 빈이 존재하면 필드 이름이나 파라미터 명으로 추가 매칭한다.- 따라서 필드 이름이나 파라미터 명을 적절히 고쳐서 사용한다.
3.5.2@Qualifier
→ @Qualifier
끼리 매칭
- 이름이 같은 컴포넌트에
@Qualifier("이름")
을 붙여준 후에, 사용하는 생성자나 수정자에@Qualifier("이름")
을 붙여서 사용한다.
3.5.3 @Primary
사용
- 자주 사용하는 방법
@Primary
를 사용한 컴포넌트가 우선권을 가진다@Primary
와@Qualifier
가 둘 다 있을경우@Qualifier
가 우선순위를 가진다.
3.6 조회한 빈이 모두 필요할 때(List, Map)
- 인스턴스 변수를 Map과 List를 이용해서 설정할 수 있다.
- Map과 List를 이용하여 생성자 주입을 하면, 테스트를 할 때 우리가 원하는 값(정책)을 골라서 사용 할 수 있다(예제에서는 fix와 rate 모두를 주입받고, 원하는 정책을 고를 수 있었다.).
3.7 자동, 수동의 올바른 실무 운영 기준
- 자동 빈 등록을 기본으로 사용
- 애플리케이션에 광범위하게 영향을 미치는 기술 지원 객체는 수동 빈으로 등록
4. 빈 생명주기 콜백
4.1 빈 생명주기 콜백
- 스프링 빈의 이벤트 라이프 사이클(싱글톤)→ 초기화나 종료시 중요(초기화나 종료 시점 알아야 하기 때문에)
- 스프링 컨테이너 생성
- 스프링 빈 생성
- 의존관계 주입
- 초기화 콜백
- 사용
- 소멸전 콜백(소멸되기 직전)
- 스프링 종료
4.2 빈 생명주기 콜백 방법
4.2.1 인터페이스(InitializingBean
, DisposableBean
)
- 초기화(
InitializingBean
) , 소멸(DisposableBean
) 인터페이스를 구현시켜 준다. - 단점
- 스프링 전용 인터페이스에 의존
- 메서드의 이름 변경X
- 외부 라이브러리에 적용X
- 거의 사용하지 않는다
4.2.2 빈 등록 초기화, 소멸 메서드
- 설정 정보에
@Bean(initMethod = "init", destroyMethod = "close")
같은 방법으로 지정 - 메서드 이름 변경 가능
- 스프링 빈이 스프링 코드에 의존하지 않는다
- 외부 라이브러리에도 사용 가능
destroyMethod
는 기본값이(inferred)
이기 때문에close
,shutdown
이름의 메서드를 자동으로 찾아서 종료 시점에 호출해 준다. → 따로 안적어도 됨
4.2.3 애노테이션(@PostConstruct
, @PreDestory
)
- 애노테이션을 메서드에 붙이기만 하면 된다.
- 최신 스프링에서 권장하는 자바 표준 방법
- 외부라이브러리에 적용X → 빈 등록 초기화, 소멸 메서드를 사용
5. 빈 스코프
5.1 빈 스코프란?
- 빈이 존재할 수 있는 범위를 의미
- 싱글톤 : 기본 스코프로써 스프링 컨테이너의 시작과 종료까지 유지
- 프로토타입 : 스프링 컨테이너가 프로토타입 빈의 생성과 의존관계 주입까지만 관리
- 웹 관련 스코프
- request : 웹 요청이 들어오고 나갈때 까지 유지
- session : 웹 세션이 생성되고 종료될 때 까지 유지
- application : 웹의 서블릿 컨텍스트와 같은 범위로 유지
@Bean
이나@Component
위에@Scope("범위")
를 입력하면 된다.
5.2 프로토타입 스코프
- 스프링 컨테이너에서 조회하면 조회할 때 마다 항상 새로운 인스턴스를 생성해서 반환해준다. → 프로토타입 빈을 생성하고, 의존관계 주입, 초기화까지만 처리
- 프로토타입 빈을 관리할 책임은 프로토타입 빈을 받은 클라이언트에 있다. →
@PreDestory
같은 종료 메서드 실행X, 직접 입력하면 됨
5.2.1 싱글톤 빈과 함께 사용시 문제점
- 싱글톤 빈이 프로토타입 스코프를 주입 받으면 싱글톤 빈 처럼 행동한다. → 직접 필요한 의존관계를 찾아야 한다.(의존관계 조회(Dependency Lookup, DL))
- 해결방법
ObjectProvider
를 이용하여getObject()
로 꺼낸다.(스프링에 의존)@Autowired private ObjectProvider<PrototypeBean> prototypeBeanProvider; public int logic() { PrototypeBean prototypeBean = prototypeBeanProvider.getObject(); prototypeBean.addCount(); int count = prototypeBean.getCount(); return count; }
- ex)
- JSR-330 Provider를 이용하여
get()
으로 꺼낸다.(자바 표준)javax.inject:javax.inject:1
을 gradle에 추가 하여Provider<>
를 사용
- 해결방법
5.3 웹 스코프
- 웹 환경에서만 동작한다.
- 프로토타입과 다르게 스프링이 종료시점까지 관리를 해준다.(종료 메서드 호출됨)
- 종류
- request : HTTP 요청 하나가 들어오고 나갈 때 까지 유되는 스코프, HTTP 요청마다 생성
- session : HTTP Session과 동일한 생명주기를 가짐
- application : 서블릿 컨텍스와 동일한 생명주기를 가짐
- websocket : 웹 소켓과 동일한 생명주기를 가짐
'FRAMEWORK > [SPRING]' 카테고리의 다른 글
[Spring] HttpMessageConverter (0) | 2022.06.06 |
---|---|
[Spring] Spring MVC 구조 (0) | 2022.06.05 |
[Spring] CGI vs Servlet (0) | 2022.06.03 |
[Spring] Web Server vs WAS (0) | 2022.06.02 |
스프링 핵심 원리 - 기본편1 (0) | 2021.08.14 |