IoC 컨테이너와 빈이 뭘까?
IoC(Inversion of Contol) : 의존관계 주입(Dependency Injection) 이라고도 함. 어떤 객체가 사용하는 의존 객체를 직접 만들어 사용 하는게 아니라, 주입 받아 사용 하는 방법.
ApplicationContext를 통해 BeanFactory관리 : 빈 설정 소스로부터 빈 정의를 읽고, 빈 구성 및 제공
애플리케이션 컴포넌트의 저장소
빈: 스프링 IoC 컨테이너가 관리하는 객체.
Repository나 Service 어노테이션 이용시 Bean 등록
라이프사이클 인터페이스 사용가능 @PostConstruct를 이용하여 빈 등록시 동작하는 메소드 구현가능
@Service
public class BookService {
// 의존 객체를 직접 만들어 사용
private BookRepository bookRepository = new BookRepository();
}
@Service
public class BookService {
// 객체를 주입 받아서 사용
private BookRepository bookRepository;
public BookService(BookRepository bookRepository){
this.bookRepository = bookRepository;
}
}
스프링 IoC 컨테이너와 빈을 사용하면 싱글톤 형식으로 관리 가능
Repository나 Service들은 하나만 이용해서 사용하면 안전하게 관리 가능
-> 어디서 이용하든 BookService는 같은 객체이므로 사용하는 메모리 면에서 효율적이고, Runtime시 매번 생성할 때보다 성능면에서도 좋다.
의존성 주입 없이 만들면 새로운 객체 생성시 많은 메모리, 성능이 낭비 될 것이고 이를 모두 싱글톤 객체로 구현하하면 복잡한 코드들이 요구 될 텐데 이를 스프링이 제공하는 IoC 컨테이너로 쉽게 관리 할 수 있습니다~
ApplicationContext를 이용한 의존성 주입
자바를 통해 빈을 등록하고 싶다면 @Configuration을 통해 아래와 같이 빈을 등록하여 의존성을 주입할 수 있습니다.
@Configuration
public class ApplicationConfig {
@Bean
public BookRepository bookRepository() {
return new BookRepository();
}
@Bean
public BookService bookService(){
BookService bookService = new BookService();
bookService.setBookRepository(bookRepository());
return bookService;
}
}
다만 main 함수에 다음과 같이 ApplicationContext에 Config.class를 등록해주어야 합니다.
ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
위와 같은 방법은 일일이 빈을 등록해주어야하는 번거로움이 있습니다.
하지만 @Respotiory, @Service에는 @Component라는 어노테이션이 이미 등록 되어있으며 Config.class에서 @ComponentScan을 이용하면 쉽게 등록할 수 있습니다.
@Configuration
@ComponentScan(basePackageClasses = CoreApplication.class)
public class ApplicationConfig {
}
이런식으로 등록한다면 하위 클래스들을 전부 Scan하여 "Spring"스러운 빈등록 및 의존성 주입을 할 수 있게됩니다.
하지만 main클래스를 갖는 CoreApplication.class에 @SpringBootApplication 어노테이션을 달아준다면 이미 해당 어노테이션에 @ComponentScan이 있기때문에 "Spring Boot"스럽게 진행할 수 있습니다.
@SpringBootApplication
public class CoreApplication {
public static void main(String[] args) {
SpringApplication.run(CoreApplication.class, args);
}
}
@Autowired를 이용한 의존성 주입
필요한 의존 객체의 "타입"에 해당하는 빈을 찾아 주입해주는 어노테이션
생성자,Setter,필드에 선언 가능
@Service
public class BookService {
// 어노테이션으로 간단하게 의존성 주입 가능
@Autowired
private BookRepository bookRepository;
}
다만 해당 타입 빈이 없는 경우, 해당 타입의 빈이 여러 개인 경우에 어플리케이션 구동 실패할 수 있습니다.
빈이 없는 경우에는 빈을 생성 및 주입 받아서 해결하면 되고, 빈이 여러개인 경우 3가지 해결방법이 있습니다.
1. 사용할 빈에 @Primary어노테이션을 추가해주는 방법.
2. List형식으로 모든 빈을 주입받는 방법
3. 주입 받을 때 @Qualifier("빈 이름")으로 주입 받는 방법.
@Primary를 이용할 경우 해당 타입의 빈을 우선 처리하기때문에 빈이 여러개이지만 하나의 빈만 사용하는 경우에 주로 사용하지만,
골고루 사용하는 경우 선택이 필요한 경우 @Qualifier를 사용해야한다. 다만 @Qualifier시 문자열로 입력 하기 때문에 Type-safe하지 못하다는 단점이 있다.
컴포넌트 스캔
컴포넌트 스캔의 주요 기능: 스캔위치 설정, 필터(어떤 어노테이션 스캔 or not 스캔)
상황에 따른 빈 등록 어노테이션
@Component : @Service, @Repository, @Controller, @Configuration에 해당하지 않지만 빈으로 등록하고 싶은 경우
아래의 어노테이션은 모드 @Component 어노테이션을 가지고 있습니다.
@Repository : 데이터를 다루는 빈 등록
@Controller : 컨트롤러 사용시
@Service : 서비스 클래스에 사용
@Configuration : 빈 설정 파일에 사용
빈의 스코프
싱글톤 스코프: 프로젝트 전반에 걸쳐 해당 빈의 인스턴스가 한 개인 경우
프로토타입 스코프: 매번 새로운 객체의 인스턴스를 만들어 사용하는 경우, ex) Request, Session, WebSocket
싱글톤 스코프 예제
다음과 같이 별다른 설정없이 Proto와 Single을 @Component로 빈을 등록시키면 싱글톤 스코프를 갖게 된다.
@Component
public class Proto {
}
@Component
public class Single {
@Autowired
private Proto proto;
public Proto getProto() {
return proto;
}
}
proto와 single의 getter를 통한 proto의 인스턴스를 출력해보면
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
Single single;
@Autowired
Proto proto;
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println(proto);
System.out.println(single.getProto());
}
}
다음과 같이 같은 레퍼런스를 갖는 것을 확인할 수 있다.
프로토타입 스코프 예제
Single 클래스는 이전과 동일하고, Proto 클래스에는 @Scope("prototype") 어노테이션을 추가해주었다.
@Component @Scope("prototype")
public class Proto {
}
ApplicationContext에서 빈을 관리하므로 의존성을 주입받아 getBean을 통해 출력해보았다.
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
ApplicationContext ctx;
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("proto");
System.out.println(ctx.getBean(Proto.class));
System.out.println(ctx.getBean(Proto.class));
System.out.println(ctx.getBean(Proto.class));
System.out.println("single");
System.out.println(ctx.getBean(Single.class));
System.out.println(ctx.getBean(Single.class));
System.out.println(ctx.getBean(Single.class));
}
}
프로토타입의 인스턴스는 새로 추가되었고, 싱글 스코프의 인스턴스는 모두 동일한 것을 확인할 수 있다.
주의사항
프로토타입 빈이 싱글톤 빈을 참조하는데에는 문제가 없다. 하지만 싱글톤 빈이 프로토타입 빈을 참조하면 문제가 된다.
싱글톤 빈으로 프로토타입 빈을 가지고 있기 때문에 프로토타입 빈이 업데이트 되지않음. 하지만 proxyMode등 다른 방법을 이용하면 사용가능. proxyMode를 사용하면 싱글톤 빈이 프로토타입 빈을 직접 참조하는 것이 아니라 proxy를 거쳐가기 때문에 proxy에서 프로토타입 빈의 업데이트가 가능하다.
Environment
프로파일과 프로퍼티를 다루는 인터페이스
ApplicationContext extends EvironmentCapable를 통해 사용가능
1. 프로파일
프로파일
- 빈들의 그룹
- Environment의 역할은 활성화할 프로파일 확인 및 설정
프로파일 유즈케이스
- 테스트 환경에서는 A 빈을 사용, 배포 환경에서는 B의 빈을 사용하는 경우
- C 빈은 모니터링 용도로 테스트할 때는 필요없고, 배포시만 등록을 원하는 경우
프로파일 정의하기
- 클래스에 정의 @Configuration @Profile("test") 또는 @Componet @Profile("test")
- 메소드에 정의 @Bean @Profile("test")
위와 같이 설정시 해당 클래스와 메소드는 프로파일이 test인 경우에만 빈이 주입된다.
프로파일을 test로 설정은 아래를 참고
프로파일 설정하기
- -Dspring.profiles.active="test"
- @ActiveProfiles("test")
프로파일 표현식
- ! not
- & and
- | or
2. 프로퍼티
다양한 방법으로 정의할 수 있는 설정값
Environment의 역할은 프로퍼티 소스 설정 및 프로퍼티 값 가져오기
MessageSource
국제화(i18n) 기능을 제공하는 인터페이스
ApplicationContext에서 이미 상속받아서 구현되어있음
getMessage를 이용해서 읽을 수 있음
스프링 부트를 사용한다면 별 다른 설정 필요없이 messages.properties사용가능
- messages.properties
- messages_ko_KR.properties
ApplicationEventPublisher
ApplicationContext extends ResourceLoader
이벤트 프로그래밍에 필여한 인터페이스. 옵저버 패턴의 구현체
이벤트 만들기
- ApplicationEvent 상속
- 스프링 4.2부터는 상속받지 않고 이벤트로 사용가능
이벤트 발생 시키기
- ApplicationEventPublisher.publishEvent() 를 이용하여 발생
이벤트 처리 방법
- ApplicationListener<Event>로 클래스 빈으로 등록
- 스프링 4.2부터 @EventListener를 사용해 빈의 메소드 사용 가능
- 기본적으로 synchronized
- @Order로 순서 설정 가능
- @Async를 통헤 비동기적 실행 가능
ResourceLoader
리소스를 읽어오는 기능을 제공하는 인터페이스
리소스 읽어오기
- 파일시스템에서 읽어오기
- classpath에서 읽어오기
- URL로 읽어오기
- 상대/절대 경로로 읽어오기
인프런 스프링프레임워크 핵심 기술 : 백기선 강좌 참고
'Spring Boot > Core' 카테고리의 다른 글
스프링 AOP (0) | 2022.08.01 |
---|---|
SpEL (0) | 2022.07.30 |
데이터 바인딩 추상화 (0) | 2022.07.26 |
Validation 추상화 (0) | 2022.07.25 |
Resource 추상화 (0) | 2022.07.25 |
댓글