이 글은 스프링 [핵심원리 - 기본편]을 듣고 정리한 내용입니다.
ApplicationContext applicationContext =
new
AnnotationConfigApplicationContext(AppConfig.class);
AppicationContext
를 스프링 컨테이너 라고 한다.ApplicationContext
는 인터페이스 이다. new AnnotationConfigApplicationContext(AppConfig.class)
이 클래스는 ApplicationContext
의 구현체이다생성과정 : new AnnotationConfigApplicationContext(AppConfig.class)
스프링 컨테이너를 생성할 때는 구성정보를 지정해주어야 하는데 AppConfig.class
를 구성 정보롤 지정하였다.
빈 이름은 메서드 이름을 사용한다. (주의 : 빈 이름이 겹치면 안됨 .오류발생!)
빈 이름을 직접 부여 할 수도 있다
ex) @Bean(name="memberService2")
화살표는 의존관계를 나타내는 것이다. 방향 : 의존 하는쪽 -> 의존 받는쪽
ex) 그림에서 맨위의 인자에 해당하는 memberRepository()는 아래쪽의 memberRepository()메서드에 의존한다.
스프링 컨테이너는 설정 정보를 참고해서 의존관계를 주입해야 한다.
스프링 빈을 생성하고, 의존관계를 주입하는 단계가 나누어져 있다. 자세한 내용은 뒤에서.
이제 스프링 컨테이너에서 데이터 조회
해볼예정
test
패키지 안에 beanfind
패키지 안에 AnnotationContextInfoTest. class
작성 class ApplicationContextInfoTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
@Test
@DisplayName("모든 빈 출력하기")
void findAllBean(){
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
//List나 배열이 있으면 'iter'입력 후 tab누르면 for문 자동완성 됨.
for (String beanDefinitionName : beanDefinitionNames) {
Object bean = ac.getBean(beanDefinitionName);
System.out.println("name= "+ beanDefinitionName+" object = "+bean);
}
}
@Test
@DisplayName("애플리케이션 빈 출력하기")
void findApplicationBean(){
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
//List나 배열이 있으면 'iter'입력 후 tab누르면 for문 자동완성 됨.
for (String beanDefinitionName : beanDefinitionNames) {
BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName);
if(beanDefinition.getRole()== BeanDefinition.ROLE_APPLICATION){
Object bean = ac.getBean(beanDefinitionName);
System.out.println("name= "+ beanDefinitionName+" object = "+bean);
}
}
}
}
ac.getBeanDefinitioNames()
: 스프링에 등록된 모든 빈 이름 조회ac.getBean()
: 빈 이름으로 객체(인스턴스) 조회getRole()
로 구분할 수 있는데ROLE_APPLICATION
: 일반적으로 사용자가 정의한 빈ROLE_INFRASTRUCTURE
: 스프링이 내부에서 사용하는 빈ac.getBean(빈이름,타입)
ac.getBean(타입)
NoSuchBeanDefinition : No bean named 'xxxx' available
public class ApplicationContextBasicFindTest {
AnnotationConfigApplicationContext ac = new
AnnotationConfigApplicationContext(AppConfig.class);
@Test
@DisplayName("빈 이름으로 조회")
void findBeanByName(){
MemberService memberService = ac.getBean("memberService",MemberService.class);
// System.out.println("memberService = " + memberService);
// System.out.println("memberService.getClass() = "+ memberService.getClass());
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
// memberService가 MemberServiceImpl의 인스턴스이면 성공
}
@Test
@DisplayName("이름 없이 타입으로만 조회")
void findBeanByType(){
MemberService memberService = ac.getBean(MemberService.class);
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
@Test
@DisplayName("구체 타입으로 조회")
void findBeanByName2(){
MemberServiceImpl memberService = ac.getBean("memberService",MemberServiceImpl.class);
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
//테스트는 항상 실패 테스트를 만들어야함
@Test
@DisplayName("빈 이름으로 조회X")
void findBeanByNameX(){
//ac.getBean("xxxx",MemberService.class);
// 예외가 터지면 성공, 안터지면 실패
assertThrows(NoSuchBeanDefinitionException.class,
() -> ac.getBean("xxxx",MemberService.class));
}
}
구체 타입으로 조회
하면 변경시 유연성이 떨어진다. AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SameBeanConfig.class);
package hello.core.beanfind;
public class ApplicationContextSameBeanFindTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SameBeanConfig.class);
@Test
@DisplayName("타입으로 조회시 같은 타입이 둘 이상 있으면, 중복 오류가 발생한다.")
void findBeanByTypeDuplicate() {
MemberRepository bean = ac.getBean(MemberRepository.class);
assertThrows(NoUniqueBeanDefinitionException.class,
() -> ac.getBean(MemberRepository.class));
}
@Test
@DisplayName("타입으로 조회 시 같은 타입이 둘 이상 있으면, 빈 이름을 지정하면 된다")
void findBeanByName(){
MemberRepository memberRepository = ac.getBean("memberRepository1",MemberRepository.class);
assertThat(memberRepository).isInstanceOf(MemberRepository.class);
}
@Test
@DisplayName("특정 타입을 모두 조회하기")
void findAllBeanByType(){
Map<String, MemberRepository> beansOfType = ac.getBeansOfType(MemberRepository.class);
for (String key : beansOfType.keySet()){
System.out.println("key = "+key + " value = "+ beansOfType.get(key));
}
System.out.println("beansOfType = "+ beansOfType);
assertThat(beansOfType.size()).isEqualTo(2);
}
@Configuration
static class SameBeanConfig {
@Bean
public MemberRepository memberRepository1() {
return new MemoryMemberRepository();
}
@Bean
public MemberRepository memberRepository2() {
return new MemoryMemberRepository();
}
}
}
ac.getBeansOfType
를 하고 command+option+v
로 변수를 리턴받으면 Map<String, MemberRepository>
형이 리턴된다.Object
타입으로 조회하면, 스프링 내에 존재하는 모든 스프링 빈이 조회된다.(Object는 모든 자바 객체의 최고 부모이기 때문에)
class ApplicationContextExtendsFindTest {
org.springframework.context.annotation.AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
@Test
@DisplayName("부모 타입으로 조회 시, 자식이 둘 이상 있으면 중복 오류가 발생한다.")
void findBeanByParentTypeDuplicate(){
assertThrows(NoUniqueBeanDefinitionException.class,
() -> ac.getBean(DiscountPolicy.class));
}
@Test
@DisplayName("부모 타입으로 조회 시, 자식이 둘 이상 있으면, 빈 이름을 지정하면 된다.")
void findBeanByParentTypeBeanName() {
DiscountPolicy rateDiscountPolicy = ac.getBean("rateDiscountPolicy", DiscountPolicy.class);
assertThat(rateDiscountPolicy).isInstanceOf(RateDiscountPolicy.class);
}
@Test
@DisplayName("특정 하위 타입으로 조회") //안좋은 방법임
void findBeanBySubType(){
RateDiscountPolicy bean = ac.getBean(RateDiscountPolicy.class);
assertThat(bean).isInstanceOf(RateDiscountPolicy.class);
}
@Test
@DisplayName("부모 타입으로 모두 조회하기")
void findAllBeanByParentType(){
Map<String, DiscountPolicy> beansOfType = ac.getBeansOfType(DiscountPolicy.class);
assertThat(beansOfType.size()).isEqualTo(2);
for( String key : beansOfType.keySet()) {
System.out.println("key = " + key + " value = " + beansOfType.get(key));
}
}
@Test
@DisplayName("부모 타입으로 모두 조회하기 - Object")
void findAllBeanByObjectType(){
Map<String, Object> beansOfType = ac.getBeansOfType(Object.class);
//스프링 내부적으로 존재하는 빈들이 전부 출력된다.
//자바 객체는 모두 Object 객체를 상속하기 때문에.
for (String s : beansOfType.keySet()) {
for( String key : beansOfType.keySet()) {
System.out.println("key = " + key + " value = " + beansOfType.get(key));
}
}
}
@Configuration
static class TestConfig {
@Bean
public DiscountPolicy rateDiscountPolicy() {
return new RateDiscountPolicy();
}
@Bean
public DiscountPolicy fixDiscountPolicy() {
return new FixDiscountPolicy();
}
}
}
applicationContext
에서 빈을 조회할 일은 거의 없다.자동 의존관계 주입
에서 문제없이 이해 할 수 있다.BeanFactory
를 직접 사용할 일은 거의 없고, 대부분 부가기능이 포함된 ApplicationContext
를 사용한다.BeanFacotry
나 ApplicationContext
를 스프링 컨테이너라 한다.스프링 컨테이너는 다양한 형식의 설정 정보를 적용할 수 있게 유연하게 설계되어 있다.
ex) 자바코드, XML, Groovy 등등
지금까지 했던 자바 코드를 이용한 설정 - new AnnotationConfigApplicatio(Appconfig.class)
AnnotationConfigApplicatio
클래스를 사용하면서 자바 코드로된 설정 정보를 넘기면 되었다.
GenericXmlApplicationContext
를 사용하면서 xml
설정파일을 넘기면 된다.test
패키지 안에 xml
패키지 안에 XmlAppContex.classpackage hello.core.xml;
public class XmlAppContext {
@Test
void xmlAppContext(){
ApplicationContext ac = new GenericXmlApplicationContext("appConfig.xml");
MemberService memberService = ac.getBean("memberService", MemberService.class);
assertThat(memberService).isInstanceOf(MemberService.class);
}
}
<?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="memberService" class="hello.core.member.MemberServiceImpl">
<constructor-arg name="memberRepository" ref="memberRepository"/>
</bean>
<bean id="memberRepository" class="hello.core.member.MemoryMemberRepository"></bean>
<bean id="orderService" class="hello.core.order.OrderServiceImpl">
<constructor-arg name="memberRepository" ref="memberRepository"/>
<constructor-arg name="discountPolicy" ref="discountPolicy" />
</bean>
<bean id="discountPolicy" class="hello.core.discount.RateDiscountPolicy"/>
</beans>
appConfig.xml
과 AppConfig.java
를 보면 구조가 거의 비슷하다.(여기는 그냥 참고 부분. 보고 넘어가기)
public class BeanDefinitionTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
// GenericXmlApplicationContext ac = new GenericXmlApplicationContext("appConfig.xml");
@Test
@DisplayName("빈 설정 메타정보 확인")
void findApplicationBean(){
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName);
if(beanDefinition.getRole()==BeanDefinition.ROLE_APPLICATION){
System.out.println("beanDefinitionName = "+ beanDefinitionName + " beanDefinition= "+ beanDefinition);
}
}
}
}
BeanDefinition
정리