본문 바로가기
Java/Spring

Spring) Java Annotation + Xml을 통한 context 작성 - IoC, DI 특징 파악하기

by 박채니 2022. 8. 16.

안녕하세요, 코린이의 코딩 학습기 채니 입니다.

 

개인 포스팅용으로 내용에 오류 및 잘못된 정보가 있을 수 있습니다.


Person

@Component : bean으로 관리할 클래스

@Autowired : 의존 주입

/**
 * DI(Dependency Injection 의존주입)
 *  1. field 주입
 *  2. setter 주입
 *  3. 생성자 주입
 *
 */

@Component
public class Person {
	@Autowired
	Pet pet;
	
	public Person() {
		System.out.println("Person 객체 생성");
		System.out.println(pet);
	}
}

 

Pet interface

public interface Pet {
	
}

 

Dog

@Component
public class Dog implements Pet {
	public Dog() {
		System.out.println("Dog 객체 생성!");
	}
}

 

pet-context.xml

beans는 자동으로 포함되며, context 네임스페이스를 사용하기 위하여 context 체크 후 Finish

 

<?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 http://www.springframework.org/schema/context/spring-context-4.3.xsd">
	
	<!-- 
		base-package하위의 @Component 어노테이션 클래스를 bean으로 관리
	 -->
	<context:component-scan base-package="com.kh.spring.pet"></context:component-scan>

</beans>

xmlns:context가 생성된 것을 확인할 수 있으며, 이는 context 네임스페이스를 사용할 수 있습니다.

네임스페이스가 여러가지일 때 default namespace는 'beans'이며, default namespace는 <beans:bean>처럼 앞에 네임스페이스를 붙이지 않고, <bean>으로 네임스페이스를 생략하여 사용할 수 있습니다.

다만, default namespace가 아니라면 반드시 앞에 네임스페이스를 붙여줘야 합니다. (context:component-scan태그처럼)

 

<context:component-scan> 태그의 base-package에서 지정한 패키지 하위에 @Component 어노테이션을 빈으로 관리해줌을 의미합니다.

 

Pet Main

public class PetMain {

	public static void main(String[] args) {
		String configLocation = "classpath:pet-context.xml";
		ApplicationContext context = new ClassPathXmlApplicationContext(configLocation);
		System.out.println("=================== spring container 초기화 완료 ===================");
	}

}

@콘솔출력값
Dog 객체 생성!
Person 객체 생성!
null
=================== spring container 초기화 완료 ===================

따라서 @Component 어노테이션이 붙은 Dog, Person 객체가 생성된 것을 확인할 수 있으며, 객체 생성 시 Pet에 대한 의존 주입이 안된 것을 확인할 수 있습니다. (null)

 


필드 주입

 

Person

getPet() 메소드 추가

@Component
public class Person {
	
	@Autowired
	Pet pet;
	
	public Person() {
		System.out.println("Person 객체 생성!");
		System.out.println(pet);
	}
	
	public Pet getPet() {
		return pet;
	}
}

 

PetMain

public class PetMain {

	public static void main(String[] args) {
		String configLocation = "classpath:pet-context.xml";
		ApplicationContext context = new ClassPathXmlApplicationContext(configLocation);
		System.out.println("=================== spring container 초기화 완료 ===================");
		
		Person person = context.getBean(Person.class);
		System.out.println(person.getPet());	// com.kh.spring.pet.Dog@1d7acb34

	}

}

@콘솔출력값
Dog 객체 생성!
Person 객체 생성!
null
=================== spring container 초기화 완료 ===================
com.kh.spring.pet.Dog@1d7acb34

생성자 생성 시에는 Pet 객체에 대한 의존주입이 이루어지지 않았지만, Pet을 사용할 때가 되니 사용직전에 필드에 직접 주입이 된 것을 확인할 수 있습니다.

 


생성자 주입

 

Person

@Component
public class Person {
	
	Pet pet;
	
	public Person() {
		System.out.println("Person 객체 생성!");
		System.out.println(pet);
	}
	
	@Autowired
	public Person(Pet pet) {
		System.out.println("Person 객체 생성! : " + pet);
		this.pet = pet;
	}
	
	public Pet getPet() {
		return pet;
	}
}

 

PetMain

public class PetMain {

	public static void main(String[] args) {
		String configLocation = "classpath:pet-context.xml";
		ApplicationContext context = new ClassPathXmlApplicationContext(configLocation);
		System.out.println("=================== spring container 초기화 완료 ===================");
	}

}

@콘솔출력값
Dog 객체 생성!
Person 객체 생성! : com.kh.spring.pet.Dog@281e3708
=================== spring container 초기화 완료 ===================

생성자 생성 시 Pet 객체가 의존주입 된 것을 확인할 수 있습니다.

 


