해당 내용은 '스프링 입문을 위한 자바 객체 지향의 원리와 이해'와 인프런 김영한님의 '스프링 핵심 원리 - 기본편' 강의를 참고하였습니다.
자 전의 자바 어플리케이션 코드를 이제 우리는 Spring 으로 변경하려고 한다.
우선 ApplicationConfig 파일부터 바꾸자.
다음과 같이 변경하면 된다.
@Configuration
public class ApplicationConfig {
@Bean
public Car carFuelTank() {
return new CarImpl(getFuelTank());
}
@Bean
public FuelTank getFuelTank() {
return new GasolineOil();
// return new ElectricBattery();
}
}
@Configuration
어노테이션과 @Bean
어노테이션을 더해준다.
@Configuration
어노테이션은 Spring 설정 클래스라는 것을 선언한다.
그렇기에 Spring Container는 @Configration
이 붙은 클래스를 설정정보로 사용한다.
@Bean
어노테이션은 해당 메서드에서 반환된 객체들을 Spring Container에 등록시켜준다.
기본적으로는 메서드 이름으로 등록된다.
자 그 다음에는 자바 어플리케이션을 실행하는 쪽을 보자.
기존에는 ApplicationConfig 파일을 통해서 직접 DI를 해주었지만, 이젠 스프링컨테이너를 통해서 해보도록하자.
public class CarApp {
public static void main(String[] args) {
// DI 도입
// ApplicationConfig applicationConfig = new ApplicationConfig();
// Car car = applicationConfig.carFuelTank();
// java to spring
// spring container 생성
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ApplicationConfig.class);
Car car = applicationContext.getBean("carFuelTank", Car.class);
System.out.println("energy = " + car.runEngine().getEnergy());
}
}
위의 코드에서 보이는 ApplicationContext
가 위에서 말한 Spring Container이다.
Spring Container를 생성할 때, configuration 클래스(ApplicationConfig.class
)를 구성정보로 지정해주면 해당 클래스에서 Bean 객체를 다시 등록한다.
보통 Bean의 이름은 기본적으로 클래스명을 사용하되 맨 앞글자만 소문자를 사용한다. 따로 @Bean(name = '???')
로 직접 지정할 수 있다.
추가로 Bean의 이름은 모두 달라야한다. 같은 Bean이 여러개가 존재한다면 기존 Bean을 덮어쓰거나 오류가 발생할 수 있다.
이렇게 Bean들이 생성되면, 스프링 컨테이너는 Configuration 클래스를 참조해서 Bean 간의 의존관계를 주입한다.(DI)
이제 우리의 코드에서 등록한 Bean들이 잘 등록되는지 확인해보자.
간단하게 테스트 코드를 작성해보자.
public class ApplicationContextInfoTest {
// 왜 앞에 interface가 아닌 구현 클래스를 썼냐면..상위 interface에는 bean을 조회하는 메서드가 없기때문
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(ApplicationConfig.class);
@Test
@DisplayName("Application 빈 출력")
void findApplicationBean(){
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
for(String beanDefinitionName : beanDefinitionNames){
BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName);
// BeanDefinition.ROLE_APPLICATION = 직접 등록한 애플리케이션 빈
// BeanDefinition.ROLE_INFRASTRUCTURE = 스프링 내부에서 사용하는 빈
if(beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION){
System.out.println("beanDefinitionName = " + beanDefinitionName);
}
}
}
}
결과는 다음과 같다. (Creating 생략)
beanDefinitionName = applicationConfig
beanDefinitionName = carFuelTank
beanDefinitionName = getFuelTank
우리가 등록한 config 파일(AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(ApplicationConfig.class);
에서 등록) 이외에도 @Bean
으로 등록한 carFuelTank, getFuelTank 두 가지가 잘 등록된 것을 확인할 수 있다.
또한 Bean은 상속 관계도 적용된다.
그래서 부모 클래스에서 Bean을 조회하면, 그 아래의 자식 클래스도 모두 조회된다.