본문 바로가기
교재 실습/자바 웹 개발 워크북

147. 어노테이션을 이용한 의존 객체 자동 주입 (1)

by Jint 2022. 11. 9.

스프링에서는 자바 어노테이션을 이용해 간단히 의존 객체를 주입할 방법을 제공해 준다. 빈의 셋터 메서드에 @Autowired를 선언하면 빈 컨테이너가 셋터의 매개변수 타입과 일치하는 빈을 찾아 자동으로 설정해 준다. 이 기능을 이용하려면 빈 설정 파일에 다음 객체를 선언해야 한다.

<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>

AutowiredAnnotationBeanPostProcessor 클래스는 빈의 후 처리기(post processor)로서 빈을 생성한 후 즉시 @Autowired로 선언된 셋터를 찾아서 호출하는 역할을 수행한다. 즉 @Autowired가 붙은 프로퍼티에 대해 의존 객체(프로퍼티 타입과 일치하는 빈)를 찾아 주입(프로퍼티에 할당)하는 일을 한다. 실습을 통해 확인해 본다.

 

1. @Autowired 적용

- 실습 패키지 생성

exam 패키지 아래에 test17 패키지를 생성한다. test16 패키지의 모든 파일을 복사해온다.

 

- Car의 engine 프로퍼티에 @Autowired 적용

Car 객체는 Engine 객체를 사용한다. Engine 객체는 Car 객체의 의존 객체이다. Engine 객체를 자동 주입하도록 셋터 메서드에 @Autowired를 선언한다.

exam.test17.Car 클래스를 열고 setEngine() 메서드에 @Autowired 어노테이션을 붙인다.

package exam.test17;

import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;

public class Car {
	
	...
	
	@Autowired
	public void setEngine(Engine engine) {
		this.engine = engine;
	}
	
	...
	
}

 

- beans.xml 빈 설정 파일

exam/test17/beans.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:p="http://www.springframework.org/schema/p"
    xmlns:c="http://www.springframework.org/schema/c"
 	xsi:schemaLocation="http://www.springframework.org/schema/beans
						http://www.springframework.org/schema/beans/spring-beans.xsd">
				
	<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
    
	<bean id="hyundaiEngine" class="exam.test17.Engine">
		<constructor-arg value="Hyundai"/>
	</bean> 
  
	<bean id="car1" class="exam.test17.Car">
		<property name="model" value="Sonata"/>
	</bean>
  
	<bean id="car2" class="exam.test17.Car">
		<property name="model" value="Grandeur"/>
	</bean>

</beans>

 

<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>

 

▶ 자바 코드로 표현하면?
AutowiredAnnotationBeanPostProcessor tempAutowiredPostProcessor = new AutowiredAnnotationBeanPostProcessor();

@Autowired 어노테이션을 처리할 빈(AutowiredAnnotationBeanPostProcessor)을 선언한다. 이 객체는 빈 컨테이너에 등록된 모든 빈을 조사하여 @Autowired가 붙은 셋터 메서드가 있다면 호출한다. 이때 셋터 메서드에 넘기는 매개변수 값은 빈 컨테이너에서 매개변수 타입으로 찾은 객체이다. 만약 빈 컨테이너에 매개변수 타입에 해당하는 객체가 없다면 예외가 발생하는데 @Autowired가 붙은 프로퍼티는 값을 반드시 설정해야 하기 때문이다. 빈 컨테이너에 매개변수 타입에 해당하는 객체가 두 개 이상 있다면 그 때도 예외가 발생한다. 그 중 어떤 것을 선택해야 할지 알 수 없기 때문이다. 예외 상황이 발생하는 경우는 다음 실습에서 알아본다.

<bean id="hyundaiEngine" class="exam.test17.Engine">
    <constructor-arg value="Hyundai"/>
</bean>

 

▶ 자바 코드로 표현하면?
Engine hyundaiEngine = new Engine("Hyundai");

먼저 Car 객체에서 사용할 Engine 객체를 준비한다.

<bean id="car1" class="exam.test17.Car">
    <property name="model" value="Sonata"/>
</bean>

<bean id="car2" class="exam.test17.Car">
    <property name="model" value="Grandeur"/>
</bean>

 

▶ 자바 코드로 표현하면?
//1) 객체 준비
Car car1 = new Car();
Car car2 = new Car();

//2) 프로퍼티 설정
car1.setModel("Sonata");
car2.setModel("Grandeur");

//3)@Autowired 처리 - AutowiredAnnotationBeanPostProcessor
car1.setEngine(hyundaiEngine);
car2.setEngine(hyundaiEngine);

두 개의 Car 객체('car1, car2')를 생성한다. car1의 모델명은 "Sonata"로 설정하고, car2의 모델명은 "Grandeur"로 설정한다. 'engine' 프로퍼티를 설정하는 코드는 없다. 그러나 'engine' 프로퍼티가 @Autowired로 선언되었기 때문에, @Autowired 어노테이션 처리기 AutowiredAnnotationBeanPostProcessor 객체에 의해 hyundaiEngine 빈이 자동 설정된다.

 

