728x90
반응형

[Spring Framework] Spring Bean Scope 완벽 가이드 | Singleton부터 Prototype까지

 

[Spring Framework] Spring Bean Scope 완벽 가이드 | Singleton부터 Prototype까지

[Spring Framework] Spring Framework에서 @Inject 애너테이션 완벽 가이드 [Spring Framework] Spring Framework에서 @Inject 애너테이션 완벽 가이드[Spring Framework] Spring Boot DI 완벽 정리 | @Resource 활용법 [Spring Framework] Sp

crushed-taro.tistory.com

1. Bean

1. init, destroy method

스프링 빈은 초기화(init)와 소멸화(destroy)의 라이프 사이클을 가지고 있다. 이 라이프 사이클을 이해하면 빈 객체가 생성되고 소멸될 때 추가적인 작업을 수행할 수 있다. 

init-method 속성을 사용하면 스프링이 빈 객체를 생성한 다음 초기화 작업을 수행할 메소드를 지정할 수 있다. 이 메소드는 빈 객체 생성자가 완료된 이후에 호출된다. init-method 속성으로 지정된 메소드는 일반적으로 빈의 초기화를 위해 사용된다.

destroy-method 속성을 사용하면 빈 객체가 소멸될 때 호출할 메소드를 지정할 수 있다. 이 메소드는 ApplicationContext의 close() 메소드가 호출되기 전에 빈 객체가 소멸될 때 호출된다. destroy-method 속성으로 지정 된 메소드는 일반적으로 사용하던 리소스를 반환하기 위해 사용된다.

 

1. java

java 설정 방식으로init-methoddestroy-method 를 테스트 하기 위해 Owner라는 클래스를 추가한다.

public class Owner {
	
	public void openShop() {
		System.out.println("사장님이 가게 문을 열었습니다. 이제 쇼핑을 하실 수 있습니다.");
	}
	
	public void closeShop() {
		System.out.println("사장님이 가게 문을 닫았습니다. 이제 쇼핑을 하실 수 없습니다.");
	}
	
}

 

Owner 타입의 빈 객체를 설정 파일에 등록한다. @Bean 어노테이션에는 initMethoddestoryMethod 라는 속성이 있는데 해당 속성을 Owner 클래스에 정의했던 openShopcloseShop메소드로 설정한다.

@Configuration
public class ContextConfiguration {
	
	...생략
	
	/* init-method로 openShop 메소드를 설정하고 destory-method로 closeShope 메소드를 설정한다. */
	@Bean(initMethod = "openShop", destroyMethod="closeShop")
	public Owner owner() {
		
		return new Owner()
	}

}

 

실행 파일에서 빈 설정 파일을 기반으로 IoC 컨테이너를 생성하고 쇼핑 카트에 상품을 담는 동일한 코드의 끝에 컨테이너를 종료하는 코드를 추가하여 실행해본다.

/* 빈 설정 파일을 기반으로 IoC 컨테이너 생성 */
ApplicationContext context = new AnnotationConfigApplicationContext(ContextConfiguration.class);

...생략

/* init 메소드는 빈 객체 생성 시점에 동작하므로 바로 확인할 수 있지만
 * destroy 메소드는 빈 객체 소멸 시점에 동작하므로 컨테이너가 종료 되지 않을 경우 확인할 수 없다.
 * 가비지 컬렉터가 해당 빈을 메모리에서 지울 때 destroy 메소드가 동작하게 되는데 
 * 메모리에서 지우기 전에 프로세스는 종료되기 때문이다.
 * 따라서 아래와 같이 강제로 컨테이너를 종료시키면 destroy 메소드가 동작할 것이다.
 * */
((AnnotationConfigApplicationContext) context).close();

/*
사장님이 가게 문을 열었습니다. 이제 쇼핑을 하실 수 있습니다.
cart1에 담긴 내용 : [붕어빵 1000 Thu Jun 08 21:58:53 KST 2023, 딸기우유 1500 500]
cart2에 담긴 내용 : [지리산암반수 3000 500]
사장님이 가게 문을 닫았습니다. 이제 쇼핑을 하실 수 없습니다.
*/

 

openShop 메소드가 Owner 객체의 생성 시점에 호출 되고, closeShop 메소드가 Owner객체의 소멸 시점에 호출 되었음을 확인할 수 있다. init 메소드는 빈 객체 생성 시점에 동작하므로 바로 확인할 수 있지만 destroy 메소드는 빈 객체 소멸 시점에 동작하므로 컨테이너가 종료 되지 않을 경우 확인할 수 없다. 따라서 컨테이너를 종료시키면 destroy 메소드의 동작까지 확인 할 수 있다.

 

2. annotation

annotation 방식으로init-method,destroy-method를 테스트 하기 위해Owner라는 클래스를 추가한다.

@Component
public class Owner {
	
	/* init-method와 같은 설정 어노테이션이다. */
	@PostConstruct
	public void openShop() {
		System.out.println("사장님이 가게 문을 오픈하셨습니다. 이제 쇼핑을 하실 수 있습니다.");
	}
	
	/* destroy-method와 같은 설정 어노테이션이다. */
	@PreDestroy
	public void closeShop() {
		System.out.println("사장님이 가게 문을 닫으셨습니다. 이제 쇼핑을 하실 수 없습니다.");
	}
	
}

컴포넌트 스캔을 통해 빈 등록 하기 위해 @Componet 어노테이션을 클래스 위에 설정한다. javax.annotation의 @PostConstruct , @PreDestroy 어노테이션은 @Bean 어노테이션에 init-methoddestroy-method 속성을 설정하는 것과 같은 역할을 한다. 단, 해당 어노테이션을 사용할 수 있도록 라이브러리 의존성이 build.gradle.kts파일에 추가 되어 있어야 한다.

dependencies {
	...생략
	implementation("javax.annotation:javax.annotation-api:1.3.2")
}

Owner타입의 빈 객체를 컴포넌트 스캔을 통해 읽어 등록할 수 있도록 빈 설정 파일에 컴포넌트 스캔 경로를 설정한다. 그 외의 상품과 쇼핑 카트의 빈 등록 코드는 그대로 사용한다.

@Configuration
@ComponentScan("project.initdestroy.subsection02.annotation")
public class ContextConfiguration {
	...생략
}

실행 파일에서 빈 설정 파일을 기반으로 IoC 컨테이너를 생성하고 쇼핑 카트에 상품을 담는 동일한 코드의 끝에 컨테이너를 종료하는 코드를 추가하여 실행해본다.

/* 빈 설정 파일을 기반으로 IoC 컨테이너 생성 */
ApplicationContext context = new AnnotationConfigApplicationContext(ContextConfiguration.class);

...생략

/* init 메소드는 빈 객체 생성 시점에 동작하므로 바로 확인할 수 있지만
 * destroy 메소드는 빈 객체 소멸 시점에 동작하므로 컨테이너가 종료 되지 않을 경우 확인할 수 없다.
 * 가비지 컬렉터가 해당 빈을 메모리에서 지울 때 destroy 메소드가 동작하게 되는데 메모리에서 지우기 전에 프로세스는 종료되기 때문이다.
 * 따라서 아래와 같이 강제로 컨테이너를 종료시키면 destroy 메소드가 동작할 것이다.
 * */
((AnnotationConfigApplicationContext) context).close();

/*
사장님이 가게 문을 열었습니다. 이제 쇼핑을 하실 수 있습니다.
cart1에 담긴 내용 : [붕어빵 1000 Thu Jun 08 21:59:07 KST 2023, 딸기우유 1500 500]
cart2에 담긴 내용 : [지리산암반수 3000 500]
사장님이 가게 문을 닫았습니다. 이제 쇼핑을 하실 수 없습니다.
*/

openShop 메소드가 Owner 객체의 생성 시점에 호출 되고, closeShop 메소드가 Owner 객체의 소멸 시점에 호출 되었음을 확인할 수 있다. 

 

3. xml

xml 설정 방식으init-methoddestroy-method 를 테스트 하기 위해 Owner라는 클래스를 추가한다.

public class Owner {
	
	public void openShop() {
		System.out.println("사장님이 가게 문을 열었습니다. 이제 쇼핑을 하실 수 있습니다.");
	}
	
	public void closeShop() {
		System.out.println("사장님이 가게 문을 닫았습니다. 이제 쇼핑을 하실 수 없습니다.");
	}
	
}

bean configuration file에<bean> 태그를 통해 상품, 쇼핑카트 그리고 Owener 에 대한 빈 등록을 설정한다.Owener 에는 init-methoddestroy-method 설정한다.

<?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="carpBread" class="project.common.Bread">
		<constructor-arg name="name" value="붕어빵"/>
		<constructor-arg name="price" value="1000"/>
		<constructor-arg name="bakedDate" ref="today"/>
	</bean>
	
	<bean id="today" class="java.util.Date" scope="prototype"/>
	
	<bean id="milk" class="project.common.Beverage">
		<constructor-arg name="name" value="딸기우유"/>
		<constructor-arg name="price" value="1500"/>
		<constructor-arg name="capacity" value="500"/>
	</bean>
	
	<bean id="water" class="project.common.Beverage">
		<constructor-arg name="name" value="지리산암반수"/>
		<constructor-arg name="price" value="3000"/>
		<constructor-arg name="capacity" value="500"/>
	</bean>
	
	<bean id="cart" class="project.common.ShoppingCart" scope="prototype"/>

	<bean id="owner" class="project.section02.initdestroy.subsection03.xml.Owner" 
			  init-method="openShop" destroy-method="closeShop"/>
</beans>

실행 파일에서 빈 설정 파일을 기반으로 IoC 컨테이너를 생성하고 쇼핑 카트에 상품을 담는 동일한 코드의 끝에 컨테이너를 종료하는 코드를 추가하여 실행해본다.

/* 빈 설정 파일을 기반으로 IoC 컨테이너 생성 */
ApplicationContext context 
	= new GenericXmlApplicationContext("section02/initdestroy/subsection03/xml/spring-context.xml");

