컨테이너, 빈, 의존성

Song Chae Won·2023년 5월 10일
0

스프링부트와 AWS

목록 보기
15/20
post-thumbnail

2023-05-09 EFUB 7주차 세션을 듣고 정리한 내용입니다.

Spring 컨테이너

Container

  • 인스턴스의 생명 주기를 관리
  • 생성된 인스턴스들에게 추가적인 기능 제공

Spring Container

Spring에서 자바 객체(Bean)들을 관리하는 공간

  • 객체의 생성부터 소멸까지 개발자 대신 관리
  • 각 객체 간의 의존 관계를 연결

Singleton 패턴

클래스의 인스턴스가 딱 1개만 생성되는 것을 보장하는 디자인 패턴

  • 객체의 공유를 통해 메모리 낭비를 줄이기 위해 사용

특징

  • static 영역에 객체 인스턴스를 생성
  • private으로 new 키워드를 막음
  • getInstance() 키워드를 통해서만 조회 가능

Singleton 패턴의 문제점

  • 복잡한 코드
  • 구체 클래스.getInstance()로 객체 조회
    ➡️ 클라이언트가 구체 클래스에 의존함으로써 DIP 위반
  • private 생성자로 자식 클래스를 만들기 어려움

Why Spring Container?

  • 매번 인스턴스를 생성할 필요 없이 단 하나만 생성해서 비용을 줄일 수 있음
    == Singleton 패턴을 적용하지 않아도 객체 인스턴스를 Singleton으로 관리
    ➡️ Singleton 컨테이너의 역할
    • 객체 간의 의존성을 낮춤 (객체 지향 프로그래밍),
    • 제어의 흐름을 외부에서 관리할 수 있음 (IoC)

Bean Factory

Spring Container의 최상위 인터페이스

  • Spring Bean을 생성하고, 객체 사이의 런타임 의존관계를 맺어주는 역할을 함
  • 직접적으로 사용하는 경우는 거의 없음.

Application Context

Application Context == Spring Container
• Application Context ⊂ Bean Factory
• Bean Factory + 부가 기능 (국제화, 환경 변수 관련 처리, 애플리케이션 이벤트, 리소스 조회)
• XML / 자바 클래스 기반으로 생성할 수 있음.

Container 동작 원리

1) Spring Container 생성
2) Spring Bean 등록
: Spring 설정 파일을 기반으로 container에 Spring Bean 등록
3) Spring Bean 의존관계 설정
: Spring 설정 파일을 기반으로 Spring Bean 의존 관계 주입

Bean

Spring Container가 관리하는 자바 객체

  • Spring Container에 의해 생성, 관리됨
  • Bean 이름은 항상 다르게 설정해야 함 ➡️ Singleton 패턴 때문

Spring Bean으로 관리되는 객체들

  • 모든 클래스의 객체가 Bean으로 처리되는 것은 아님
  • 핵심 배역을 하는 객체들만 Bean으로 등록됨
  • 주로 오랜 시간동안 프로그램 내에 상주하면서 중요한 역할을 하는 역할 중심의 객체들
  • ex. DTO, VO – 데이터에 중점을 둔 객체 ➡️ Bean으로 관리되지 않음
    Service – DAO가 받아온 데이터를 전달받아 가공하는 역할 ➡️ Bean으로 관리됨

Container에 Bean 등록하기

1) 컴포넌트 스캔
• @Component를 사용
• @Controller, @Service, @Repository는 인터페이스로 @Component를 받음 ➡️ 컴포넌트 등록 가능
• 단, 실행되는 패키지와 같은 패키지에 있는 클래스에 대해서만 @Component가 적용됨

@Component
public @interface Service {
	...
}

2) 직접 등록

  • @Configuration, @Bean으로 직접 등록 가능
@Configuration
public class SpringConfig {
	@Bean
    public MemberService memberService() {
    	return new MemberService(memberRepository());
        }
    }

Bean 관련 메서드

1) getBeanDefinitionNames() : 스프링에 등록된 모든 Bean 이름을 조회
2) getBean() : 특정 Bean을 조회하는데 사용

  • ac.getBean(classType)
  • ac.getBean(beanA)
  • ac.getBean(beanA, classType)
    ➡️ Bean이 존재하지 않는 경우, NoSuchDefinitionException 발생
    3) getBeansOfType() : 특정 타입에 해당하는 모든 Bean 조회

IoC

IoC 제어의 역전

프로그램의 제어 흐름을 개발자가 아닌 외부에서 관리
• 객체의 생성과 소멸, 객체 간의 의존관계 설정을 모두 Spring에서 제어
• 객체의 의존성을 역전시켜 모듈 간의 의존도를 줄이고 유연한 코드를 작성할 수 있게 함
• IoC가 이루어지는 공간 == IoC 컨테이너

IoC == DI ?

  • IoC는 설계(디자인) 패턴이 아닌 원칙으로, 높은 레벨의 가이드라인을 제공하는 역할
  • DI는 객체를 외부에서 생성한 후 주입시켜주는 방식으로, DI를 통해서 IoC가 이루어질 수 있음

참고) IoC가 구현된 예

  • Dependency Injection
  • Service Locator Pattern
  • Event-driven Programs

DI

DI 의존성 주입

: 클래스 사이의 의존 관계를 Bean 설정 정보를 바탕으로 자동 연결해주는 것

의존성 주입 방법

1) 필드 주입
2) 수정자(setter) 주입
3) 생성자 주입
4) 일반 메서드 주입