- 빈 컨테이너 테스트

Car 객체에 Engine 객체가 자동으로 설정되었는지 확인해 본다. test16 패키지에서 복사해 온 테스트 클래스 exam.test17.Test를 다음과 같이 편집한다.

package exam.test17;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
	
	public static void main(String[] args) {
		//IoC 컨테이너 준비하여 빈 생성
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("exam/test17/beans.xml");
		
		Car car1 = (Car)ctx.getBean("car1");
		Car car2 = (Car)ctx.getBean("car2");
		
		System.out.println(car1);
		System.out.println(car2);
	}
	
}

그림 1 (exam.test17.Test 클래스 실행 결과)

출력 결과를 보면 'car1', 'car2' 모두 정상적으로 엔진 객체가 할당된 것을 알 수 있다.

 

2. @Autowired의 required 속성

프로퍼티(셋터 메서드)에 대해 @Autowired를 선언하면 해당 프로퍼티는 기본으로 필수 입력 항목이 된다. 만약 프로퍼티에 주입할 의존 객체를 찾을 수 없다면 예외가 발생한다. @Autowired의 required 속성을 이용하면 필수 입력 여부를 조정할 수 있다. required 속성을 false로 설정하면 프로퍼티에 주입할 의존 객체를 찾지 못하더라도 예외가 발생하지 않는다. 실습을 통해 확인해 본다.

 

- 실습 패키지 생성

exam 패키지 아래에 test18 패키지를 생성한다. test17 패키지의 모든 파일을 복사해온다.

 

- beans.xml 빈 설정 파일

exam/test18/beans.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:p="http://www.springframework.org/schema/p"
    xmlns:c="http://www.springframework.org/schema/c"
 	xsi:schemaLocation="http://www.springframework.org/schema/beans
						http://www.springframework.org/schema/beans/spring-beans.xsd">
				
	<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
    
	<bean id="car1" class="exam.test18.Car">
		<property name="model" value="Sonata"/>
	</bean>

</beans>

Engine 빈을 선언하지 않았다. 따라서 'car1'의 engine 프로퍼티에 주입할 객체를 찾지 못해 예외가 발생할 것이다.

 

- 빈 컨테이너 테스트

@Autowired로 선언한 engine 프로퍼티에 값을 주입하지 못할 때 예외가 발생하는 상황을 확인해 본다. exam.test18.Test 클래스를 다음과 같이 편집한다.

package exam.test18;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
	
	public static void main(String[] args) {
		//IoC 컨테이너 준비하여 빈 생성
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("exam/test18/beans.xml");
		
		Car car1 = (Car)ctx.getBean("car1");
		System.out.println(car1.getEngine());
	}
	
}

앞의 코드를 실행하면 beans.xml을 로딩하여 객체를 준비하는 과정에 예외가 발생할 것이다. 다음은 실행 결과의 일부이다(그림 2).

그림 2 (exam.test18.Test 클래스 실행 결과)

출력된 예외 정보 중에서 다음 문장에 주목하기 바란다.

Error creating bean with name 'car1':

제일 먼저 보이는 메시지가 'car1'이라는 이름을 가진 빈을 생성하는데 실패했다는 것이다. 그리고 이어서 그 이유가 나오는데 의존 객체를 자동 주입하는데 실패했다고 한다. 구체적으로 어떤 프로퍼티(setEngine())를 호출하려다가 실패했는지 언급하고 있다. 이렇듯 오류 메시지만 봐도 왜 오류가 발생했는지 그 이유를 어렵지 않게 파악할 수 있다.

 

- @Autowired의 required 속성 사용

이번에는 required 속성을 사용하여 프로퍼티를 선택 항목이 되게 만들어 본다.

exam.test18.Car 클래스를 열고 setEngine() 메서드에 선언된 @Autowired 어노테이션을 다음과 같이 변경한다.

package exam.test18;

import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;

public class Car {
	
	...
	
	@Autowired(required=false)
	public void setEngine(Engine engine) {
		this.engine = engine;
	}
	
	...
	
}

@Autowired 어노테이션의 required 속성값을 false로 설정한다.

다시 exam.test18.Test 클래스를 실행한다. 이전과 달리 engine 프로퍼티에 주입할 객체가 없어도 오류가 발생하지 않는다.

그림 3 (exam.test18.Test 클래스 실행 결과)

앞의 실행 결과는 다음 코드 때문이다.

Car car1 = (Car)ctx.getBean("car1");
System.out.println(car1.getEngine());

'car1' 빈을 찾아서 engine 프로퍼티의 값을 출력해 보니 null인 것이다. 이전과 달리 engine 프로퍼티에 주입할 의존 객체를 찾지 못해도 오류가 발생하지 않는다.

 

참고도서 : https://freelec.co.kr/book/1674/

 

[열혈강의] 자바 웹 개발 워크북

[열혈강의] 자바 웹 개발 워크북

freelec.co.kr

댓글