...생략

/* init 메소드는 빈 객체 생성 시점에 동작하므로 바로 확인할 수 있지만
 * destroy 메소드는 빈 객체 소멸 시점에 동작하므로 컨테이너가 종료 되지 않을 경우 확인할 수 없다.
 * 가비지 컬렉터가 해당 빈을 메모리에서 지울 때 destroy 메소드가 동작하게 되는데 
 * 메모리에서 지우기 전에 프로세스는 종료되기 때문이다.
 * 따라서 아래와 같이 강제로 컨테이너를 종료시키면 destroy 메소드가 동작할 것이다.
 * */
((GenericXmlApplicationContext) context).close();

/*
사장님이 가게 문을 열었습니다. 이제 쇼핑을 하실 수 있습니다.
cart1에 담긴 내용 : [붕어빵 1000 Thu Jun 08 22:00:09 KST 2023, 딸기우유 1500 500]
cart2에 담긴 내용 : [지리산암반수 3000 500]
사장님이 가게 문을 닫았습니다. 이제 쇼핑을 하실 수 없습니다.
*/

openShop 메소드가 Owner 객체의 생성 시점에 호출 되고, closeShop 메소드가 Owner 객체의 소멸 시점에 호출 되었음을 확인할 수 있다. 

728x90
반응형
728x90
반응형

[Spring Framework] Spring Framework에서 @Inject 애너테이션 완벽 가이드

 

[Spring Framework] Spring Framework에서 @Inject 애너테이션 완벽 가이드

[Spring Framework] Spring Boot DI 완벽 정리 | @Resource 활용법 [Spring Framework] Spring Boot DI 완벽 정리 | @Resource 활용법[Spring Framework] Spring Boot DI 핵심 가이드 | @Primary와 @Qualifier 제대로 이해하기 [Spring Framework]

crushed-taro.tistory.com

1. Bean

아래 코드는 이번 차시의 테스트에 공통적으로 사용할 Product , Beverage , Bread , ShoppingCart 클래스이다.

 

  • Product(abstract class)
public abstract class Product {
	
	private String name;	//상품명
	private int price;		//상품가격
	
	public Product() {}

