[Spring] 스프링 핵심 원리 - 기본편

1. 객체지향

다형성 (역할과 기능 분리)

  • 인터페이스
  • 클라이언트를 변경하지 않고, 서버의 구현 기능을 유연하게 변경할 수 있다.
  • 한계 : 역할(인터페이스) 자체가 변하면, 서버 모두에 큰 변경 발생 -> 인터페이스를 안정적으로 설계할 것

스프링과 객체지향

  • 다형성
  • 스프링에서 이야기하는 제어의 역전(IoC), 의존관계 주입(DI)은 다형성을 활용해서 역할과 구현을 편리하게 다룰 수 있도록 지원한다.
  • 스프링의 핵심 = 객체 지향
  • 객체지향의 핵심 = 다형성 = 구현과 기능 분리

좋은 객체 지향 설계의 5가지 원칙(SOLID)

  • SRP(단일 책임 원칙) : 한 클래스는 하나의 책임만. 변경이 있을 때 파급 효과가 적어야 함
  • OCP(개방-폐쇄 원칙)* : 확장에는 열려있으나 변경에는 닫혀있어야 한다 -> 스프링 컨테이너가 OCP를 지켜줌
  • LSP(리스코프 치환 원칙) : 프로그램의 정확성을 깨뜨리지 않아야 한다
  • ISP(인터페이스 분리 원칙) : 여러 개의 인터페이스로 분리
  • DIP(의존관계 역전 원칙)* : 추상화에 의존해야지, 구체화에 의존하면 안된다. 추상 클래스에 의존해야지, 구체 클래스에 의존해서는 안된다.
    => DI와 DI컨테이너를 통해 SOLID를 지킬 수 있음

회원 도메인 설계의 문제점

  • DIP 준수 X
    • memberService와 MemberSericeImpl를 모두 의존하고 있음

관심사의 분리 - AppConfig

  • MemberSericeImpl에서 MemoryMemberRepository를 가지도록 지정 받지 않고, AppConfig에서 MemberRepository를 가지도록 지정해줌
  • *구체적인 것은 AppConfig에서 결정. "공연기획자"

제어의 역전 - IoC

  • 프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서(AppConfig)에서 관리하는 것
  • IoC 컨테이너, DI 컨테이너 = AppConfig

2. 스프링 컨테이너

ApplicationContext applicationContext 
	= new AnnotationConfigApplicationContext(AppConfig.class);

스프링 컨테이너 생성 방법

  • XML 기반
  • 애노테이션 기반의 자바 설정 클래스

빈 등록 방법

  • 직접 등록
  • FactoryBean을 통해 등록 (일반적으로 사용됨)

빈 출력 방법

  • 빈 이름으로 출력하기
    • ac.getBean("memberService", MemberService.class)
  • 빈 타입으로 출력하기
    • ac.getBean(MemberService.class);

상속관계

  • 스프링 컨테이너 > 스프링 빈 저장소 > 스프링 빈
  • BeanFactory <- ApplicationContext <- AnnotationConfigApplicatonContext

3. 싱글톤 컨테이너

싱글톤 패턴

  • 클래스의 인스턴스가 딱 1개만 생성되는 것을 보장하는 디자인 패턴
  • 해당 객체가 하나만 생성되고, 객체 인스턴스를 공유해서 사용

스프링 & 싱글톤

  • 스프링 컨테이너는 싱글톤 패턴을 적용하지 않아도, 객체 인스턴스를 싱글톤으로 관리한다
  • 싱글톤 방식의 주의점 : 무상태(stateless)로 설계해야 한다!!!
  • 객체 인스턴스를 공유하기 때문에 값이 변경될 수 있음

@Configuration과 싱글톤

  • AppConfig@CGLIB 을 통해 싱글톤 보장
    • @Bean이 붙은 메서드마다 이미 스프링 빈이 존재하면 존재하는 빈을 반환하고, 스프링 빈이 없으면 생성해서 스프링 빈으로 등록하고 반환하는 코드가 동적으로 만들어진다.
  • @Configuration : AppConfig-> AppConfig@CGLIB
    • @Configuration을 안 하고 @Bean만 하면 스프링 컨테이너의 싱글톤이 깨짐

4. @Component 와 DI

컴포넌트 스캔과 의존관계 자동 주입

  • 기존 : AppConfig.class & 설정정보에 직접 등록
  • 컴포넌트 스캔 : @Component 애노테이션이 붙은 클래스를 스캔하여 스프링 빈으로 등록
  • 의존관계 자동 주입 : 클래스의 생성자에 @Autowired 를 붙여줌 -> 의존관계를 auto wire 함
    • 생성자가 1개 있으면 @Autowired 를 생략할 수 있음

