본문 바로가기
Spring

[Spring] IoC/DI 제어의 역전/의존성 주입 (3)

by jaee_ 2021. 10. 3.
본 글은 📚 스프링 입문을 위한 자바 객체지향의 원리와 이해 를 읽고 정리한 내용입니다.

 

이전글 : [Spring] IoC/DI 제어의 역전/의존성 주입 (2)

 

[Spring] IoC/DI 제어의 역전/의존성 주입 (2)

본 글은 📚 스프링 입문을 위한 자바 객체지향의 원리와 이해 를 읽고 정리한 내용입니다. 이전글 : [Spring] IoC/DI 제어의 역전/의존성 주입 (1) [Spring] IoC/DI 제어의 역전/의존성 주입 (1) 본 글은

yeoonjae.tistory.com

이전 글에선 스프링의 XML파일과 @Autowired 어노테이션을 이용해 의존성을 주입하는 방법을 살펴보았다. 이번 글에서는 스프링의 @Resource 어노테이션을 통한 의존성 주입을 살펴보자.


스프링을 통한 의존성 주입 - @Resource 를 통한 속성 주입

의사코드

  • 운전자가 종합 쇼핑몰에서 자동차를 구매 요청한다.
  • 종합 쇼핑몰은 자동차를 생산한다.
  • 종합 쇼핑몰은 타이어를 생산한다.
  • 종합 쇼핑몰은 자동차에 타이어를 장착한다.
  • 종합 쇼핑몰은 운전자에게 자동차를 전달한다.

의사코드는 이전 글에서 작성했던 의사코드와 동일하다. 이전 글에서는 @Autowired를 통해 속성을 주입했다. 이번 글에서는 @Resource 어노테이션을 통해 속성을 주입해보겠다. 

import javax.annotation.*;

public class Car {

	@Resource
	Tire tire;
	
	public String getTireBrand() {
		return "장착된 타이어 : "+ tire.getBrand();
	}
	
	
}

나머지 코드는 이전 글에서 작성했던 코드와 동일하다. 그럼 여기서 "@Autowired 하면 될 것을 왜 굳이 @Resource로 변경했는가?" 라는 질문을 할 수 있을 것이다.

 

@Autowired는 스프링의 어노테이션이다. @Resource는 자바 표준 어노테이션이다.

 

스프링 프레임워크를 사용하지 않는다면 @Autowired는 사용할 수 없고, 오직 @Resource를 사용해야 한다. 그리고 이전 글에서 봤듯이 @Autowired의 경우 type과 id 가운데 매칭 우선순위는 type이 높다. 

 

@Resource 의 경우 type과 id 가운데 매칭 우선순위는 id가 높다. @Resource의 경우 id로 매칭할 빈을 찾지 못한 경우 type으로 매칭할 빈을 찾게 된다. 

 


스프링을 통한 의존성 주입 - @Autowired vs. @Resource vs. <property> 태그

@Autowired와 @Resource 어노테이션은 이전 절에서 살펴본대로 두 객체 사이에 의존성을 해결해준다. 둘을 비교한 표는 다음과 같다. 

  @Autowired @Resource
출처 스프링 프레임워크 표준 자바
소속 패키지 org.springframework.beans.factory.annotation.Autowired javax.annotation.Resource
빈 검색 방식 byType 먼저, 찾지 못하면 byName byName 먼저, 찾지 못하면 byType
특이사항 @Qualifier("") 협업 name 어트리뷰트
byName 강제하기 @Autowired
@Qualifier("tire1")
@Resource(name="tire1")

위의 표를 사례연구를 통해 코드를 작성하며 이해해보자. 변경되는 파일은 Car.java 파일과 XML파일만 변경된다. 아래의 인터페이스 및 타이어 클래스는 동일하다. 

 

// 타이어 인터페이스
public interface Tire {
	String getBrand();
}


// 미국 타이어 클래스
public class AmericaTire implements Tire{

	@Override
	public String getBrand() {
		return "미국 타이어";
	}

}


// 한국 타이어 클래스
public class KoreaTire implements Tire{

	@Override
	public String getBrand() {
		return "코리아 타이어";
	}
}
// 실행 클래스
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Driver {
	public static void main(String[] args) {
		
		ApplicationContext context = new ClassPathXmlApplicationContext("expert005/expert005.xml");
		
		Car car = context.getBean("car",Car.class);
		
		System.out.println(car.getTireBrand());
	}
}

사례연구 1. XML 설정 - 한 개의 빈이 id없이 tire 인터페이스를 구현한 경우

<?xml version="1.0" encoding="UTF-8"?>
<beans ... 생략/>

	<context:annotation-config/>
	
	<bean class="expert006.KoreaTire"></bean>
	
	<bean id = "car" class="expert006.Car"></bean>
	
</beans>
// @Resource를 사용
import javax.annotation.*;

public class Car {

	@Resource
	Tire tire;
	
	public String getTireBrand() {
		return "장착된 타이어 : "+ tire.getBrand();
	}
	
	
}
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Driver {
	public static void main(String[] args) {
		
		ApplicationContext context = new ClassPathXmlApplicationContext("expert006/expert006.xml");
		
		Car car = context.getBean("car",Car.class);
		
		System.out.println(car.getTireBrand());
	}
}

XML파일을 사례연구와 같이 변경하고 Car.java 클래스를 @Autowired로 변경 후 Driver.java 클래스를 실행 / @Resource로 변경 후 Driver.java 클래스를 실행하면 실행결과가 아무문제 없이 둘 다 동일하게 나오는 것을 확인할 수 있다. 


사례연구 2. XML 설정 - 두 개의 빈이 id없이 tire 인터페이스를 구현한 경우