	public Product(String name, int price) {
		super();
		this.name = name;
		this.price = price;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getPrice() {
		return price;
	}

	public void setPrice(int price) {
		this.price = price;
	}

 

  • Beverage
public class Beverage extends Product {
	
	private int capacity;	//용량
	
	public Beverage() {
		super();
	}
	
	public Beverage(String name, int price, int capacity) {
		super(name, price);
		this.capacity = capacity;
	}
	
	public int getCapacity() {
		return this.capacity;
	}
	
	public void setCapacity(int capacity) {
		this.capacity = capacity;
	}
	
	@Override
	public String toString() {
		return super.toString() + " " + this.capacity;
	}
}

 

  • Bread
public class Bread extends Product {
	
	private java.util.Date bakedDate;	//생산시간
	
	public Bread() {
		super();
	}
	
	public Bread(String name, int price, java.util.Date bakedDate) {
		super(name, price);
		this.bakedDate = bakedDate;
	}
	
	public java.util.Date getBakedDate() {
		return this.bakedDate;
	}
	
	public void setBakedDate(java.util.Date bakedDate) {
		this.bakedDate = bakedDate;
	}
	
	@Override
	public String toString() {
		return super.toString() + " " + this.bakedDate;
	}
}

 

  • ShoppingCart
public class ShoppingCart {
	
	private final List<Product> items;	//쇼핑카트에 담긴 상품들
	
	public ShoppingCart() {
		items = new ArrayList<>();
	}
	
	public void addItem(Product item) {
		items.add(item);
	}
	
	public List<Product> getItem() {
		return items;
	}
}

 

1. Bean Scope

bean scope란, 스프링 빈이 생성될 때 생성되는 인스턴스의 범위를 의미한다. 스프링에서는 다양한 bean scope를 제공한다.

Bean Scope Description
Singleton 하나의 인스턴스만을 생성하고, 모든 빈이 해당 인스턴스를 공유하여 사용한다.
Prototype 매번 새로운 인스턴스를 생성한다.
Request HTTP 요청을 처리할 때마다 새로운 인스턴스를 생성하고, 요청 처리가 끝나면 인스턴스를 폐기한다. 웹 애플리케이션 컨텍스트에만 해당된다.
Session HTTP 세션 당 하나의 인스턴스를 생성하고, 세션이 종료되면 인스턴스를 폐기한다. 웹 애플리케이션 컨텍스트에만 해당된다.

 

1. singleton

Spring Framework에서 Bean의 기본 스코프는 singleton이다. singleton은 애플리케이션 내에서 하나의 인스턴스만을 생성하고, 모든 빈이 해당 인스턴스를 공유하여 사용한다. 이를 통해 메모리 사용량을 줄일 수 있으며, 성능 향상을 기대할 수 있다.

 

@Configuration
public class ContextConfiguration {
	
	@Bean
	public Product carpBread() {
		
		return new Bread("붕어빵", 1000, new java.util.Date());
	}
	
	@Bean
	public Product milk() {
		
		return new Beverage("딸기우유", 1500, 500);
	}
	
	@Bean
	public Product water() {
		
		return new Beverage("지리산암반수", 3000, 500);
	}
	
	@Bean
	@Scope("singleton")		//기본값
	public ShoppingCart cart() {
		
		return new ShoppingCart();
	}
}

ContextConfiguration 설정 파일에Bread 타입의 붕어빵과 Beaverage 타입의 딸기우유와 지리산암반수, ShoppingCart 타입의 쇼핑카트를 bean으로 등록 한다. @Scope 어노테이션에 기본 값에 해당하는 singleton설정도 기재하였다.

 

/* 빈 설정 파일을 기반으로 IoC 컨테이너 생성 */
ApplicationContext context = new AnnotationConfigApplicationContext(ContextConfiguration.class);

/* 붕어빵, 딸기우유, 지리산 암반수 등의 빈 객체를 반환 받는다. */
Product carpBread = context.getBean("carpBread", Bread.class);
Product milk = context.getBean("milk", Beverage.class);
Product water = context.getBean("water", Beverage.class);

/* 첫 번째 손님이 쇼핑 카트를 꺼낸다. */
ShoppingCart cart1 = context.getBean("cart", ShoppingCart.class);
cart1.addItem(carpBread);
cart1.addItem(milk);

/* 붕어빵과 딸기우유가 담겨있다. */
System.out.println("cart1에 담긴 내용 : " + cart1.getItem());

/* 두 번째 손님이 쇼핑 카트를 꺼낸다. */		
ShoppingCart cart2 = context.getBean("cart", ShoppingCart.class);
cart2.addItem(water);

/* 붕어빵과 딸기우유와 지리산암반수가 담겨있다. */
System.out.println("cart2에 담긴 내용 : " + cart2.getItem());
		
/* 두 카드의 hashcode를 출력해보면 동일한 것을 볼 수 있다. */
System.out.println("cart1의 hashcode : " + cart1.hashCode());
System.out.println("cart2의 hashcode : " + cart2.hashCode());

/*
cart1에 담긴 내용 : [붕어빵 1000 Thu Jun 08 21:58:27 KST 2023, 딸기우유 1500 500]
cart2에 담긴 내용 : [붕어빵 1000 Thu Jun 08 21:58:27 KST 2023, 딸기우유 1500 500, 지리산암반수 3000 500]
cart1의 hashcode : 696933920
cart2의 hashcode : 696933920
*/

이 예제에서 손님 두 명이 각각 쇼핑 카트를 이용해 상품을 담는다고 가정했지만singleton으로 관리되는 cart는 사실 하나의 객체이므로 두 손님이 동일한 카트에 물건을 담는 상황이 발생한다. 상황에 따라서 기본 값인 singleton 스코프가 아닌 prototype 스코프가 필요할 수 있다. 

 

2. prototype

prototype 스코프를 갖는 Bean은 매번 새로운 인스턴스를 생성한다. 이를 통해 의존성 주입 등의 작업에서 객체 생성에 대한 부담을 줄일 수 있다.
@Configuration
public class ContextConfiguration {
	
	@Bean
	public Product carpBread() {
		
		return new Bread("붕어빵", 1000, new java.util.Date());
	}
	
	@Bean
	public Product milk() {
		
		return new Beverage("딸기우유", 1500, 500);
	}
	
	@Bean
	public Product water() {
		
		return new Beverage("지리산암반수", 3000, 500);
	}
	
	@Bean
	@Scope("prototype")		//기본 값에서 변경
	public ShoppingCart cart() {
		
		return new ShoppingCart();
	}

}

이전 예제와 동일하게 ContextConfiguration 설정 파일에 Bread 타입의 붕어빵과 Beaverage 타입의 딸기우유와 지리산암반수, ShoppingCart 타입의 쇼핑카트를 bean으로 등록 한다. 단, ShoppingCart의 경우 @Scope 어노테이션에 기본 값이 아닌 해당하는 prototype설정도 기재하였다.

 

/* 빈 설정 파일을 기반으로 IoC 컨테이너 생성 */
ApplicationContext context = new AnnotationConfigApplicationContext(ContextConfiguration.class);

/* 붕어빵, 딸기우유, 지리산 암반수 등의 빈 객체를 반환 받는다. */
Product carpBread = context.getBean("carpBread", Bread.class);
Product milk = context.getBean("milk", Beverage.class);
Product water = context.getBean("water", Beverage.class);

/* 첫 번째 손님이 쇼핑 카트를 꺼낸다. */
ShoppingCart cart1 = context.getBean("cart", ShoppingCart.class);
cart1.addItem(carpBread);
cart1.addItem(milk);

/* 붕어빵과 딸기우유가 담겨있다. */
System.out.println("cart1에 담긴 내용 : " + cart1.getItem());

/* 두 번째 손님이 쇼핑 카트를 꺼낸다. */		
ShoppingCart cart2 = context.getBean("cart", ShoppingCart.class);
cart2.addItem(water);

/* 지리산암반수가 담겨있다. */
System.out.println("cart2에 담긴 내용 : " + cart2.getItem());
		
/* 두 카드의 hashcode를 출력해보면 다른 것을 볼 수 있다. */
System.out.println("cart1의 hashcode : " + cart1.hashCode());
System.out.println("cart2의 hashcode : " + cart2.hashCode());

/*
cart1에 담긴 내용 : [붕어빵 1000 Thu Jun 08 21:58:40 KST 2023, 딸기우유 1500 500]
cart2에 담긴 내용 : [지리산암반수 3000 500]
cart1의 hashcode : 1946988038
cart2의 hashcode : 1990519794
*/

ShoppingCart의 bean scope를prototype으로 설정하자 getBean으로 인스턴스를 꺼내올 때 마다 새로운 인스턴스를 생성하게 된다. 따라서 이번 예제에서는 손님 두 명이 각각 쇼핑 카트를 이용해 상품을 담는 상황이 잘 연출되었다.

 

3. xml 설정

의 예제에서는 모두 Java 빈 객체 설정을 통해 확인해보았다. 만약 XML 파일에<bean> 태그를 이용한다면 다음과 같이 속성을 기재할 수 있다.

<!-- singleton 설정 --> 
<bean id="cart" class="패키지명.ShoppingCart" scope="singleton"/>

<!-- prototype 설정 -->
<bean id="cart" class="패키지명.ShoppingCart" scope="prototype"/>

XML 설정에 대한 별도의 실행 예제는 생략한다.

728x90
반응형
728x90
반응형

[Spring Framework] Spring Boot DI 완벽 정리 | @Resource 활용법

 

[Spring Framework] Spring Boot DI 완벽 정리 | @Resource 활용법

[Spring Framework] Spring Boot DI 핵심 가이드 | @Primary와 @Qualifier 제대로 이해하기 [Spring Framework] Spring Boot DI 핵심 가이드 | @Primary와 @Qualifier 제대로 이해하기[Spring Framework] Spring Framework 의존성 주입(DI)

crushed-taro.tistory.com

1. DI Annotation

1. @Inject

@Inject 어노테이션은 자바에서 제공하는 기본 어노테이션이다. @Autowired 어노테이션과 같이 Type으로 빈을 의존성 주입한다.

당 어노테이션은 사용하기 전 라이브러리 의존성 추가가 필요하므로 Maven Repository에서javax inject을 검색하여 build.gradle.kts파일에 아래와 같은 구문을 추가한다.

dependencies {
	implementation("javax.inject:javax.inject:1")
	...생략
}

 

1. 필드 주입

드로Pokemon 타입의 객체를 의존성 주입 받는 PokemonService 클래스를 선언한다. @Inject 어노테이션은 Type 으로 의존성 주입하므로 3개의 동일한 타입의 빈이 있는 현재 상황에서는 오류가 발생한다. 따라서 @Named어노테이션을 함께 사용해서 빈의 이름을 지정하면 해당 빈을 의존성 주입할 수 있다.

@Service("pokemonServiceInject")
public class PokemonService {

	/* 1. 필드 주입 */
	@Inject
	@Named("pikachu")
	private Pokemon pokemon;

	public void pokemonAttack() {
		pokemon.attack();
	}
}

CharmanderPikachuSquirtlePokemonService 를 빈 스캐닝 할 수 있는 basePackages를 설정하여 스프링 컨테이너를 생성한다.

ApplicationContext context = new AnnotationConfigApplicationContext("project");

PokemonService pokemonService = context.getBean("pokemonServiceInject", PokemonService.class);
		
pokemonService.pokemonAttack();

/*
피카츄 백만볼트⚡
*/

 

2. 생성자 주입

성자로Pokemon 타입의 객체를 의존성 주입 받는 PokemonService클래스를 선언한다.

@Service("pokemonServiceInject")
public class PokemonService {

	private Pokemon pokemon;

	/* 2. 생성자 주입 */
	@Inject
	public PokemonService(@Named("pikachu") Pokemon pokemon) {
		this.pokemon = pokemon;
	}

	public void pokemonAttack() {
		pokemon.attack();
	}
}

/*
피카츄 백만볼트⚡
*/

@Named어노테이션의 경우 메소드 레벨, 파라미터 레벨에서 둘 다 사용 가능하다.

 

3. 세터 주입

터로Pokemon타입의 객체를 의존성 주입 받는PokemonService클래스를 선언한다.

@Service("pokemonServiceInject")
public class PokemonService {

	private Pokemon pokemon;

	/* 3. 세터 주입 */
	@Inject
	public void setPokemon(@Named("pikachu") Pokemon pokemon) {
		this.pokemon = pokemon;
	}

	public void pokemonAttack() {
		pokemon.attack();
	}
}

/*
피카츄 백만볼트⚡
*/

@Inject는 필드 주입생성자 주입세터 주입모두 가능하다.

 

정리

DI는 스프링 프레임워크에서 매우 중요한 개념 중 하나로, 개발자는 객체 간의 의존성을 직접 관리하지 않고 스프링 컨테이너가 객체 간의 의존성을 주입해주는 방식으로 관리할 수 있다.

다양한 DI 어노테이션이 있는데 각각의 특징과 사용 방식이 다르다.

  @Autowried @Resource @Inject
제공 Spring Java Java
지원 방식 필드, 생성자, 세터 필드, 세터 필드, 생성자, 세터
빈 검색 우선 순위 타입 → 이름 이름 → 타입 타입 → 이름
빈 지정 문법 @Autowired
@Qualifier(”name”)
@Resource(name=”name”) @Inject
@Named(”name”)
728x90
반응형
728x90
반응형

[Spring Framework] Spring Boot DI 핵심 가이드 | @Primary와 @Qualifier 제대로 이해하기

 

[Spring Framework] Spring Boot DI 핵심 가이드 | @Primary와 @Qualifier 제대로 이해하기

[Spring Framework] Spring Framework 의존성 주입(DI) 어노테이션 총정리 [Spring Framework] Spring Framework 의존성 주입(DI) 어노테이션 총정리[Spring Framework] Spring Boot에서 Dependency Injection 완벽 가이드 | 개념부터

crushed-taro.tistory.com

1. Collection

  • 같은 타입의 빈을 여러 개 주입 받고 싶다면Collection타입을 활용할 수 있다.

 

1. List 타입

List<Pokemon>타입의 객체를 의존성 주입 받는PokemonService래스를 선언한다.

@Service("pokemonServiceCollection")
public class PokemonService {
	
	/* 1. List 타입으로 주입 */
	private List<Pokemon> pokemonList;

	@Autowired
	public PokemonService(List<Pokemon> pokemonList) {
		this.pokemonList = pokemonList;
	}

	public void pokemonAttack() {
		pokemonList.forEach(Pokemon::attack);
	}
}

CharmanderPikachuSquirtlePokemonService 를 빈 스캐닝 할 수 있는 basePackages를 설정하여 스프링 컨테이너를 생성한다.

ApplicationContext context = new AnnotationConfigApplicationContext("project");

PokemonService pokemonService = context.getBean("pokemonServiceCollection", PokemonService.class);
		
pokemonService.pokemonAttack();

/*
파이리 불꽃 공격🔥
피카츄 백만볼트⚡
꼬부기 물대포 발사🌊
*/

bean 이름의 사전순으로 List에 추가 되어 모든 Pokemon 타입의 빈이 주입 된다.

 

2. Map 타입

Map<String, Pokemon>타입의 객체를 의존성 주입 받는PokemonService클래스를 선언한다.

@Service("pokemonServiceCollection")
public class PokemonService {
	
	/* 2. Map 타입으로 주입 */
	private Map<String, Pokemon> pokemonMap;
	
	@Autowired
	public PokemonService(Map<String, Pokemon> pokemonMap) {
		this.pokemonMap = pokemonMap;
	}
	
	public void pokemonAttack() {
      pokemonMap.forEach((k, v) -> {
          System.out.println("key : " + k);
          System.out.print("공격 : ");
          v.attack();
      });
  }
}

/*
key : charmander
공격 : 파이리 불꽃 공격🔥
key : pikachu
공격 : 피카츄 백만볼트⚡
key : squirtle
공격 : 꼬부기 물대포 발사🌊
*/

bean 이름의 사전순으로 Map에 추가 되어 모든 Pokemon 타입의 빈이 주입 된다.

 

2. Resource

@Resource 어노테이션은 자바에서 제공하는 기본 어노테이션이다. @Autowired와 같은 스프링 어노테이션과 다르게 
name 속성 값으로 의존성 주입을 할 수 있다.

해당 어노테이션은 사용하기 전 라이브러리 의존성 추가가 필요하므로 Maven Repository에서javax annoataion을 검색하여 build.gradle.kts파일에 아래와 같은 구문을 추가한다.

dependencies {
	implementation("javax.annotation:javax.annotation-api:1.3.2")
	...생략
}

 

1. 이름으로 주입

드로Pokemon 타입의 객체를 의존성 주입 받는 PokemonService 클래스를 선언한다. @Resource 어노테이션의 name속성에 주입할 빈 객체의 이름을 지정한다.

@Service("pokemonServiceResource")
public class PokemonService {
  
  /* pikachu 이름의 빈 지정 */
	@Resource(name = "pikachu")
	private Pokemon pokemon;

	public void pokemonAttack() {
		pokemon.attack();
	}
}

CharmanderPikachuSquirtlePokemonService 를 빈 스캐닝 할 수 있는 basePackages를 설정하여 스프링 컨테이너를 생성한다.

ApplicationContext context = new AnnotationConfigApplicationContext("project");

PokemonService pokemonService = context.getBean("pokemonServiceResour", PokemonService.class);
		
pokemonService.pokemonAttack();

/*
피카츄 백만볼트⚡
*/

 

2. 타입으로 주입

List<Pokemon> 타입으로 변경한 뒤 name 속성을 따로 기재하지 않고 동작시킬 수 있다. 기본적으로는 name속성을 통해 주입하지만 name 속성이 없을 경우 Type을 통해 의존성 주입한다.

@Service("pokemonServiceResource")
public class PokemonService {
  
  @Resource
	private List<Pokemon> pokemonList;

	public void pokemonAttack() {
		pokemonList.forEach(Pokemon::attack);
	}
}

/*
파이리 불꽃 공격🔥
피카츄 백만볼트⚡
꼬부기 물대포 발사🌊
*/

bean 이름의 사전순으로 List에 추가 되어 모든 Pokemon 타입의 빈이 주입 된다.

728x90
반응형
728x90
반응형

[Spring Framework] Spring Framework 의존성 주입(DI) 어노테이션 총정리

 

[Spring Framework] Spring Framework 의존성 주입(DI) 어노테이션 총정리

[Spring Framework] Spring Boot에서 Dependency Injection 완벽 가이드 | 개념부터 실습까지 [Spring Framework] Spring Boot에서 Dependency Injection 완벽 가이드 | 개념부터 실습까지[Spring Framework] Annotation-based Configuration

crushed-taro.tistory.com

1. DI Annotation

  • @Autowired 어노테이션은 가장 보편적으로 사용 되는 의존성 주입 Annotation이다. @Autowired 와 함께 사용하거나 또는 대체해서 사용할 수 있는 어노테이션을 학습한다.
  • 아래 코드는 테스트에 공통적으로 사용 할 Pokemon, Charmander, Pikachu, Squirtle 클래스이다.

 

  • Pokemon
public interface Pokemon {
	
	/* 공격하다 */
	void attack();
}
  • Charmander
@Component
public class Charmander implements Pokemon {

	@Override
	public void attack() {
		System.out.println("파이리 불꽃 공격🔥");
	}
}
  • Pikachu
@Component
public class Pikachu implements Pokemon {

	@Override
	public void attack() {
		System.out.println("피카츄 백만볼트⚡");
	}
}
  • Squirtle
@Component
public class Squirtle implements Pokemon {

	@Override
	public void attack() {
		System.out.println("꼬부기 물대포 발사🌊");
	}
}

 

1. @Primary

@Primary 어노테이션은 여러 개의 빈 객체 중에서 우선순위가 가장 높은 빈 객체를 지정하는 어노테이션이다.

생성자로 Pokemon 타입의 객체를 의존성 주입 받는 PokemonService클래스를 선언한다.

@Service("pokemonServicePrimary")
public class PokemonService {
	
	private Pokemon pokemon;
	
	@Autowired
	public PokemonService(Pokemon pokemon) {
		this.pokemon = pokemon;
	}
	
	public void pokemonAttack() {
		pokemon.attack();
	}

}

CharmanderPikachuSquirtlePokemonService 를 빈 스캐닝 할 수 있는 basePackages를 설정하여 스프링 컨테이너를 생성한다.

ApplicationContext context = new AnnotationConfigApplicationContext("project");

PokemonService pokemonService = context.getBean("pokemonServicePrimary", PokemonService.class);
		
pokemonService.pokemonAttack();

/*
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: 
Error creating bean with name 'pokemonServicePrimary' defined in file 파일 경로 : 
Unsatisfied dependency expressed through constructor parameter 0; 
nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: 
No qualifying bean of type 'project.common.Pokemon' available: 
expected single matching bean but found 3: charmander,pikachu,squirtle
...생략
*/

스프링 컨테이너 내부에 Pokemon 타입의 빈 객체가 charmander,pikachu,squirtle 3개가 있어 1개의 객체를 PokemonService의 생성자로 전달할 수 없어 오류가 발생했음을 확인할 수 있다.

 

Charmander,Pikachu,Squirtle중에서Charmander빈 객체를 우선적으로 주입받도록@Primary어노테이션을 설정한다.

@Component
@Primary
public class Charmander implements Pokemon {

    @Override
    public void attack() {
        System.out.println("파이리 불꽃 공격🔥");
    }
}

/*
파이리 불꽃 공격🔥
*/

@Primary 어노테이션을 설정하면 @Autowired로 동일한 타입의 여러 빈을 찾게 되는 경우 자동으로 연결 우선 시 할 타입으로 설정 된다.

동일한 타입의 클래스 중 한 개만 @Primary 어노테이션을 사용할 수 있다.

Charmander 빈 객체에 @Primary 어노테이션이 설정되어 있으므로, PokemonService의 생성자로 Pokemon 객체를 주입받으면 Charmander 빈 객체가 우선적으로 주입된다.

 

2. @Qualifier

@Qualifierb어노테이션은 여러 개의 빈 객체 중에서 특정 빈 객체를 이름으로 지정하는 어노테이션이다. 

 

1. 필드 주입

드로Pokemon 타입의 객체를 의존성 주입 받는 PokemonService 클래스를 선언한다. @Autowired 어노테이션과 함께 @Qualifier 어노테이션을 사용하여 빈 이름을 통해 주입할 빈 객체를 지정한다.

@Service("pokemonServiceQualifier")
public class PokemonService {
	
  /* @Qualifier 어노테이션을 사용하여 pikachu 빈 객체를 지정한다. */
	@Autowired
	@Qualifier("pikachu")
	private Pokemon pokemon;
	
	public void pokemonAttack() {
		pokemon.attack();
	
}

CharmanderPikachuSquirtlePokemonService 를 빈 스캐닝 할 수 있는 basePackages를 설정하여 스프링 컨테이너를 생성한다.

ApplicationContext context = new AnnotationConfigApplicationContext("project");

PokemonService pokemonService = context.getBean("pokemonServiceQualifier", PokemonService.class);
		
pokemonService.pokemonAttack();

/*
피카츄 백만볼트⚡
*/

@Primary 어노테이션과 @Qualifier 어노테이션이 함께 쓰였을 때 @Qualifier 우선한다는 것도 결과를 통해 확인할 수 있다.

 

2. 생성자 주입

생성자 주입의 경우 @Qualifier 어노테이션은 메소드의 파라미터 앞에 기재한다. 역시 빈 이름을 통해 주입할 빈 객체를 지정한다.

@Service("pokemonServiceQualifier")
public class PokemonService {
	
	private Pokemon pokemon;

  /* @Qualifier 어노테이션을 사용하여 squirtle 빈 객체를 지정한다. */
	@Autowired
	public PokemonService(@Qualifier("squirtle") Pokemon pokemon) {
		this.pokemon = pokemon;
	}
	
	public void pokemonAttack() {
		pokemon.attack();
	}
}

/*
꼬부기 물대포 발사🌊
*/
728x90
반응형
728x90
반응형

[Spring Framework] Spring Boot에서 Dependency Injection 완벽 가이드 | 개념부터 실습까지

 

[Spring Framework] Spring Boot에서 Dependency Injection 완벽 가이드 | 개념부터 실습까지

[Spring Framework] Annotation-based Configuration이란? Spring 설정을 더 간결하게! [Spring Framework] Annotation-based Configuration이란? Spring 설정을 더 간결하게![Spring Framework] Spring IoC 컨테이너 사용법 완벽 정리 | 실

crushed-taro.tistory.com

1. DI Annotation (@Autowired)

@Autowired 어노테이션은 Type을 통한 DI를 할 때 사용한다. 스프링 컨테이너가 알아서 해당 타입의 Bean을 찾아서 주입해준다.

아래 코드는 테스트에 공통적으로 사용 할 BookDTOBookDAOBookDAOImpl 클래스이다.

 

  • BookDTO
@Data
@AllArgsConstructor
public class BookDTO {

    private int sequence;        //도서번호
    private int isbn;            //isbn
    private String title;        //제목
    private String author;       //저자
    private String publisher;    //출판사
    private Date createdDate;    //출판일

}

 

  • BookDAO
public interface BookDAO {

    /* 도서 목록 전체 조회 */
    List<BookDTO> selectBookList();

    /* 도서 번호로 도서 조회 */
    BookDTO selectOneBook(int sequence);
}

 

  • BookDAOImpl
/* @Repository : @Component의 세분화 어노테이션의 한 종류로 DAO 타입의 객체에 사용한다. */
@Repository("bookDAO")
public class BookDAOImpl implements BookDAO {

    private Map<Integer, BookDTO> bookList;

    public BookDAOImpl() {
        bookList = new HashMap<>();
        bookList.put(1, new BookDTO(1, 123456, "자바의 정석", "남궁성", "도우출판", new Date()));
        bookList.put(2, 
					new BookDTO(2, 654321, "칭찬은 고래도 춤추게 한다", "고래", "고래출판", new Date()));
    }

    @Override
    public List<BookDTO> selectBookList() {
        return new ArrayList<>(bookList.values());
    }

    @Override
    public BookDTO selectOneBook(int sequence) {
        return bookList.get(sequence);
    }
}

 

1. 필드(field) 주입

/* @Service : @Component의 세분화 어노테이션의 한 종류로 Service 계층에서 사용한다. */
@Service("bookServiceField")
public class BookService {

    /* BookDAO 타입의 빈 객체를 이 프로퍼티에 자동으로 주입해준다. */
    @Autowired
    private BookDAO bookDAO;

		/* 도서 목록 전체 조회 */
    public List<BookDTO> selectAllBooks(){

        return bookDAO.selectBookList();
    }

		/* 도서 번호로 도서 조회 */
    public BookDTO searchBookBySequence(int sequence) {

        return bookDAO.selectOneBook(sequence);
    }
}

private BookDAO bookDAO = new BookDAOImpl(); 와 같이 필드를 선언한다면 BookService 클래스는 BookDAOImpl 클래스의 변경에 직접적으로 영향을 받는 강한 결합을 가지게 된다. 객체간의 결합을 느슨하게 하기 위해 new BookDAOImpl() 와 같은 직접적으로 객체를 생성하는 생성자 구문을 제거하고 필드에 @Autowired 어노테이션을 작성할 수 있다. 그러면 스프링 컨테이너는 BookService 빈 객체 생성 시 BookDAO 타입의 빈 객체를 찾아 의존성을 주입해준다.

 

스프링 컨테이너를 생성하여 @Repository@Service 등의 어노테이션이 작성 된 클래스가 빈 스캐닝을 통해 잘 등록 되었는지, 또한 객체의 의존 관계에 따라 @Autowired어노테이션을 통해 의존성 주입이 되었는지를 테스트한다.

/* AnnotationConfigApplicationContext 생성자에 basePackages 문자열을 전달하며 ApplicationContext 생성한다. */
ApplicationContext context = new AnnotationConfigApplicationContext("project");

BookService bookService = context.getBean("bookServiceField", BookService.class);

/* 전체 도서 목록 조회 후 출력 확인 */
bookService.selectAllBooks().forEach(System.out::println);

/* 도서번호로 검색 후 출력 확인*/
System.out.println(bookService.searchBookBySequence(1));
System.out.println(bookService.searchBookBySequence(2));

/*
BookDTO(sequence=1, isbn=123456, title=자바의 정석, author=남궁성, publisher=도우출판, 
	createdDate=Sun May 28 20:19:12 KST 2023)
BookDTO(sequence=2, isbn=654321, title=칭찬은 고래도 춤추게 한다, author=고래, publisher=고래출판, 
	createdDate=Sun May 28 20:19:12 KST 2023)
BookDTO(sequence=1, isbn=123456, title=자바의 정석, author=남궁성, publisher=도우출판, 
	createdDate=Sun May 28 20:19:12 KST 2023)
BookDTO(sequence=2, isbn=654321, title=칭찬은 고래도 춤추게 한다, author=고래, publisher=고래출판, 
	createdDate=Sun May 28 20:19:12 KST 2023)
*/

 

2. 생성자(constructor) 주입

/* @Service : @Component의 세분화 어노테이션의 한 종류로 Service 계층에서 사용한다. */
@Service("bookServiceConstructor")
public class BookService {

    private final BookDAO bookDAO;

		/* BookDAO 타입의 빈 객체를 생성자에 자동으로 주입해준다. */
    @Autowired
    public BookService(BookDAO bookDAO) {
        this.bookDAO = bookDAO;
    }

    public List<BookDTO> selectAllBooks(){

        return bookDAO.selectBookList();
    }

    public BookDTO searchBookBySequence(int sequence) {

        return bookDAO.selectOneBook(sequence);
    }

}

생성자에도 @Autowired 어노테이션을 작성할 수 있다. 그러면 스프링 컨테이너는 BookService 빈 객체 생성 시 BookDAO 타입의 빈 객체를 찾아 의존성을 주입해준다.

Spring 4.3 버전 이후로는 생성자가 한 개 뿐이라면 @Autowired 어노테이션을 생략해도 자동으로 생성자 주입이 동작한다. 단, 생성자가 1개 이상일 경우에는 명시적으로 작성을 해주어야 한다. 위의 코드에 기본 생성자를 추가로 작성하고 매개변수 생성자에 @Autowired 어노테이션을 생략하게 되면 생성자 주입이 동작하지 않아 오류가 발생한다.

 

생성자 주입의 장점

  • 객체가 생성 될 때 모든 의존성이 주입 되므로 의존성을 보장할 수 있다.
    • 필드 주입/세터 주입은 의존성이 있는 객체가 생성되지 않아도 객체 생성은 가능하여 메소드가 호출 되면(런타임) 오류가 발생한다.
    • 생성자 주입은 의존성이 있는 객체가 생성되지 않으면 객체 생성이 불가능하여 어플리케이션 실행 시점에 오류가 발생한다.
  • 객체의 불변성을 보장할 수 있다.
    • 필드에 final 키워드를 사용 할 수 있고 객체 생성 이후 의존성을 변경할 수 없어 안정성이 보장 된다.
  • 코드 가독성이 좋다.
    • 해당 객체가 어떤 의존성을 가지고 있는지 명확히 알 수 있다.
  • DI 컨테이너와의 결합도가 낮기 때문에 테스트 하기 좋다.
    • 스프링 컨테이너 없이 테스트를 할 수 있다.

 

프링 컨테이너를 생성하여@Repository@Service 등의 어노테이션이 작성 된 클래스가 빈 스캐닝을 통해 잘 등록 되었는지, 또한 객체의 의존 관계에 따라 @Autowired어노테이션을 통해 의존성 주입이 되었는지를 테스트한다.

/* AnnotationConfigApplicationContext 생성자에 basePackages 문자열을 전달하며 ApplicationContext 생성한다. */
ApplicationContext context = new AnnotationConfigApplicationContext("project");

BookService bookService = context.getBean("bookServiceConstructor", BookService.class);

/* 전체 도서 목록 조회 후 출력 확인 */
bookService.selectAllBooks().forEach(System.out::println);

/* 도서번호로 검색 후 출력 확인*/
System.out.println(bookService.searchBookBySequence(1));
System.out.println(bookService.searchBookBySequence(2));

/*
BookDTO(sequence=1, isbn=123456, title=자바의 정석, author=남궁성, publisher=도우출판, 
	createdDate=Sun May 28 20:19:12 KST 2023)
BookDTO(sequence=2, isbn=654321, title=칭찬은 고래도 춤추게 한다, author=고래, publisher=고래출판, 
	createdDate=Sun May 28 20:19:12 KST 2023)
BookDTO(sequence=1, isbn=123456, title=자바의 정석, author=남궁성, publisher=도우출판, 
	createdDate=Sun May 28 20:19:12 KST 2023)
BookDTO(sequence=2, isbn=654321, title=칭찬은 고래도 춤추게 한다, author=고래, publisher=고래출판, 
	createdDate=Sun May 28 20:19:12 KST 2023)
*/

 

3. 세터(setter) 주입

/* @Service : @Component의 세분화 어노테이션의 한 종류로 Service 계층에서 사용한다. */
@Service("bookServiceSetter")
public class BookService {

    private BookDAO bookDAO;

    /* BookDAO 타입의 빈 객체를 setter에 자동으로 주입해준다. */
    @Autowired
    public void setBookDAO(BookDAO bookDAO) {
        this.bookDAO = bookDAO;
    }

    public List<BookDTO> selectAllBooks(){

        return bookDAO.selectBookList();
    }

    public BookDTO searchBookBySequence(int sequence) {

        return bookDAO.selectOneBook(sequence);
    }
}

setter 메소드에도 @Autowired 어노테이션을 작성할 수 있다. 그러면 스프링 컨테이너는 BookService 빈 객체 생성 시 BookDAO 타입의 빈 객체를 찾아 의존성을 주입해준다. 

 

스프링 컨테이너를 생성하여 @Repository@Service 등의 어노테이션이 작성 된 클래스가 빈 스캐닝을 통해 잘 등록 되었는지, 또한 객체의 의존 관계에 따라 @Autowired어노테이션을 통해 의존성 주입이 되었는지를 테스트한다.

/* AnnotationConfigApplicationContext 생성자에 basePackages 문자열을 전달하며 ApplicationContext 생성한다. */
ApplicationContext context = new AnnotationConfigApplicationContext("project");

BookService bookService = context.getBean("bookServiceSetter", BookService.class);

/* 전체 도서 목록 조회 후 출력 확인 */
bookService.selectAllBooks().forEach(System.out::println);

/* 도서번호로 검색 후 출력 확인*/
System.out.println(bookService.searchBookBySequence(1));
System.out.println(bookService.searchBookBySequence(2));

/*
BookDTO(sequence=1, isbn=123456, title=자바의 정석, author=남궁성, publisher=도우출판, 
	createdDate=Sun May 28 20:19:12 KST 2023)
BookDTO(sequence=2, isbn=654321, title=칭찬은 고래도 춤추게 한다, author=고래, publisher=고래출판, 
	createdDate=Sun May 28 20:19:12 KST 2023)
BookDTO(sequence=1, isbn=123456, title=자바의 정석, author=남궁성, publisher=도우출판, 
	createdDate=Sun May 28 20:19:12 KST 2023)
BookDTO(sequence=2, isbn=654321, title=칭찬은 고래도 춤추게 한다, author=고래, publisher=고래출판, 
	createdDate=Sun May 28 20:19:12 KST 2023)
*/
728x90
반응형
728x90
반응형

[Spring Framework] Annotation-based Configuration이란? Spring 설정을 더 간결하게!

 

[Spring Framework] Annotation-based Configuration이란? Spring 설정을 더 간결하게!

[Spring Framework] Spring IoC 컨테이너 사용법 완벽 정리 | 실전 예제로 이해하는 DI [Spring Framework] Spring IoC 컨테이너 사용법 완벽 정리 | 실전 예제로 이해하는 DI[Spring Framework] 스프링 IoC 컨테이너 이해

crushed-taro.tistory.com

1. Dependency Injection

1. Dependency Injection이란?

1. Dependency Injection

Dependency Injection(의존성 주입, 이하 DI)은 객체 간의 의존 관계를 빈 설정 정보를 바탕으로 컨테이너가 자동적으로 연결해주는 것을 말한다. 이를 통해 객체 간의 결합도를 낮출 수 있으며 이로 인해 유지보수성과 유연성이 증가한다.

 

2. 의존 관계와 결합도

public class A {
	
	private B b = new B();

}

public class B {

}

class A가 class B를 필드로 가질 때 A는 B에 의존하게 된다.

public class A {

	/* 컴파일 에러 발생 */
	private B b = new B();

}

/* 클래스명이 B에서 NewB로 변경 */
public class NewB {
	
}

존성이 강하다는 것은 한 객체가 변경되면 이에 의존하는 다른 객체들도 함께 변경되어야 한다는 것을 의미한다.B가 NewB 로 변경되면 해당 클래스를 필드로 가지고 있는 A 도 변경되어야 할 것이다. 이처럼 객체 간의 의존 관계가 강하게 묶여있을 때 결합도 가 높다고 표현한다. 이로 인해 유지보수성과 유연성이 저하될 수 있다.

public class A {

	/* 상위 타입을 필드로 설정 */
	private B b;

	/* 직접 객체를 생성하지 않고 생성자를 통해 전달 받음 */
	public A(B b) {
		this.b = b;
	}

}

/* 상위 타입으로 사용할 인터페이스 */
public interface B {

}

/* 인터페이스의 구현 클래스 */
public class NewB implements B {

}

이전의 코드와 비교하면 NewB 라는 구체적인 구현체의 타입을 사용하는 대신 B 라는 상위 인터페이스 타입으로 필드를 선언했다. 또한 직접 객체를 생성하는 구문도 없어졌고 생성자를 통해 전달 받도록 했다. 이렇게 변경하면 실제로 사용하는 구현체가 NewB 에서 또 다른 타입으로 변경 되더라도 A 의 코드는 변경 될 필요가 없다. 의존 관계가 느슨해지고 결합도가 낮아졌다고 할 수 있다.

 

2. DI 방법 알아보기

아래 코드는 테스트에 공통적으로 사용 할 AccountPersonalAccountMemberDTO 클래스이다.

  • Account
public interface Account {

    /* 잔액 조회 */
    String getBalance();

    /* 입금 */
    String deposit(int money);

    /* 출금 */
    String withDraw(int money);

}

 

  • PersonalAccount
@Data
public class PersonalAccount implements Account {

    private final int bankCode;       //은행코드
    private final String accNo;       //계좌번호
    private int balance;              //잔액

    @Override
    public String getBalance() {

        return this.accNo + " 계좌의 현재 잔액은 " + this.balance + "원 입니다.";
    }

    @Override
    public String deposit(int money) {

        String str = "";

        if(money >= 0) {
            this.balance += money;
            str = money + "원이 입금되었습니다.";
        } else {
            str = "금액을 잘못 입력하셨습니다.";
        }

        return str;
    }

    @Override
    public String withDraw(int money) {

        String str = "";

        if(this.balance >= money) {
            this.balance -= money;
            str = money + "원이 출금되었습니다.";
        } else {
            str = "잔액이 부족합니다. 잔액을 확인해주세요.";
        }

        return str;
    }

}

 

  • MemberDTO
  •  
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MemberDTO {
	
    private int sequence;                 //회원번호
    private String name;                  //이름
    private String phone;                 //휴대폰번호
    private String email;                 //이메일
    private Account personalAccount;      //개인계좌

}

=> Account(계좌) 인터페이스를 구현한 PersonalAccount(개인계좌) 클래스가 있고 MemberDTO 는 Account 타입을 필드로 가지고 있다. (MemberDTO 는 Account 타입에 의존한다.)

 

1. XML Configuration

1. 생성자(Constructor) 주입

<bean id="account" class="PersonalAccount">
		<constructor-arg index="0" value="20"/>
    <constructor-arg index="1" value="110-234-567890"/>
</bean>

<bean id="member" class="MemberDTO">
		<constructor-arg name="sequence" value="1"/>
    <constructor-arg name="name" value="홍길동"/>
    <constructor-arg name="phone" value="010-1234-5678"/>
    <constructor-arg name="email" value="hong123@gmail.com"/>
    <constructor-arg name="personalAccount">
		    <ref bean="account"/>
    </constructor-arg>
</bean>

 

bean 태그의 클래스 속성은 인터페이스 타입이 아닌 구현 클래스 타입으로 작성해야 한다. 따라서 account 빈 등록 시 class 속성에는 Account 인터페이스가 아닌  PersonalAccount 클래스를 사용한다.

MemberDTO  Account 타입을 의존하고 있기 때문에 member 빈 등록 시 account 빈을 참조하도록 <constructor-arg> 태그의 ref 속성을 작성한다. 생성자를 통해 의존성 객체를 전달하여 의존성을 주입하고 있으므로 이를 생성자 주입 이라 한다.

/* XML 설정 파일을 기반으로 ApplicationContext 객체 생성 */
ApplicationContext context 
	= new GenericXmlApplicationContext("section01/xmlconfig/spring-context.xml");

/* MemberDTO 타입의 빈 가져오기 */
MemberDTO member = context.getBean(MemberDTO.class);

/* MemberDTO의 PersonalAccount 객체 출력 */
System.out.println(member.getPersonalAccount());
/* 10000원 입금 */
System.out.println(member.getPersonalAccount().deposit(10000));
/* 잔액 출력 */
System.out.println(member.getPersonalAccount().getBalance());
/* 5000원 출금 */
System.out.println(member.getPersonalAccount().withDraw(5000));
/* 잔액 출력 */
System.out.println(member.getPersonalAccount().getBalance());

/*
PersonalAccount(bankCode=20, accNo=110-234-567890, 
	balance=110-234-567890 계좌의 현재 잔액은 0원 입니다.)
110-234-567890 계좌의 현재 잔액은 0원 입니다.
10000원이 입금되었습니다.
110-234-567890 계좌의 현재 잔액은 10000원 입니다.
5000원이 출금되었습니다.
110-234-567890 계좌의 현재 잔액은 5000원 입니다.
*/

 

2. 세터(Setter) 주입

<bean id="account" class="PersonalAccount">
		<constructor-arg index="0" value="20"/>
    <constructor-arg index="1" value="110-234-567890"/>
</bean>

<bean id="member" class="MemberDTO">
		<property name="sequence" value="1"/>
    <property name="name" value="홍길동"/>
    <property name="phone" value="010-1234-5678"/>
    <property name="email" value="hong123@gmail.com"/>
    <property name="personalAccount" ref="account"/>
</bean>

/*
PersonalAccount(bankCode=20, accNo=110-234-567890, 
	balance=110-234-567890 계좌의 현재 잔액은 0원 입니다.)
110-234-567890 계좌의 현재 잔액은 0원 입니다.
10000원이 입금되었습니다.
110-234-567890 계좌의 현재 잔액은 10000원 입니다.
5000원이 출금되었습니다.
110-234-567890 계좌의 현재 잔액은 5000원 입니다.
*/

<property> 태그는 setter 메소드를 통해 빈 객체의 값을 초기화하는 설정이다.

  • name : 필드명
  • value : 필드에 담을 값
  • ref : 참조할 빈의 id

MemberDTO 는 Account 타입을 의존하고 있기 때문에 member 빈 등록 시 account 빈을 참조하도록 <property> 태그의 ref 속성을 작성한다. Setter 메소드를 통해 의존성 객체를 전달하여 의존성을 주입하고 있으므로 이를 세터 주입 이라 한다.

빈 객체를 초기화 하는 방법이 생성자 또는 setter 메소드라는 차이는 있으나 테스트 코드의 결과는 동일하다

 

2. Java Configuration

1. 생성자(Constructor) 주입

@Bean
public Account accountGenerator() {

		return new PersonalAccount(20, "110-234-567890");
}

@Bean
public MemberDTO memberGenerator() {

		/* MemberDTO 생성자를 통해 Account를 생성하는 메소드를 호출한 리턴 값을 전달하여 bean을 조립할 수 있다. */
		return new MemberDTO(1, "홍길동", "010-1234-5678", "hong123@gmail.com", accountGenerator());
}

MemberDTO 는 Account 타입을 의존하고 있기 때문에 memberGenerator 빈 등록 시 accountGenerator 빈을 참조하도록 MemberDTO 생성자의 인자로 accountGenerator 메소드 호출의 결과(PersonalAccount bean 객체)를 전달한다. 생성자를 통해 의존성 객체를 전달하여 의존성을 주입하고 있으므로 이를 생성자 주입 이라 한다.

/* Java 설정 파일을 기반으로 ApplicationContext 객체 생성 */
ApplicationContext context 
	= new AnnotationConfigApplicationContext(ContextConfiguration.class);

/* MemberDTO 타입의 빈 가져오기 */
MemberDTO member = context.getBean(MemberDTO.class);

/* MemberDTO의 PersonalAccount 객체 출력 */
System.out.println(member.getPersonalAccount());
/* 10000원 입금 */
System.out.println(member.getPersonalAccount().deposit(10000));
/* 잔액 출력 */
System.out.println(member.getPersonalAccount().getBalance());
/* 5000원 출금 */
System.out.println(member.getPersonalAccount().withDraw(5000));
/* 잔액 출력 */
System.out.println(member.getPersonalAccount().getBalance());

/*
PersonalAccount(bankCode=20, accNo=110-234-567890, 
	balance=110-234-567890 계좌의 현재 잔액은 0원 입니다.)
110-234-567890 계좌의 현재 잔액은 0원 입니다.
10000원이 입금되었습니다.
110-234-567890 계좌의 현재 잔액은 10000원 입니다.
5000원이 출금되었습니다.
110-234-567890 계좌의 현재 잔액은 5000원 입니다.
*/

 

2. 세터(Setter) 주입

@Bean
public Account accountGenerator() {

		return new PersonalAccount(20, "110-234-567890");
}

@Bean
public MemberDTO memberGenerator() {

    MemberDTO member = new MemberDTO();
    member.setSequence(1);
    member.setName("홍길동");
    member.setPhone("010-1234-5678");
    member.setEmail("hong123@gmail.com");
		/* setter를 통해 Account를 생성하는 메소드를 호출한 리턴 값을 전달하여 bean을 조립할 수 있다. */
    member.setPersonalAccount(accountGenerator());

    return member;
}

/*
PersonalAccount(bankCode=20, accNo=110-234-567890, 
	balance=110-234-567890 계좌의 현재 잔액은 0원 입니다.)
110-234-567890 계좌의 현재 잔액은 0원 입니다.
10000원이 입금되었습니다.
110-234-567890 계좌의 현재 잔액은 10000원 입니다.
5000원이 출금되었습니다.
110-234-567890 계좌의 현재 잔액은 5000원 입니다.
*/

MemberDTO 는 Account 타입을 의존하고 있기 때문에 memberGenerator 빈 등록 시 accountGenerator 빈을 참조하도록 setPersonalAccount 메소드의 인자로 accountGenerator 메소드 호출의 결과(PersonalAccount bean 객체)를 전달한다. setter를 통해 의존성 객체를 전달하여 의존성을 주입하고 있으므로 이를 세터 주입 이라 한다.

빈 객체를 초기화 하는 방법이 생성자 또는 setter 메소드라는 차이는 있으나 테스트 코드의 결과는 동일하다.

 

정리

DI는 객체 간의 의존 관계를 빈 설정 정보를 바탕으로 컨테이너가 자동적으로 연결해주는 것이다. 이를 통해 객체 간의 결합도를 낮출 수 있으며 이로 인해 유지보수성과 유연성이 증가한다.

  • XML 빈 설정 시에는 <constructor-args> 또는 <property> 태그의 ref 속성에 의존성 주입할 bean의 이름을 설정한다.
  • Java 빈 설정 시에는 생성자, setter 메소드의 인자 값으로 의존성 주입할 bean의 메소드 호출 반환 값을 전달한다.
728x90
반응형
728x90
반응형

[Spring Framework] Spring IoC 컨테이너 사용법 완벽 정리 | 실전 예제로 이해하는 DI

 

[Spring Framework] Spring IoC 컨테이너 사용법 완벽 정리 | 실전 예제로 이해하는 DI

[Spring Framework] 스프링 IoC 컨테이너 이해하기 | 정의 [Spring Framework] 스프링 IoC 컨테이너 이해하기 | 정의[Spring Framework] Spring Framework란? 개요와 핵심 구성 모듈 정리 [Spring Framework] Spring Framework란? 개

crushed-taro.tistory.com

1. IoC Container

1. Annotation-based Configuration

1. @ComponentScan이란?

  • base package로 설정 된 하위 경로에 특정 어노테이션을 가지고 있는 클래스를 bean으로 등록하는 기능이다.
  • @Component 어노테이션이 작성 된 클래스를 인식하여 bean으로 등록한다.
  • 특수 목적에 따라 세부 기능을 제공하는 @Controller, @Service, @Repository, @Configuration 등을 사용한다.
어노테이션 설명
@Component 객체를 나타내는 일반적인 타입으로<bean> 태그와 동일한 역할
@Controller 프리젠테이션 레이어. 웹 어플리케이션에서 View에서 전달된 웹 요청과 응답을 처리하는 클래스
EX) Controller Class
@Service 서비스 레이어, 비즈니스 로직을 가진 클래스
EX) Service Class
@Repository 퍼시스턴스(persistence) 레이어, 영속성을 가지는 속성(파일, 데이터베이스)을 가진 클래스
EX) Data Access Object Class
@Configuration 빈을 등록하는 설정 클래스