수정자(setter) 주입

➡️ 주로 선택과 변경 가능성이 있는 의존 관계에서 사용

@Component
public class SampleController {
	private SampleService sampleService;
    
    @Autowired
    public void setSampleService(SampleService sampleService){
    	this.sampleService = sampleService;
        }
    }
  • Setter 메서드를 public으로 두어야 함 ➡️ 오류에 취약함
  • 순수 자바 코드를 통해 단위 테스트를 하는 경우 의존 관계가 누락되었을 때 NullPointerException을 통해서만 확인 가능

생성자 주입

  • 생성자에 @Autowired를 붙이는 방법
  • 생성자가 하나인 경우, @Autowired를 붙이지 않아도 실행됨 (Spring 4.3 버전부터)
  • Spring에서 가장 권장함
  • 대부분의 의존관계 주입은 한 번 일어날 경우 애플리케이션 종료시까지 의존관계를 변경할 일 X
    ➡️ 생성자 주입은 객체를 호출할 때 딱 한 번만 호출되므로 불변하게 설계 가능
  • 순수 자바 코드를 통해 단위 테스트를 하는 경우 의존관계가 누락되었을 때 컴파일 에러를 통해
    바로 캐치할 수 있음
  • 순환 의존성 검출 가능 (BeanCurrentlyCreationException 발생)
    ➡️ 프레임워크에 의존하지 않고 순수 자바 언어의 특징을 가장 잘 살리는 방법
  • 최근에는 생성자를 1개 두고 @Autowired를 생략하는 방식을 주로 사용함
  • @RequiredArgsComstructor를 함께 사용 䡸 필요한 기능을 더 깔끔하게 사용 가능

@Autowired

• 필요한 의존 객체의 타입에 해당하는 Bean을 찾아 주입
• 생성자, setter, 필드에서 사용 가능

➕ 사용시 주의할 점
• DI는 스프링이 관리하는 객체에서만 동작함
== Spring Bean으로 등록하지 않고 직접 생성한 객체에서는 동작 X
• Bean이 없는 경우 의존성을 주입할 수 없음
➡️ 주입할 Bean이 없어도 동작해야 하는 경우 존재!

@Autowired 옵션 처리

• @Autowired(required = false): 자동 주입할 대상이 없으면 setter 메서드 호출 X
• org.springframework.lang.@Nullable 어노테이션: 자동 주입할 대상이 없으면 Null 입력
• Java8 Optional <>: 자동 주입할 대상이 없으면 Optional.empty 입력

의존성 주입 Annotation

  1. @Autowired: 해당 타입의 객체를 찾아서 자동으로 할당, Spring에서 지원
  2. @Qualifier: 특정 객체의 이름을 이용하여 의존성을 주입
  3. @Resource: 해당 타입의 객체를 찾아서 자동으로 할당, 객체의 이름를 이용하여 의존성을 주입
    (@Autowired + @Qualifier)
  4. @Inject: @Autowired와 동일한 기능, 특정 프레임워크에 종속 X

+) XML 파일: 별도 파일인 .xml 파일에 등록할 Bean들을 모두 정의

LomBok

: 반복되는 메서드를 어노테이션을 사용하여 자동으로 작성해주는 라이브러리

• Java에 사용하는 메서드가 많아질수록 코드가 복잡해지고 생성하는데 번거로워짐
➡️ Lombok을 사용하여 코드 다이어트
• 어노테이션 기반의 코드 자동 생성을 통한 생산성 향상
• 반복 코드 다이어트를 통해 가독성 및 유지보수성 향상

의존성 주입 방법

Spring, Lombok을 결합한 생성자 주입
• @RequiredArgsConstructor를 사용하는 방법
• Lombok 라이브러리를 통해 생성자를 생성 ➡️ Spring 프레임워크가 해당 생성자에 @Autowired가 생략되었다는 것을 인식 ➡️ 적합한 Spring Bean 주입
• Spring 4.3부터 사용 가능

Lombok으로 의존성 주입하기

• 생성자 의존관계 주입시 생성자 코드를 반드시 삽입해야 함
➡️ 이 또한 Lombok @RequiredArgsConstructor 애너테이션으로 간편하게 처리 가능!

Lombok Annotation

🔻생성자 관련
• @NoArgsConstructor: 매개 변수가 없는 기본 생성자를 생성
• @RequiredArgsConstructor: final 필드만 포함된 생성자를 생성
• @AllArgsConstructor: 모든 필드를 포함한 생성자를 생성

🔻메서드 관련
• @Getter/@Setter: Getter/Setter를 자동으로 생성
• @toString: toString 메서드를 자동으로 생성
• @EqualsAndHashCode: equals, hashCode를 자동으로 생성

🔻빌더 패턴
• @Builder: 메서드 체이닝을 이용하는 static 메서드 builder를 생성

🔻통합 기능
• @Data: @ToString, @EqualsAndHashCode, @Getter, @Setter, @RequiredArgsConstructor를
한꺼번에 제공

Lombok Annotation 사용시 주의할 점

• @Data 사용 지양: 다양한 애너테이션을 한꺼번에 사용하는 만큼 부작용 다수 발생
• 무분별한 Setter 사용 지양
• Setter는 그 의도가 분명하지 않음
• 객체를 얼마든지 변경할 수 있는 상태가 되어 객체의 일관성을 유지하기 어려움

profile
@chhaewxn

0개의 댓글