스프링 빈 & 의존관계

  • @Configuration, @Bean : AppConfig 에 함
  • @Component, @Autowired : 객체에 함

애노테이션

  • @Configuration : 스프링 설정 정보로 인식. AppConfig에서 사용
  • @Component : 컴포넌트 스캔에서 사용
  • @Controller : 스프링 MVC 컨트롤러
  • @Service : 스프링 비즈니스 로직
  • @Repository : 스프링 데이터 접근 계층

필터

  • includeFilters : 컴포넌트 스캔에 추가할 대상 지정
  • excludeFilters : 컴포넌트 스캔에 제외할 대상 지정

자동의존관계 주입 방법

스프링 빈에 등록되어야 의존관계 주입(DI)를 할 수 있다
  1. 생성자 주입 (권장)

    • 불변, 필수
    • 생성자 호출 시점에 딱 1번 호출
    • 장점 : 대부분 다 불변임. final 키워드를 사용할 수 있음. 순수한 자바 언어의 특징을 잘 살리는 방법임.
      - 롬복 : 코드 깔끔 - 예) getter setter 자동으로 만들어줌
  2. 수정자 주입(setter 주입)

    • getter/setter -> 선택, 변경
  3. 필드주입

    • 코드 간결 but 외부 변경 불가능
    • 테스트 코드에서만 사용하자
  4. 일반 메서드 주입

    • 잘 사용하지 않는다

조회된 빈이 2개 이상일 때

  1. @Autowired 필드 명 매칭
@Autowired private DiscountPolicy rateDiscountPolicy
  1. @Qualifier 끼리 매칭
@Component
@Qualifier("mainDiscountPolicy")
public class RateDiscountPolicy implements DiscountPolicy {
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, @Qualifier("mainDiscountPolicy") DiscountPolicy discountPolicy) {
 this.memberRepository = memberRepository;
 this.discountPolicy = discountPolicy;
}
  1. @Primary : 우선순위 부여
@Component
@Primary
public class RateDiscountPolicy implements DiscountPolicy { ... }
@Component
public class FixDiscountPolicy implements DiscountPolicy { ... }

동적으로 빈을 사용할 때

private final Map<String, DiscountPolicy> policyMap;
private final List<DiscountPolicy> policies;
  • fixDiscountPolicy, rateDiscountPolicy 에 상관없이 Map 에 등록된 Bean 을 읽어와서 해당 할인 가격을 반환함
  • 다형성을 활용하는 경우

자동, 수동의 올바른 실무 운영 기준

  • 편리한 자동 기능을 기본으로 사용하자!
  • 애플리케이션에 광범위하게 영향을 미치는 기술 지원 객체는 수동 빈으로 등록해서 딱! 설정 정보에 바로 나타나게 하는 것이 유지보수 하기 좋다.
  • 다형성을 적극 활용하는 비즈니스 로직은 수동 등록을 고민해보자

5. 빈 생명주기

스프링 빈의 이벤트 라이프사이클

  • 스프링 컨테이너 생성 -> 스프링 빈 생성 -> 의존관계 주입 -> 초기화 콜백 -> 사용 -> 소멸전 콜백 -> 스프링 종료
    • 참고 : 생성자 주입은 스프링 빈이 생성될 때 의존관계 주입을 함께 함. but 객체 생성과 초기화는 분리하는 것이 좋음 (단일 책임의 원칙)
  • 애노테이션 @PostConstruct @PreDestroy

6. 빈 스코프

빈 스코프

  • 빈이 존재할 수 있는 범위
  • 싱글톤, 프로토타입, 웹관련스코프(request)
  • 스프링 빈 => 싱글톤 스코프

프로토타입 스코프

  • @Scope("prototype")
  • 프로토타입 스코프의 특징
    • 스프링 컨테이너에 요청할 때 마다 새로 생성된다.
    • 종료 메서드가 호출되지 않는다
  • 싱글톤 빈과 함께 사용시 문제점
    • 싱글톤 빈 위에 프로토타입 스코프가 만들어 지는데, 싱글톤 빈은 한번만 호출되고, 프로토타입 스코프도 싱글톤 빈 위에서 주입 시점에 한번만 호출됨
    • ObjectProvider : 찾아주는 기능만 해서 여러개의 프로토타입 생성 가능

웹 스코프 - request

  • @Scope(value = "request")
  • 웹 클라이언트의 요청(request) 가 올 때 생성됨

프록시

  • 프록시(가짜) 객체를 만들어 진짜 myLogger을 찾는 방법을 저장
  • 프록시 객체는 request 스코프의 진짜 myLogger.logic()을 호출함
  • 프록시 객체 덕분에 클라이언트는 마치 싱글톤 빈을 사용하듯이 편리하게 request scope를 사용할 수 있다.

좋은 웹페이지 즐겨찾기