Spring 사진 1

 

2. @Component 어노테이션으로 자동 빈 등록하기

@Component
public class MemberDAO {

    private final Map<Integer, MemberDTO> memberMap;

    public MemberDAO() {
        memberMap = new HashMap<>();

        memberMap.put(1, new MemberDTO(1, "user01", "pass01", "홍길동"));
        memberMap.put(2, new MemberDTO(2, "user02", "pass02", "유관순"));
    }

    /* 매개변수로 전달 받은 회원 번호를 map에서 조회 후 회원 정보를 리턴하는 메소드 */
    public MemberDTO selectMember(int sequence) {
        return memberMap.get(sequence);
    }

    /* 매개변수를 전달 받은 회원 정보를 map에 추가하고 성공 실패 여부를 boolean으로 리턴하는 메소드 */
    public boolean insertMember(MemberDTO newMember) {

        int before = memberMap.size();

        memberMap.put(newMember.getSequence(), newMember);

        int after = memberMap.size();

        return after > before;
    }
}

bean으로 등록하고자 하는 클래스 위에 @Component 어노테이션을 기재하면 스프링 컨테이너 구동 시 빈으로 자동 등록된다.

  • 이름을 별도로 지정하지 않으면 클래스명의 첫 문자를 소문자로 변경하여 bean의 id로 자동 인식한다.
  • @Component("myName") 또는 @Component(value="myName") 의 형식으로 bean의 id를 설정할 수 있다.

 

