[Spring] 스프링 컨테이너, 스프링 빈
- [ Backend ]/Spring
- 2022. 1. 22.
프레임워크 vs 라이브러리
내가 작성한 코드를 제어하고, 대신 실행한다면(IoC) 그것은 프레임워크이고, 내가 작성한 코드가 직접 제어의 흐름을 담당한다면 라이브러리이다.
스프링을 사용함으로서 범용의 프레임워크를 이용해서 개발을 진행할 수 있다.
스프링 컨테이너
ApplicationContext를 스프링 컨테이너라고 한다.
스프링 컨테이너는 @Configuration 어노테이션이 붙은 AppConfig 전달받아 설정정보로 사용한다. 이때 @Bean이라 명시된 메소드를 모두 호출해서 반환된 객체를 메소드명으로 스프링 컨테이너에 등록한다. 이렇게 스프링 컨테이너에 등록된 객체를 스프링 빈이라고 한다.
//스프링 컨테이너를 애노테이션 기반의 자바 설정 클래스로 설정
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
스프링 컨테이너가 생성되면 설정 클래스 정보를 전달받은 후 앞서 얘기한 @Bean 어노테이션을 보고 스프링 컨테이너 내의 스프링 빈 저장소에 이름과 객체명을 받아서 저장소에 등록하고, 의존관계를 주입(DI)해준다. 이때 빈의 이름은 중복되어서는 안된다.
컨테이너에 등록된 빈 조회
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationConText(AppConfig.class);
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
iteration {
ac.getBean(beanDefinitionNames); //스프링의 모든 빈 출력
ac.getBeanDefinition(beanDefinitionNames); //내가 등록한 빈 출력
}
getBeanDefinitionNames()메소드를 통해서 빈의 이름들을 가져오고, getBean()이나 getBeanDefinition()메소드의 매개변수로 가져온 이름을 전달해주면 빈을 호출할 수 있다.
이때 getBean()으로는 이름뿐 아니라 타입, 이름과 타입 모두 전달 가능한데, 타입만으로 조회시 오류가 발생할 수 있다. 따라서 이런 경우에는 이름과 타입 모두 전달하거나, ac.getBeansOfType()를 통해서 Map<> 컬렉션 타입으로 특정 타입을 모두 조회할 수 있다.
스프링 빈 내의 상속관계
부모 타입으로 조회하면 모든 자식타입이 조회된다. 따라서 타입으로 조회 시 자식이 둘 이상 있으면 무조건 에러(NoUniqueBeanDefinitionException)가 발생한다. 따라서 위에서 했듯이 이름도 지정해주어야 한다.
스프링 빈 생명주기
애플리케이션의 시작시점과 종료시점에 연결을 모두 종료하는 작업을 진행하려면, 객체의 초기화와 종료 작업이 필요하다. 스프링 빈은 객체를 생성하고, 의존관계 주입이 다 끝난 다음에야 필요한 데이터를 사용할 수 있는 준비가 완료된다. 따라서 애플리케이션의 동작은 반드시 의존관계 주입이 모두 끝난 후에 동작해야 한다.
따라서 스프링은 의존관계 주입이 완료되면 스프링 빈에게 초기화 콜백 메서드를 통해서 초기화 시점을 알려주며, 컨테이너가 종료되기 직전에 소멸 콜백을 준다.
1. 스프링 컨테이너 생성
2. 스프링 빈 생성
3. 의존관계 주입
4. 초기화 콜백
(사용)
5. 소멸 콜백
6. 스프링 종료
간단하게 위와 같은 생명주기를 가진다. 살펴보면 생성자를 통해 스프링 빈을 생성하는 작업과, setter 등을 통해 초기화하는 작업을 분리하고 있음을 알 수 있다. (생성은 인자를 받고 메모리를 할당하는 작업을 말하며, 초기화는 할당된 메모리에 스프링이 적절한 의존성을 주입해준다는 작업이라는 차이를 가진다.)
이렇게 스프링은 메모리 할당과 초기화 작업을 분리하고 있다. 이는 생성과 다르게 초기화의 경우 외부 API를 연결하는 등의 무거운 작업이 포함될 수 있으며, 메모리 할당과 의존성 주입이라는 다른 목적을 가지고 있기 때문이라고 추측된다.
빈 생명주기 콜백 설정
콜백이란?
자바에서 콜백이란 무엇일까? 구글링을 하다보니 'a function passed as an argument to another function and executed when that function completes or some event happens'이라는 말을 찾아볼 수 있었다.
public class Test {
public static void main(String[] args) throws Exception {
new Test().doWork(new Callback() { // implementing class
@Override
public void call() {
System.out.println("callback called");
}
});
}
public void doWork(Callback callback) {
System.out.println("doing work");
callback.call();
}
public interface Callback {
void call();
}
}
위 코드를 보면 doWork()메소드가 call()이라는 메서드를 호출하고 있다. 콜백이란 이처럼 프레임워크나 라이브러리가 필요할 때 특정 메서드를 호출하여 개발자가 작성한 코드를 실행하는 방식을 말한다.
- 방법 1. 인터페이스 구현
public class NetworkClient implements InitializingBean, DisposableBean {
@Override
public void afterPropertiesSet() throws Exception {...}
@Override
public void destroy() throws Exception {...}
}
InitiializingBean가 afterPropertiesSet()메소드로 초기화를, DisposableBean가 destroy()메소드로 소멸을 해준다.
- 방법 2. 빈 등록시점에 초기화/소멸 메소드 지정
//설정정보 클래스
...
@Bean(initMethod = "init", destroyMethod = "close")
public NetworkClient networkClient() {
NetwordClient networkClient = new NetworkClient();
networdClient.setUrl("http://hello-spring.dev");
return networkClient;
}
설정정보에 빈을 등록할 때 initMethod, destroyMethod옵션을 이용해서 초기화, 소멸 등록할 수 있다.
- 방법 3. 어노테이션 지정
@PostConstruct
public void init() {...}
@PreDestroy
public void close() {...}
어노테이션 하나만 붙여주면 해결된다. 다만 외부 라이브러리에는 적용하지 못하므로 필요하다면 (2)의 방법을 사용하자.
'[ Backend ] > Spring' 카테고리의 다른 글
[Spring] 빈 스코프 (0) | 2022.01.27 |
---|---|
[Spring] NoUniqueBeanDefinitionException (0) | 2022.01.25 |
[Spring] IoC, 컴포넌트 스캔, 의존관계 주입 (0) | 2022.01.24 |
[Spring] @Autowired vs @PersistenceContext (0) | 2022.01.23 |
[Spring] 스프링의 싱글톤 (0) | 2022.01.22 |