[Spring] 빈을 설정하는 3가지 방법 - XML, JAVA, Component Scan
고전적인 방법 - Spring Bean XML 설정 파일
resources 폴더에 ‘application.xml’ 이름으로 Spring Config XML 파일을 만든다.
application.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="testService" class="com.test.mong.TestService"/>
<bean id="testRepository" class="com.test.mong.TestRepository"/>
</beans>
<bean>
에 빈을 등록 해주면 된다. id, class 속성은 필수 속성이다.
id에 빈의 id를, class에 빈으로 등록 할 클래스의 패키지명 포함 전체 이름을 넣어준다.
여기까지 설정한 내용은 TestService와 TestRepository를 빈으로 만들라는 것이다.
application.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="testService" class="com.test.mong.TestService">
<property name="testRepository" ref="testRepository" />
</bean>
<bean id="testRepository" class="com.test.mong.TestRepository" />
</beans>
빈 주입은 주입받을 빈의 <bean>
태그 하위에 <property>
태그를 이용해서 설정하면 된다.
<property>
의 name 속성에 프로퍼티(setter) 이름을, ref속성에는 주입할 빈의 id를 지정한다.
등록한 빈은 ApplicationContext를 통해 사용할 수 있다.
TestApplication.java
public class TestApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
String[] beanDefinitionNames = context.getBeanDefinitionNames();
System.out.println(Arrays.toString(beanDefinitionNames));
}
}
----------------------------------------------------------------------------------------
[testService, testRepository]
ClassPathXmlApplicationContext()에 application.xml 파일을 넘겨 ApplicationContext를 생성한다.
ApplicationContext의 getBeanDefinitionNames()는 IoC 컨테이너에 등록된 빈의 id 목록을 가져온다.
ApplicationContext의 getBean()으로 빈을 꺼낼 수 있다. String 타입 파라미터에 빈의 id를 넘긴다. (testService, testRepository)
TestApplication.java
public class TestApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
String[] beanDefinitionNames = context.getBeanDefinitionNames();
System.out.println(Arrays.toString(beanDefinitionNames));
TestService testService = (TestService) context.getBean("testService");
System.out.println(testService.testRepository != null);
}
}
----------------------------------------------------------------------------------------
[testService, testRepository]
true
위에 작성한 xml 설정 파일에 의해 TestService 가 testRepositry 빈을 주입받은 것이다.
이 방법은 모든 빈을 <bean>
으로 일일이 등록해줘야 해서 굉장히 번거롭다.
그래서 다음으로 등장한 것이 component scan이다. application.xml을 다음과 같이 설정한다.
application.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.test.mong"/>
</beans>
위 설정은 base-package에 지정한 패키지에서부터 모든 하위 패키지를 scanning해서 빈으로 등록 한다.
당연히 모든 클래스를 빈으로 등록하는 건 아니고 @Component 어노테이션이 붙은 클래스를 빈으로 등록한다.
@Component를 확장한 몇가지 어노테이션이 더 있는데 그중 @Service, @Repository, @Controller가 있다.
이러한 @Component를 확장한 어노테이션을 사용해도 마찬가지로 빈으로 등록된다.
TestService.java
@Service
public class TestService {
TestRepository testRepository;
public void setTestRepository(TestRepository testRepository) {
this.testRepository = testRepository;
}
}
TestRepository.java
@Repository
public class TestRepository {
}
이렇게 TestService 와 TestRepository 클래스에 각각 @Service, @Repository 어노테이션을 붙여준다.
그러면 두 클래스가 component-scan에 의해 빈으로 등록된다.
의존성 주입은 @Autowired 어노테이션을 사용한다.
TestService.java
@Service
public class TestService {
@Autowired
TestRepository testRepository;
public void setTestRepository(TestRepository testRepository) {
this.testRepository = testRepository;
}
}
TestService.java의 TestRepository 변수에 @Autowired를 붙여주자.
이제 빈 등록과 의존성 주입을 위한 설정이 모두 완료되었다.
main()을 실행해보면 이전과 동일한 결과가 나온다.
application.xml 에 일일이 설정하는 대신 어노테이션을 scanning해서 빈을 등록하고 의존성 주입하도록 변경한 것이다.
★ 어노테이션 기반의 빈 등록 및 설정은 spring 2.5부터 추가되었다.
JAVA 설정 파일의 사용
위에서 작성한 application.xml 과 같은 XML 파일이 아닌 java로도 설정파일을 만들 수 있다.
ApplicationConfig.java
@Configureation
public class ApplicationConfig {
@Bean
public TestRepository testRepository() {
return new TestRepository();
}
@bean
public TestService testService() {
TestService testService = new TestService();
testService.setTestRepository(testRepository());
return testService;
}
}
Java 설정 파일을 만들려면 @Configuration을 붙여주고 빈으로 등록할 객체를 리턴하는 메소드를 정의한다.
메소드명이 빈 id, 리턴 타입이 빈 타입, 리턴한 객체가 빈 레퍼런스가 된다.
의존성은 직접 setter를 호출해서 주입해준다.
TestApplication.java
public class TestApplication {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
String[] beanDefinitionNames = context.getBeanDefinitionNames();
System.out.println(Arrays.toString(beanDefinitionNames));
TestService testService = (TestService) context.getBean("testService");
System.out.println(testService.testRepository != null);
}
}
Java로 만든 설정 파일은 AnnotationConfigApplicationContext()에 class를 넘겨서 사용할 수 잇다.
★ AnnotationConfigApplicationContext란?
어노테이션 기반의 빈 config를 사용하는 ApplicationContext 구현체이다.
기존에 붙어있던 @Service, @Repository, @Autowired를 삭제하고 실행해보자.
역시 실행 결과는 동일하다.
Java설정 파일에서 직접 setter를 호출하지 않고 @Autowired를 붙여서 의존성을 주입해주는 것도 가능하다.
ApplicationConfig.java
@Configuration
public class ApplicationConfig {
@Bean
public TestRepository testRepository() {
return new TestRepository();
}
@Bean
public TestService testService() {
return new TestService();
}
}
TestService.java
public class TestService {
@Autowired
TestRepository testRepository;
public void setTestRepository(TestRepository testRepository) {
this.testRepository = testRepository;
}
}
Java 설정 파일에서 직접 setter를 호출해서 의존 관계를 엮어주지 않아도 @Autowired 어노테이션을 붙여주면 의존성 주입이 된다.
JAVA 설정 파일 + Component Scan
위 방법은 XML 설정 파일에서 component scan을 사용하도록 설정하는 것보다 더 번거롭다.
그래서 Java 설정 파일에서도 component scan을 사용할 수 잇다.
ApplicationConfig.java
@Configuration
//@ComponentScan(basePackages = "io.mong")
@ComponentScan(basePackageClasses = ApplicationConfig.class) // 더 type safe한 방법
public class ApplicationConfig {
}
Java 설정 파일 클래스에 @ComponentScan 어노테이션을 붙인다.
Scan을 시작하는 패키지를 지정하는 방법으로 basePackages와 basePackagesClasses의 두 가지 속성을 사용할 수 있는데 basePackages에는 패키지 이름을 문자열로, basePackagesClasses에는 scan을 시작할 위치의 클래스를 지정한다.
basePackagesClasses가 더 type safe한 방법이다.
이렇게 설정해주면 XML 설정 파일에서 <context:component-scan...>
을 설정한 것과 동일하게 동작한다.
이렇게 Java 설정 파일에 @ComponentScan 어노테이션을 붙여서 설정하는 것이 spring boot 기반에서 사용하는 방법과 가장 가까운 방법이다.
역시 실행 결과는 동일하다.
★ 지금까지 ApplicationContext를 직접 만들었는데 Spring은 ApplicationContext를 알아서 만들어준다.
또한 Java 설정 파일로 만들었던 ApplicationConfig.java 같은 파일도 Spring boot를 사용하면 따로 생성 할 필요가 없다.