3. @ComponentScan 어노테이션으로 base packages 설정하기

@ComponentScan(basePackages="project")
public class ContextConfiguration {}

@ComponentScan어노테이션의 basePackagese 속성에 입력한 패키지가 빈 스캐닝의 범위가 된다.

 

스프링 컨테이너에 빈 스캐닝을 통해bean이 자동으로 등록 되고 생성 되었는지 확인한다.

ApplicationContext context 
	= new AnnotationConfigApplicationContext(ContextConfiguration.class);

/* getBeanDefinitionNames : 스프링 컨테이너에서 생성 된 bean들의 이름을 배열로 반환한다. */
String[] beanNames = context.getBeanDefinitionNames();
for(String beanName : beanNames) {
		System.out.println("beanName : " + beanName);
}

/*
...생략
beanName : contextConfiguration
beanName : memberDAO
*/

 

4. excludeFilters

@ComponentScan 의 excludeFilters속성을 이용해서 일부 컴포넌트를 빈 스캐닝에서 제외할 수 있다.

  • Type으로 설정하는 방법
@ComponentScan(basePackages="project",
	excludeFilters={
		@ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, 
		classes={MemberDAO.class})
})
public class ContextConfiguration {}

 

  • Annotation 종류로 설정하는 방법
@ComponentScan(basePackages="project",
	excludeFilters={
		@ComponentScan.Filter(type=FilterType.ANNOTATION, 
		classes={org.springframework.stereotype.Component.class})
})
public class ContextConfiguration {}

 

  • 표현식으로 설정하는 방법
