스프링 이모저모

Seok-Hyun Lee·2022년 5월 3일
0
  • 스프링은 객체 지향 언어가 가진 강력한 특징을 살려내는 프레임워크
    • DI 컨테이너, DI, IoC, AOP
  • 객체지향의 특징 : 캡슐화, 상속, 추상화, 다형성
  • 객체 지향 프로그래밍 : 속성과 행위를 가지는 객체들을 활용하여 프로그램을 유연하고 변경 용이하게 만드는 방법
    • 유연과 변경 용이 ? 레고 블럭 조립하듯이 쉽게 컴포넌트들을 갈아 끼울 수 있기 때문
    • 즉, 다형성! 객체 지향 프로그래밍의 핵심이라 볼 수 있다. => 역할과 구현의 분리
    • 역할은 인터페이스 구현은 구현체

OCP 개발-폐쇄 원칙 => 특정 역할에 의존하고 구현체를 갈아끼우는 식으로 사용자의 기존 코드는 유지한 채 다른 기능으로 확장이 가능
그러나 일반적으로 사용자의 기존 코드에서 변경할 구현체로 변경하지 않고는 구현체 갈아끼우기가 불가능
이를 해결하기 위해서는 외부에서 구현체의 변경을 관리해줘야 한다.

DIP -> 구현체가 아닌 추상화에 의존하는 원칙 => 인터페이스 타입의 변수에 구현체 객체를 할당하는 방법도 이를 위반한 것
MemberRepository repo = new MemoryMemberRepository(); // DIP 위반

  • 스프링 빈은 실제로 런타임에 사용이 필요한 순간에 생성되도록 LazyInit 으로 설정되어 있다.

  • 스프링 컨테이너는 java 코드나 XML 이던 상관 없이 BeanDefinition (빈의 메타정보) 만 알면 된다. 즉, BeanDefinition 을 통해서 빈의 생성 및 연결을 관리할 수 있는 것

싱글톤 객체는 아래와 같은 문제를 가지고 있다.

  • 구현 클래스의 getInstance() 메소드를 사용해서 객체를 가져와야하기에 구현 클래스에 의존하게 된다.
  • 구현 클래스에 의존하는 것은 DIP 와 OCP 를 위반할 수 있다.
  • 즉, 싱글톤은 안티패턴이다

하지만, 스프링 컨테이너는 위의 문제를 해결해준다.

  • 컨테이너인 ApplicationContext 객체로부터 getBean() 을 하면 BeanDefinition 을 참조하여 객체를 싱글톤으로 생성
  • 이 객체는 getBean() 을 할 때마다 참조되기에 같은 참조값을 가짐
  • 스프링 컨테이너는 애초에 IoC 와 DI 를 위해 객체의 생성과 연결을 외부에서 관리하도록 만들어진 것이기에 DIP 와 OCP 를 위반하지 않는다.

그래도 싱글톤 객체는 이러한 문제가 있다.

  • 싱글톤 내부의 데이터는 전역에 공유된다

그래서 싱글톤 객체는 Stateless 해야하는 것이 중요

  • 특정 클라이언트에 의존적인 필드가 있으면 안된다
  • 특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안된다
  • 가급적 읽기에만 사용
  • 필드 대신에 전역에서 공유되지 않는 지역변수, 파라미터, ThreadLocal 등을 사용해야 한다.

빈을 중복 등록하면 에러 발생 -> BeanDefinitionStoreException

조회한 빈 모두를 사용해야 할 때 Map 또는 List 를 사용할 수 있다.
예를 들어,
interface Policy 가 있고 이를 구현하는 FirstPolicy, SecondPolicy 가 있다면

Map<String, Policy> policyMap / List< Policy> policies 를 활용해 빈을 조회하면
자바 컬렉션 안에 관련된 모든 빈이 나온다
왜냐하면, 스프링은 기본적으로 빈 조회 시 자식에 해당하는 모든 빈이 조회되게 설정되어 있기 때문에 가능

  • 스프링 빈 기본 라이프사이클

    1. 스프링 컨테이너 생성
    2. 빈 생성
    3. 의존 관계 주입
    4. 초기화 후 콜백(@PostConstruct)
    5. 객체 사용
    6. 소멸 전 콜백(@PreDestroy)
    7. 스프링 컨테이너 종료
  • 빈 스코프

    • 싱글톤 : 기본 스코프, 스프링 컨테이너 시작과 종료
    • 프로토타입 : 빈 생성, 의존 주입, 초기화까지만 관여, 클라이언트측이 객체 소멸의 책임을 가짐(destroy method)
    • 웹 관련 스코프 : 웹 환경에서만 동작, 스코프의 종료까지 관리(종료 메소드 호출됨)
      • request : 웹 요청이 들어오고 나갈때까지 유지
      • session : 웹 세션이 생성되고 종료될때까지 유지
      • application : 웹의 서블릿 컨텍스트와 같은 범위로 유지

주의해야 할 점!!
싱글톤 빈에서 프로토타입 빈을 사용하는 경우,
프로토타입은 주입 후 초기화까지만 컨테이너에서 관리하기 때문에
프로토타입 빈을 주입받은 싱글톤 빈에서는 이 프로토타입 빈을 계속 사용한다.
즉, 싱글톤 빈에서 프로토타입 빈의 메소드를 호출하는 경우에 계속 같은 빈을 참조함을 의미
이러한 경우에 처음에 의도한 프로토타입 빈을 제대로 활용하지 못하게 된다

이를 해결하려면 프로토타입 빈은 필요할 때마다 새롭게 주입시켜주면 된다.
즉, getBean() 을 필요할 때마다 계속 호출해서 주입시켜주면 됨
이러한 방식을 Dependency Lookup(Dl)/조회(탐색) 의존 관계라고 불리움

하지만, 이렇게 되면 객체가 스프링 컨테이너에 의존하게 되는 문제가 발생한다.
그래서 스프링에서는 ObjectProvider<> 를 제공한다
그러면 싱글톤 스프링 빈에서는 아래와 같이 Provider 를 사용해서 DL 해주면 된다

private ObjectProvider<PrototypeBean> prototypeBeanProvider;

public void temp(){
	PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
    // TO-DO
}

또는 자바 표준 중 하나인 JSR303 Provider 를 사용할 수도 있다.

웹 스코프 소개

  • Request 스코프 : HTTP 요청 하나가 들어오고 나갈 때까지 유지되는 스코프, 각각의 HTTP 요청마다 별도의 빈 인스턴스가 생성되고 관리된다.
  • Session 스코프 : HTTP Session 과 동일한 생명주기를 가지는 스코프
  • Application 스코프 : 서블릿 컨텍스트와 동일한 생명주기를 가지는 스코프
  • Websocket 스코프 : 웹 소캣과 동일한 생명주기를 가지는 스코프

여기서 문제가 발생,
1. HTTP 요청이 안 들어오면 빈 생성 X -> 다른 빈들에서 직접 의존 불가 -> Provider 가 필요
2. 스코프에 proxy 를 추가 -> 가짜 프록시 객체 주입 -> HTTP 요청이 오면 실제 객체에 위임

이 둘이 가지는 장점은 편리하게 의존성을 주입할 수 있음과 동시에 지연처리가 가능하다는 것

profile
Arch-ITech

0개의 댓글