<?xml version="1.0" encoding="UTF-8"?>
<beans ... 생략/>

	<context:annotation-config/>
	
	<bean class="expert006.KoreaTire"></bean>
	<bean class="expert006.AmericaTire"></bean>
	
	<bean id = "car" class="expert006.Car"></bean>
	
</beans>

@Resource ,@Autowired 둘 다 다음과 같은 오류 메세지를 볼 수 있다. 

No qualifying bean of type 'expert006.Tire' available: 
expected single matching bean but found 2: 
expert006.KoreaTire#0,expert006.AmericaTire#0

사례연구 3. XML 설정 - 두 개의 빈이 tire 인터페이스를 구현하고 하나가 일치하는 id를 가진 경우

<?xml version="1.0" encoding="UTF-8"?>
<beans ... 생략/>

	<context:annotation-config/>
	
	<bean id= "tire" class="expert006.KoreaTire"></bean>
	<bean id= "tire1" class="expert006.AmericaTire"></bean>
	
	<bean id = "car" class="expert006.Car"></bean>
	
</beans>

@Autowired, @Resource 둘 다 정상적으로 장착된 타이어 : 코리아 타이어 라는 결과를 출력한다.


사례연구 4. XML 설정 - 두 개의 빈이 tire 인터페이스를 구현하고 일치하는 id가 없을 경우

<?xml version="1.0" encoding="UTF-8"?>
<beans ... 생략/>

	<context:annotation-config/>
	
	<bean id= "tire2" class="expert006.KoreaTire"></bean>
	<bean id= "tire1" class="expert006.AmericaTire"></bean>
	
	<bean id = "car" class="expert006.Car"></bean>
	
</beans>

@Autowired, @Resource 둘 다 아래와 같은 오류메세지를 출력한다. 

 

No qualifying bean of type 'expert006.Tire' available: 
expected single matching bean but found 2: 
tire2,tire1

사례연구 5. XML 설정 - 일치하는 하나의 id가 있지만 인터페이스를 구현하지 않은 경우

<?xml version="1.0" encoding="UTF-8"?>
<beans ... 생략/>

	<context:annotation-config/>
	
	<bean id= "tire" class="expert006.Door"></bean>
	
	<bean id = "car" class="expert006.Car"></bean>
	
</beans>
//tire 인터페이스를 구현하지 않음
public class Door{}

@Resource 의 경우 다음과 같은 오류 메세지를 출력한다. 

Bean named 'tire' is expected to be of type 'expert006.Tire' but was actually of type 'expert006.Door'

@Autowired 의 경우 다음과 같은 오류 메세지를 출력한다. 

No qualifying bean of type 'expert006.Tire' available: 
expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: 
{@org.springframework.beans.factory.annotation.Autowired(required=true)}

byName이 우선인 @Resource와 byType이 우선인 @Autowired의 오류 메세지가 서로 다른 것을 볼 수 있다. 

 

위의 사례연구를 보면 @Autowired와 @Resource를 바꿔서 사용하는데 크게 차이가 없다는 사실을 알 수 있다. 그렇다면 둘 중 어떤 것을 사용하는 것이 좋을까?

 

정답은 아니지만, @Resource를 사용하는 것을 추천한다. 나중에 스프링이 아닌 다른 프레임워크로 교체되는 것을 대비하면 자바 표준인 @Resource를 사용하는 것이 유리하다. 

 

그렇다면 @Resource는 사실 <bean>태그의 자식 태그인 <property>태그로 해결될 수 있다. 그렇다면 @Resource와 <property> 태그 중 어떤 것을 사용하는 것이 좋을까? 

 

이것 역시 정답은 아니지만, <property>를 사용하는 것을 추천한다. XML파일만 봐도 DI 관계를 쉽게 확인할 수 있기 때문에 유지보수성이 좋다. 

 

정리

  • @Autowired 와 @Resource 중에서는 @Resource 추천
  • @Resource 와 <property> 중에서는 <property> 추천

사례연구 6. XML 설정 - 두 개의 빈이 tire 인터페이스를 구현하고 속성과 일치하는 id가 없지만 @Resource 어노테이션의 name 속성이 id와 일치하는 경우 

<?xml version="1.0" encoding="UTF-8"?>
<beans...생략>
	<context:annotation-config/>
	
	<bean id= "tire1" class="expert006.KoreaTire"></bean>
	<bean id= "tire2" class="expert006.AmericaTire"></bean>
	
	<bean id = "car" class="expert006.Car"></bean>
	
</beans>
import javax.annotation.*;

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

public class Car {

	@Resource(name="tire1")
	Tire tire;
	
	public String getTireBrand() {
		return "장착된 타이어 : "+ tire.getBrand();
	}
	
	
}

정상 작동한다.


사례연구 7. XML 설정 - 사례 연구 6과 같도록 @Autowired를 지정하려면 다음과 같이 설정한다. 

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

public class Car {

	@Autowired	// 이부분 주목
	@Qualifier("tire1")	//	이부분 주목
	Tire tire;
	
	public String getTireBrand() {
		return "장착된 타이어 : "+ tire.getBrand();
	}
	
	
}

정상 작동한다.


정리

사실 변수에 값을 할당하는 모든 곳에 의존 관계가 생긴다. 즉, 대입연산자 (=)에 의해 변수에 값이 할당되는 순간에 의존이 생긴다. 변수가 지역 변수이건, 속성이건, 할당되는 값이 리터럴이건 객체이건 의존은 발생한다. 의존 대상이 내부에 있을수도 있고, 외부에 있을 수도 있다.DI는 외부에 있는 의존 대상을 주입하는 것을 말한다.의존 대상을 구현하고 배치할 때 SOLID와 응집도는 높이고 결합도는 낮추라는 기본 원칙에 충실해야 프로젝트의 구현과 유지보수가 수월하다. 

 

댓글