@ComponentScan(basePackages="project",
	excludeFilters={
		@ComponentScan.Filter(type=FilterType.REGEX, 
		pattern={"annotationconfig.java.*"})
})
public class ContextConfiguration {}

 

빈 스캐닝에MemberDAO클래스가 제외되어 아래 코드를 실행하면 해당 이름을 가진 빈이 없다는 오류가 발생한다.

ApplicationContext context = new AnnotationConfigApplicationContext(ContextConfiguration.class);

String[] beanNames = context.getBeanDefinitionNames();
for(String beanName : beanNames) {
		System.out.println("beanName : " + beanName);
}

MemberDAO memberDAO = context.getBean("memberDAO", MemberDAO.class);

/*
...생략
beanName : contextConfiguration
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: 
No bean named 'memberDAO' available
*/

 

5. includeFilters

@ComponentScan 의 includeFilters성을 이용해서 일부 컴포넌트를 빈 스캐닝에 포함 시킬 수 있다.

@ComponentScan(basePackages="project",
	useDefaultFilters=false,
	includeFilters={@ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, 
		classes= {MemberDAO.class})
})
public class ContextConfiguration {}

useDefaultFilters 속성의 기본 값은 true로 @Component 종류의 어노테이션을 자동으로 등록 처리 해준다. 해당 속성을 false로 변경하면 컴포넌트 스캔이 일어나지 않게 된다.

