스프링 핵심원리
객체 지향 설계와 스프링
- 스프링은 좋은 객체 지향 애플리케이션을 개발할 수 있게 도와주는 프레임 워크
- 다형성
- 역할과 구현 구분:공연-역할(인터페이스)-배우(바꿀 수 있음,구현객체)
- 객체 인스턴스 실행시점 유연하게 변경
- 클라이언트 변경 x
- 객체 지향 설계 5가지 원칙(solid)
- 단일 책임(srp): 하나의 책임-변경의 파급효과 적게
- 개방-폐쇄(ocp): 확장에 열려있고 변경(클라이언트)에 닫힘(설정자 필요), 할인정책(고정, 정률)
- 리스코프 치환(lsp): 정확성 유지 하위타입 인스턴스로 바꿀 수 있어야 함(자동차 엑셀은 앞으로)
- 인터페이스 분리(isp): 특정 클라이언트 위한 인터페이스 여러개 > 범용
- 의존관계 역전(dip): 추상화(인터페이스,역할) 의존o, 구체화x
- 스프링 di(의존성 주입), di컨테이너 통해 ocp,dip 가능
스프링 핵심 원리
DIP 위반(구체화 의존), srp 위반(구현객체 생성,연결,실행),OCP 위반(클라이언트 변경)
private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
- 관심사 분리 필요-> 기획자 AppConfig 구현 객체 생성, 생성자 통해 주입
- ioc(제어의 역전): 구현 객체는 자신의 로직을 실행 ,프로그램에 대한 제어
AppConfig -> 프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것 - DI(의존관계 주입): 실행 시점에 외부에서 실제 구현 객체를 생성하고 클라이언트에 전달 -> 클라이언트와 서버의 실제 의존관계가 연결 되는 것
- DI 컨테이너(IoC 컨테이너): AppConfig 처럼 객체를 생성하고 관리하면서 의존관계를 연결해 주는 것
- 스프링전환: AppConfig에 설정 구성(@Configuration), 스프링 컨테이너(ApplicationContext)에 스프링 빈으로 등록 (메서드에 @Bean, 메서드이름=스프링빈이름)
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
MemberService memberService = applicationContext.getBean("memberService", MemberService.class);
스프링 컨테이너와 스프링 빈
- 스프링 컨테이너 생성: new AnnotationConfigApplicationContext(AppConfig.class), AppConfig.class(구성정보)
- 스프링 빈 이름: 메서드 이름, 직접 @Bean(name="memberService2"), 빈 이름은 항상 달라야 함
- 빈 조회
- 컨테이너에 등록된 모든 빈 조회: ac.getBeanDefinitionNames(), ac.getBean(beanDefinitionName)
- ac.getBean(빈이름, 타입), ac.getBean(타입)
- 같은 타입 빈 둘이상 오류발생-> 빈 이름 지정
- 특정 타입 모두 조회 ac.getBeansOfType()
- 부모타입 조회 시 자식도 조회
- ApplicationContext(BeanFactory상속)
- 국제화 기능, 환경변수, 애플리케이션 이벤트, 리소스 조회
- 설정 형식: 자바, xml, groovy->BeanDefinition(스프링 빈 설정 메타 정보)
싱글톤 컨테이너
- 스프링 없는 순수한 DI 컨테이너 요청할 때 마다 객체 생성 -> 객체 1개 생성 공유하는 싱글톤 패턴 적용 필요
- 싱글톤 패턴 문제점: 구현 코드 많음, 구체 클래스 의존, 테스트 어렵, 내부속성 변경/초기화 어렵, 자식 클래스 만들기 어렵, 유연성 떨어짐 -> 싱글톤 컨테이너로 해결
- 싱글톤 방식 주의점: 무상태로 설계해야함(지역변수, 파라미터, ThreadLocal)
- @Configuration과 바이트코드 조작(CGLIB): 싱글톤 보장
컴포넌트 스캔
- 컴포넌트 스캔: @ComponentScan을 설정 정보에 붙여주면 @Component 애노테이션이 붙은 클래스를 스캔해서 스프링 빈을 등록
- 빈이름: 클래스명 맨 앞글자 소문자
- @Autowired: 타입이 같은 빈을 찾아 의존관계 자동으로 주입
- 컴포넌트 스캔 기본 대상
- @Component
- @Controlller: MVC 컨트롤러로 인식
- @Service: 개발자들이 핵심 비즈니스 로직으로 인식
- @Repository: 데이터 접근 계층으로 인식
- @Configuration: 스프링 설정 정보로 인식, 싱글톤을 유지
- 필터
- includeFilters : 컴포넌트 스캔 대상을 추가
- excludeFilters : 컴포넌트 스캔에서 제외
- 중복 등록과 충돌(같은 빈 이름): 자동 빈 등록끼리 ConflictingBeanDefinitionException, 수동>자동, 스프링 부트는 오류
의존관계 자동 주입
- 생성자 주입: 생성자 호출시점에 딱 1번 호출, 불변, 필수 의존관계
- 수정자 주입(setter 주입): 선택, 변경 가능성이 있는 의존관계
- 필드 주입: 외부에서 변경 불가, 테스트 하기 힘듦
@Autowired private MemberRepository memberRepository;
- 일반 메서드 주입: 한번에 여러 필드를 주입
- 옵션 처리
- @Autowired(required=false): 자동 주입할 대상 없으면 수정자 메서드 호출 안됨
- org.springframework.lang.@Nullable: 자동 주입할 대상 없으면 null 입력
- Optional<>: 자동 주입할 대상이 없으면 Optional.empty 입력
- 생성자 주입 권장
- 불변: 의존관계를 변경할 일이 없다. 불변해야 한다.
- 누락: 수정자 의존관계 -> 의존관계 주입 누락 -> Null Point Exception / 생성자 주입 final 키워드 컴파일 오류로 알 수 있음
- 롬복의 @RequiredArgsConstructor: final이 붙은 필드 생성자 만들어줌
- 최근: 생성자 1개, @Autowired 생략, @RequiredArgsConstructor
조회 빈이 2개 이상
- @Autowired 는 타입(Type)으로 조회->NoUniqueBeanDefinitionException
- @Autowired 필드 명 빈이름으로(타입매칭시도 후 추가동작)
@Autowired private DiscountPolicy rateDiscountPolicy
- @Qualifier끼리 (서브 데이터베이스 커넥션 빈)
@Qualifier("mainDiscountPolicy")
- @Primary: 우선순위 (메인 데이터베이스의 커넥션)
- @Qualifier > @Primary
조회한 빈이 모두 필요할 때, List, Map
public DiscountService(Map<String, DiscountPolicy> policyMap,
List<DiscountPolicy> policies) {
this.policyMap = policyMap;
this.policies = policies;
}
자동/수동 빈 등록 사용
- 업무 로직 빈: 컨트롤러, 서비스, 리포지토리-> 자동
- 기술 지원 빈:기술적인 문제나 공통 관심사(AOP)-> 수동
빈 생명주기 콜백
스프링 컨테이너 생성-> 스프링 빈 생성-> 의존관계 주입-> 초기화 콜백-> 사용-> 소멸전 콜백-> 스프링 종료
빈 생명주기 콜백 지원 방법
- 인터페이스: InitializingBean의 afterPropertiesSet() 초기화 지원, DisposableBean의 destroy() 소멸 지원
- 단점: 스프링 전용 인터페이스 의존, 초기화, 소멸메서드 이름 변경 불가, 코드를 고칠 수 없는 외부 라이브러리에 적용불가
- 설정 정보에 초기화 메서드, 종료 메서드 지정: 메서드 이름 자유, 스프링 코드에 의존x, 설정 정보를 사용->코드를 고칠 수 없는 외부 라이브러리에도 초기화, 종료 메서드 적용 o, destroyMethod(기본값 추론)-> close , shutdown 자동 호출
@Bean(initMethod = "init", destroyMethod = "close")
- @PostConstruct, @PreDestroy 애노테이션 지원: 가장 편리, 권장, 자바 표준, 외부 라이브러리에 적용 못함
빈 스코프
스코프: 빈이 존재할 수 있는 범위
- 싱글톤
- 기본 스코프,
- 스프링 컨테이너의 시작과 종료까지,
- 가장 넓은 범위,
- 항상 같은 인스턴스의 스프링 빈을 반환
- 프로토타입
- 프로토타입 빈의 생성과 의존관계 주입, 초기화까지만 관여,
- 항상 새로운 인스턴스를 생성,
- 프로토타입 빈 관리 책임->프로토타입 빈 받은 클라이언트,
- @PreDestroy호출 x
@Scope("prototype")
- 싱글톤 빈이 프로토타입 빈 주입 받을 경우 함께 유지됨
- Provider로 문제 해결
- 직접 필요한 의존관계 찾는 것 Dependency Lookup (DL)
- ObjectFactory, ObjectProvider
@Autowired private ObjectProvider<PrototypeBean> prototypeBeanProvider; ... prototypeBeanProvider.getObject();
- JSR-330 Provider: 자바 표준
@Autowired private Provider<PrototypeBean> provider; ... provider.get()
- ObjectFactory, ObjectProvider
- 웹 관련 스코프: 스코프의 종료시점까지 관리
- request: 웹 요청이 들어오고 나갈때 까지
- session: 웹 세션이 생성되고 종료될 때 까지
- application: 웹의 서블릿 컨텍스트와 같은 범위
- websocket: 웹 소켓과 동일한 생명주기
- AnnotationConfigServletWebServerApplicationContext 기반
- MyLogger -> @Scope(value = "request") -> @RequiredArgsConstructor 애플리케이션을 실행하는 시점에 싱글톤 빈 생성해서 주입하는데 -> request 스코프 빈 생성 안됨 -> 오류
- 해결: 빈 생성 지연
//Provider
private final ObjectProvider<MyLogger> myLoggerProvider;
//프록시(CGLIB): 요청이 오면 그때 내부에서 진짜 빈 요청
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
'스프링' 카테고리의 다른 글
[스프링] 서블릿, JSP, MVC 패턴 (0) | 2022.06.09 |
---|---|
[스프링] 웹 애플리케이션 이해, 서블릿 (0) | 2022.06.09 |
스프링 프로젝트 (0) | 2022.01.07 |
IntelliJ 설치 (0) | 2022.01.07 |
자바11 설치 (0) | 2022.01.07 |