setter 주입

 

Person

@Component
public class Person {
	
	Pet pet;
	
	public Person() {
		System.out.println("Person 객체 생성!");
		//System.out.println(pet);
	}

	public Person(Pet pet) {
		System.out.println("Person 객체 생성! : " + pet);
		this.pet = pet;
	}
	
	@Autowired
	public void setPet(Pet pet) {
		System.out.println("Person#setPet 호출! : " + pet);
		this.pet = pet;
	}
	
	public Pet getPet() {
		return pet;
	}
}

 

PetMain

public class PetMain {

	public static void main(String[] args) {
		String configLocation = "classpath:pet-context.xml";
		ApplicationContext context = new ClassPathXmlApplicationContext(configLocation);
		System.out.println("=================== spring container 초기화 완료 ===================");
	}

}

@콘솔출력값
Dog 객체 생성!
Person 객체 생성!
Person#setPet 호출! : com.kh.spring.pet.Dog@1068e947
=================== spring container 초기화 완료 ===================

기본생성자가 호출된 후 setPet이 호출된 것을 확인할 수 있으며, 마찬가지로 Pet 객체가 주입 된 것을 확인할 수 있습니다.

 


@Qualifier : 사용할 의존 객체를 선택하여 주입해줌

- 해당 타입의 빈이 2개 이상이라면 오류 발생 → 이름(bean id)으로 지정 가능

- 필드주입/setter주입에만 사용 가능

 

Cat 클래스 추가

@Component
public class Cat implements Pet {
	public Cat() {
		System.out.println("Cat 객체 생성!");
	}
}

 

Person

@Component
public class Person {
	
	Pet pet;
	
	public Person() {
		System.out.println("Person 객체 생성!");
		//System.out.println(pet);
	}

	public Person(Pet pet) {
		System.out.println("Person 객체 생성! : " + pet);
		this.pet = pet;
	}

	@Autowired
	public void setPet(Pet pet) {
		System.out.println("Person#setPet 호출! : " + pet);
		this.pet = pet;
	}
	
	public Pet getPet() {
		return pet;
	}
}

@콘솔출력값
경고: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'person': Unsatisfied dependency expressed through method 'setPet' parameter 0; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.kh.spring.pet.Pet' available: expected single matching bean but found 2: cat,dog
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'person': Unsatisfied dependency expressed through method 'setPet' parameter 0; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.kh.spring.pet.Pet' available: expected single matching bean but found 2: cat,dog

Cat 클래스를 추가하고, Cat 클래스 또한 bean으로 관리하도록 하였습니다.

하지만 이렇게 된다면, Pet은 Dog, Cat 빈을 가지고 있게 되고 이 때 실행하게 된다면 오류가 발생하게 됩니다.

 

이런 경우, 어떤 타입의 빈을 주입받을 것인지에 대해 지정해줄 수 있으며, 이 때 @Qualifier 어노테이션을 사용하게 됩니다.

 

Person

@Component
public class Person {
	
	Pet pet;
	
	public Person() {
		System.out.println("Person 객체 생성!");
		//System.out.println(pet);
	}

	public Person(Pet pet) {
		System.out.println("Person 객체 생성! : " + pet);
		this.pet = pet;
	}
	
	/**
	 * 
	 * @Autowired는 해당 타입의 빈을 찾아서 의존주입
	 * - 해당 타입의 빈이 2개 이상이라면 오류!
	 * - 이 때 @Qualifier를 통해 이름(bean id)으로 지정 가능
	 * - @Qualifier는 필드주입/setter주입에만 사용가능
	 *  
	 */
	@Qualifier("dog")
	@Autowired
	public void setPet(Pet pet) {
		System.out.println("Person#setPet 호출! : " + pet);
		this.pet = pet;
	}
	
	public Pet getPet() {
		return pet;
	}
}

bean id는 클래스명을 소문자로 시작되는 것으로 변환하여 부여!

 

 

PetMain

public class PetMain {

	public static void main(String[] args) {
		String configLocation = "classpath:pet-context.xml";
		ApplicationContext context = new ClassPathXmlApplicationContext(configLocation);
		System.out.println("=================== spring container 초기화 완료 ===================");
	}

}

@콘솔출력값
Cat 객체 생성!
Dog 객체 생성!
Person 객체 생성!
Person#setPet 호출! : com.kh.spring.pet.Dog@382db087
=================== spring container 초기화 완료 ===================

@Qualifier("dog")로 Pet 타입으로 처리될 수 있는 bean을 찾되, 이름이 'dog'인 bean을 찾아달라!는 의미로 이번엔 오류없이 Dog 객체가 의존주입된 것을 확인할 수 있습니다.