별도로 includeFilters 속성을 정의해 컴포넌트 스캔이 발생하도록 한다.

excludeFilters에서 설정하는 방식과 동일하므로 종류별 예시는 생략한다.

 

아래 코드를 실행하면 빈 스캐닝에서MemberDAO클래스가 포함되어 해당 이름을 가진 빈을 확인할 수 있다.

ApplicationContext context 
	= new AnnotationConfigApplicationContext(ContextConfiguration.class);

String[] beanNames = context.getBeanDefinitionNames();
for(String beanName : beanNames) {
		System.out.println("beanName : " + beanName);
}

/*
...생략
beanName : contextConfiguration
beanName : memberDAO
*/

 

6. XML에서 Component Scan 설정

아래와 같이 XML 설정 파일에서 Component Scan의base package를 설정할 수도 있다.

<?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="project"/>

</beans>

<context:component-scan> 태그는 context 네임스페이스의 기능이기 때문에 context: 접두어가 필요하다.

또한 XML 설정 파일의 기본 네임스페이스가 beans로 설정 되어 있기 때문에 context 네임스페이스 추가가 필요하다.

 

base package@Component 어노테이션을 작성한 MemberDAOclass를 배치해 두고 아래와 같은 코드를 실행하면 빈이 등록 되어있음을 확인할 수 있다.

ApplicationContext context 
	= new GenericXmlApplicationContext("section03/javaannotation/spring-context.xml");

String[] beanNames = context.getBeanDefinitionNames();
for(String beanName : beanNames) {
		System.out.println("beanName : " + beanName);
}

/*
beanName : memberDAO
...생략
*/

 

아래와 같이 <context:component-scan> 태그 내부에 <exclude-filter> 또는 <include-filter> 태그를 정의할 수 있다.

<include-filter> 의 경우 동일한 방식으로 정의하므로 생략하였다.

<?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="project">
        <context:exclude-filter type="assignable" expression="common.MemberDAO"/>
  </context:component-scan>

</beans>

 

스캐닝에서MemberDAO클래스가 제외되어 아래 코드를 실행하면 해당 이름을 가진 빈이 없다는 오류가 발생한다.

ApplicationContext context 
	= new GenericXmlApplicationContext("section03/javaannotation/spring-context.xml");

String[] beanNames = context.getBeanDefinitionNames();
for(String beanName : beanNames) {
		System.out.println("beanName : " + beanName);
}

MemberDAO memberDAO = context.getBean("memberDAO", MemberDAO.class);

/*
...생략
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException
: No bean named 'memberDAO' available
*/

 

정리

  • XML 설정은 전통적으로 사용하던 방식으로 최근에는 Java 설정이 선호된다.
  • 개발자가 직접 컨트롤 가능한 Class의 경우, @Component를 클래스에 정의하여 빈 스캐닝을 통한 자동 빈 등록을 한다.
  • 개발자가 직접 제어할 수 없는 외부 라이브러리는 @Bean을 메소드에 사용하여 수동 빈 등록을 한다.
    • 다형성을 활용하고 싶은 경우에도 @Bean을 사용할 수 있다.
 

Java-based Container Configuration :: Spring Framework

This section covers how to use annotations in your Java code to configure the Spring container.

docs.spring.io

728x90
반응형
728x90
반응형

[Spring Framework] 스프링 IoC 컨테이너 이해하기 | 정의

 

[Spring Framework] 스프링 IoC 컨테이너 이해하기 | 정의

[Spring Framework] Spring Framework란? 개요와 핵심 구성 모듈 정리 [Spring Framework] Spring Framework란? 개요와 핵심 구성 모듈 정리[Servlet] Servlet에서 Thread가 어떻게 작동하는가? 웹 개발자를 위한 완벽 가이드

crushed-taro.tistory.com

1. IoC Container 사용하기

  • 아래 코드는 테스트에 공통적으로 사용할MemberDTO클래스이다.
@Data
@AllArgsConstructor
public class MemberDTO {

    private int sequence;     //회원번호
    private String id;        //아이디
    private String pwd;       //비밀번호
    private String name;      //이름

}

 

1. XML - based Configuration

1. GenericApplicationContext

GenericXmlApplicationContext Spring IoC Container 중 하나이다. XML 형태의 Configuration Metadata를 사용하여 Bean을 생성한다.

/* GenericXmlApplicationContext 클래스를 사용하여 ApplicationContext를 생성한다. 
 * 생성자에 XML 설정 메타 정보를 인자로 전달한다. */
ApplicationContext context 
	= new GenericXmlApplicationContext("spring-context.xml"
		/*XML Configuration Metadata 파일 경로*/);

 

2. XML 기반 Configuration Metadata

XML 형태의 Configuration Metadata 파일은 다음과 같이 작성할 수 있다. 다음 예제에서는MemberDTO클래스의 객체를 Bean으로 등록하고 있다.

<?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="member" class="MemberDTO">
				<!-- int 타입의 첫 번째 파라미터에 1 값을 전달 -->
        <constructor-arg index="0" value="1"/>
	      <!-- String 타입의 id 파라미터에 "user01" 값을 전달 -->
        <constructor-arg name="id" value="user01"/>
        <!-- String 타입의 세 번째 파라미터에 "pass01" 값을 전달 -->
        <constructor-arg index="2"><value>pass01</value></constructor-arg>
				<!-- String 타입의 name 파라미터에 "홍길동" 값을 전달 -->
        <constructor-arg name="name"><value>홍길동</value></constructor-arg>
    </bean>

</beans>

 

  • <beans>그는 Bean 설정 정보를 담고 있는 메타데이터를 정의하는 태그이다.
    • xmlns : XML Namespace를 정의
    • xmlns:xsi : XML Schema Instance Namespace를 정의
    • xsi:schemaLocation : XML Schema Location을 정의
    • XML Schema는 XML 문서의 구조와 유효성을 검사하기 위한 언어로, XML Schema Definition(XSD) 파일을 통해 정의
  • <beans> 태그 내부에 <bean>그를 사용해 하나의 bean 설정 정보를 작성할 수 있다.
    • id : Bean의 이름을 정의
    • class : 객체의 클래스를 지정
  • <constructor-arg> 태그는 생성자를 호출할 때 전달할 인자를 정의한다. 만약 <bean> 태그 내부에 아무 것도 작성하지 않으면 기본 생성자를 사용한다는 의미이다.
    • index : 메서드의 파라미터 순서(index)로 전달
    • name : 파라미터 이름으로 전달
    • value : 파라미터 값으로 전달

 

3. GenericApplicationContext(스프링 컨테이너) 생성 테스트

GenericApplicationContext에 bean등록 되고 생성 되었는지 확인한다.

  • bean의 id를 이용해서 bean을 가져오는 방법
MemberDTO member = (MemberDTO) context.getBean("member");
System.out.println(member);

//MemberDTO(sequence=1, id=user01, pwd=pass01, name=홍길동)

 

  • bean의 클래스 메타 정보를 전달하여 가져오는 방법
MemberDTO member = context.getBean(MemberDTO.class);
System.out.println(member);

//MemberDTO(sequence=1, id=user01, pwd=pass01, name=홍길동)

 

  • bean의 id와 클래스 메타 정보를 전달하여 가져오는 방법
MemberDTO member = (MemberDTO) context.getBean("member");
System.out.println(member);

//MemberDTO(sequence=1, id=user01, pwd=pass01, name=홍길동)

 

2. Java-based Configuration

1. AnnotationConfigApplicationContext

AnnotationConfigApplicationContext는 Spring IoC Container 중 하나이다. Java Configuration 형태의 Configuration Metadata를 사용하여 Bean을 생성한다.

/* AnnotationConfigApplicationContext클래스를 사용하여 ApplicationContext를 생성한다. 
 * 생성자에 @Configuration 어노테이션이 달린 설정 클래스의 메타 정보를 전달한다. */
ApplicationContext context 
	= new AnnotationConfigApplicationContext(ContextConfiguration.class);

 

2. Java 기반 Configuration Metadata

Java Configuration 형태의 Configuration Metadata 파일은 다음과 같이 작성할 수 있다. 다음 예제에서는MemberDTO래스의 객체를 Bean으로 등록하고 있다.

@Configuration
public class ContextConfiguration {

    @Bean(name="member")
    public MemberDTO getMember() {

        return new MemberDTO(1, "user01", "pass01", "홍길동");
    }

}

 

@Configuration 어노테이션은 해당 클래스가 빈을 생성하는 클래스임을 표기한다.

 

  • @Bean 어노테이션은 해당 메소드의 반환 값을 스프링 컨테이너에 빈으로 등록한다는 의미이다.
    • 이름을 별도로 지정하지 않으면 메소드 이름을 bean의 id로 자동 인식한다.
    • @Bean("myName") 또는 @Bean(name="myName") 의 형식으로 bean의 id를 설정할 수 있다.

 

3. AnnotationConfigApplicationContext(스프링 컨테이너) 생성 테스트

AnnotationConfigApplicationContext에 bean 이 등록 되고 생성 되었는지 확인한다.

MemberDTO member = context.getBean("member", MemberDTO.class);
System.out.println(member);

//MemberDTO(sequence=1, id=user01, pwd=pass01, name=홍길동)
728x90
반응형
728x90
반응형

[Spring Framework] Spring Framework란? 개요와 핵심 구성 모듈 정리

 

[Spring Framework] Spring Framework란? 개요와 핵심 구성 모듈 정리

[Servlet] Servlet에서 Thread가 어떻게 작동하는가? 웹 개발자를 위한 완벽 가이드 [Servlet] Servlet에서 Thread가 어떻게 작동하는가? 웹 개발자를 위한 완벽 가이드[Servlet] 자바 서블릿과 네트워크 | 요청

crushed-taro.tistory.com

1. IoC Container

1. IoC & IoC Container 정의

1. IoC(Inversion of Control)란?

제어의 역전(IoC, Inversion of Control)은 일반적인 프로그래밍에서, 프로그램의 제어 흐름 구조가 뒤바뀌는 것을 의미한다.
  • ⇒ 객체의생성과 관리, 객체 간의 의존성 처리을 프레임워크에서 대신 처리해주는 것이 IoC의 대표적인 예이다.

 

2. IoC Container란?

IoC Container는 IoC를 구현한 구체적인 프레임워크를 말한다. IoC Container를 사용하면 객체의 생성, 초기화, 의존성 처리 등을 자동으로 수행할 수 있다. 
  • ⇒ 대표적인 IoC Container로는 Spring Framework의ApplicationContext가 있다.

 

Spring 사진 1

 

2. Spring IoC Container

1. Bean이란?

Bean은 Spring IoC Container에서 관리되는 객체를 말한다. 
  • 스프링은 Bean을생성하고, 초기화하고, 의존성 주입하고, 제거하는 등의 일을 IoC Container를 통해 자동으로 처리할 수 있다.

 

2. Bean Factory란?

BeanFactory는 Spring IoC Container의 가장 기본적인 형태로, Bean의 생성, 초기화, 연결, 제거 등의 라이프사이클을 관리한다. 
  • 이를 위해Configuration Metadata를 사용한다.

 

3. Configuration Metadata란?

BeanFactory가 IoC를 적용하기 위해 사용하는 설정 정보다.
  • ⇒ 설정 메타 정보는 IoC 컨테이너에 의해 관리 되는 Bean 객체를 생성하고 구성할 때 사용 된다.

Spring 사진 2

 

4. Application Context란?

BeanFactory를 확장한 IoC 컨테이너로 Bean을 등록하고 관리하는 기능은 BeanFactory와 동일하지만, 스프링이 제공하는 각종 부가 기능을 추가로 제공한다.

 

Spring 사진 3

 

  • ListableBeanFactory : BeanFactory가 제공하는 모든 기능을 포함한다.
  • ApplicationEventPublisher : 이벤트 처리(Event Handling) 기능을 제공한다.
  • MessageSource : 국제화(i18n) 를 지원하는 메세지를 해결하는 부가 기능을 제공한다.
  • ResourceLoader : 리소스 핸들링(Resource Handling) 기능을 제공한다.
  • GenericXmlApplicationContext : ApplicationContext를 구현한 클래스. XML MetaData Configuration을 읽어 컨테이너 역할을 수행한다.
  • AnnotationConfigApplicationContext : ApplicationContext를 구현한 클래스. Java MetaData Configuration을 읽어 컨테이너 역할을 수행한다.
728x90
반응형

+ Recent posts