Spring

[Spring] @Autowired, @Inject, @Resource 차이점 비교정리

coding-knowjam(코딩노잼) 2021. 1. 8.
728x90

안녕하세요 coding-knowjam입니다.

오늘은 의존성 주입에 사용하는 @Autowired, @Inject, @Resource에 대해서 각각에 대해 어떤 특징과 차이점이 있는지 알아보겠습니다.

예시로 사용할 클래스는 아래와 같습니다.

 

 StreamingService interface

package com.nojam.coding.service;

public interface StreamingService {
	
	public void streaming(String str);
}

 

MusicStreamingService class

package com.nojam.coding.service;

import org.springframework.stereotype.Service;

@Service
public class MusicStreamingService implements StreamingService {

	@Override
	public void streaming(String str) {
        System.out.println("#### " + str + " : MusicStreamingService ####");
	}
}

 

VideoStreamingService class

package com.nojam.coding.service;

import org.springframework.stereotype.Service;

@Service
public class VideoStreamingService implements StreamingService {

	@Override
	public void streaming(String str) {
        System.out.println("#### " + str + " : VideoStreamingService ####");
	}
}

 

 

1. @Autowired

@Autowired의 특징은 다음과 같습니다.

  • Field, Setter Method, Constructor에 사용 가능
  • 기본적으로 타입을 기준으로 의존성을 주입
  • 동일한 타입의 빈이 여러 개 존재할 경우 기본적으로 참조 변수의 이름과 동일한 빈을 찾아서 주입
  • 이름을 기준으로 의존성을 주입할 때 @Qualifier 사용해서 주입될 빈 지정 가능
  • 동일한 타입의 빈이 여러 개 존재할 경우 @Primary을 사용해서 주입될 빈 지정 가능

일반적으로는 @Controller가 선언된 클래스에 service 빈을 주입하지만 제대로 주입되는지 매번 서버를 실행하기에는 번거로우므로 간단하게 테스트 코드를 통해 추가 설명을 드리겠습니다.

 

AutowiredTest Class

package com.nojam.coding;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.nojam.coding.service.MusicStreamingService;
import com.nojam.coding.service.VideoStreamingService;


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
public class AutowiredTest {

	@Autowired
	//타입을 기준으로 의존성이 주입됩니다.
	MusicStreamingService music;
	
	@Autowired
	//타입을 기준으로 의존성이 주입됩니다.	
	VideoStreamingService video
    
	@Test
	public void autowiredTest() {
		music.streaming("타입을 기준으로 빈 주입");
		video.streaming("타입을 기준으로 빈 주입");
	}
}

타입 별 빈이 1개씩만 존재할 경우 타입을 기준으로 의존성이 주입됩니다.

실행하면 아래와 같이 정상적으로 주입이 된 걸 볼 수 있습니다.

@Autowired 실행 로그 (타입 기준)

 

동일한 타입의 빈이 2개 이상 존재할 경우 타입으로는 구분이 안되므로 어떤 빈을 주입할 건지 명시를 해줘야 합니다.

명시를 해주기 위해  MusicStreamingService class에 빈 이름을 아래와 같이 지정해주겠습니다.

 

MusicStreamingService Class

package com.nojam.coding.service;

import org.springframework.stereotype.Service;

@Service("music")
public class MusicStreamingService implements StreamingService {

	@Override
	public void streaming(String str) {
		System.out.println("#### " + str + " : MusicStreamingService ####");
	}
}

 

테스트 코드를 작성해보겠습니다.

AutowiredTest Class

package com.nojam.coding;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.nojam.coding.service.MusicStreamingService;
import com.nojam.coding.service.StreamingService;
import com.nojam.coding.service.VideoStreamingService;


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
public class AutowiredTest {

	// #### 타입 별로 빈이 1개만 존재할 경우 #### 
	
	/*
	타입을 기준으로 의존성이 주입됩니다.
	@Autowired
	MusicStreamingService music; 
	
	//타입을 기준으로 의존성이 주입됩니다.	
	@Autowired
	VideoStreamingService video; 
	*/

	
	// #### 동일한 타입의 빈이 2개이상 존재 할 경우  ####  

	/*
	타입만으로는 구분이 안되서 에러가 발생합니다.
	@Autowired
	StreamingService StreamingService;
	*/ 
	
	//참조변수의 이름과 일치하는 빈이 존재할 경우 해당 빈이 주입됩니다.
	@Autowired
	StreamingService videoStreamingService;
	
	//참조변수의 이름과 일치하는 빈이 존재할 경우 해당 빈이 주입됩니다.
	@Autowired
	StreamingService music;

	// @Qualifier("빈 이름") 애너테이션을 통해서 주입 될 빈을 명시합니다.
	@Autowired
	@Qualifier("music")
	StreamingService streamingService;
		
	@Test
	public void autowiredTest() {
		videoStreamingService.streaming("이름을 기준으로 빈 주입");
		music.streaming("이름을 기준으로 빈 주입");
		streamingService.streaming("이름을 기준으로 빈 주입");
	}
}

실행을 하면 bean이름을 기준으로 주입이 된 걸 볼 수 있습니다.

@Autowired 실행 로그 (이름 기준)

동일한 타입의 빈이 2개 이상 존재할 경우 이름을 기준으로 특정 bean을 명시하는 방법 말고 @Primary을 통해 지정하는 방법도 있습니다. 

VideoStreamingService Class에 @Primary을 아래와 같이 붙여주겠습니다.

 

VideoStreamingService Class

package com.nojam.coding.service;

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;

@Primary
@Service
public class VideoStreamingService implements StreamingService {

	@Override
	public void streaming(String str) {
		System.out.println("#### " + str + " : VideoStreamingService ####");
	}
}

 

테스트 코드를 작성해보겠습니다.

 

AutowiredTest Class

package com.nojam.coding;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.nojam.coding.service.MusicStreamingService;
import com.nojam.coding.service.StreamingService;
import com.nojam.coding.service.VideoStreamingService;


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
public class AutowiredTest {

	// #### 타입 별로 빈이 1개만 존재할 경우 #### 
	
	/*
	타입을 기준으로 의존성이 주입됩니다.
	@Autowired
	MusicStreamingService music; 
	
	//타입을 기준으로 의존성이 주입됩니다.	
	@Autowired
	VideoStreamingService video; 
	*/

	
	// #### 동일한 타입의 빈이 2개이상 존재 할 경우  ####  

	/*
	타입만으로는 구분이 안되서 에러가 발생합니다.
	@Autowired
	StreamingService StreamingService;
	
	//참조변수의 이름과 일치하는 빈이 존재할 경우 해당 빈이 주입됩니다.
	@Autowired
	StreamingService videoStreamingService;
	
	//참조변수의 이름과 일치하는 빈이 존재할 경우 해당 빈이 주입됩니다.
	@Autowired
	StreamingService music;

	// @Qualifier("빈 이름") 애너테이션을 통해서 주입 될 빈을 명시합니다.
	@Autowired
	@Qualifier("music")
	StreamingService streamingService;
	*/
	
	
	// @Priamry 애너테이션이 붙은 클래스가 항상 우선 주입됩니다.
	@Autowired
	StreamingService streamingService2;
	
	// 참조변수의 이름과 동일한 빈이 있어도 @Primary가 붙은 클래스가 우선 주입됩니다.
	@Autowired
	StreamingService music ;
	
	@Test
	public void autowiredTest() {
		streamingService2.streaming("@Primary가 붙은 클래스 우선 주입");
		music.streaming("이름을 기준으로 빈 주입");
	}
}

@Primary 가 붙은 클래스가 존재하면 참조 변수의 이름과 동일한 빈이 존재해도 @Primary가 붙은 객체가 우선 주입되므로 이름으로 주입을 하고자 할 때는 @Qualifier를 붙여서 사용하셔야 합니다.

@Autowired 실행 로그 (@Primary애너테이션 사용)

 

테스트 코드에서는 간단하게 필드 주입방법을 사용하였지만 실제 코드 작성을 하실 때는 생성자 주입을 권고드립니다.

이와 관련해서는 제가 작성한 글을 참고해주세요.

참고 글 - [Spring] @Autowired DI 정리 (feat. 왜 생성자 주입을 사용해야 하는가??)

 

 

2. @Resource

@Resource의 특징은 다음과 같습니다.

  • Field, Setter Method에 사용 가능합니다. (생성자에는 사용 불가능)
  • 기본적으로 참조 변수의 이름과 동일한 빈이 존재하면 해당 빈을 주입해줍니다.
  • name속성을 사용해서 주입받을 빈을 지정할 수 있습니다.
  • 이름으로 빈을 찾지 못하면 타입을 기준으로 의존성을 주입합니다.
  • Java9 이후부터는 삭제되어서 사용할 수 없습니다. 

@Autowired를 설명할 때 수정했던 코드들은 전부 처음 상태로 수정해주세요.

그럼 이어서 테스트 코드를 작성해보겠습니다.

 

ResourceTest Class

package com.nojam.coding;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.nojam.coding.service.MusicStreamingService;
import com.nojam.coding.service.StreamingService;
import com.nojam.coding.service.VideoStreamingService;

import javax.annotation.Resource;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
public class ResourceTest {

	//name속성을 사용해서 특정 빈을 주입합니다.
	@Resource(name="musicStreamingService")
	StreamingService StreamingService;
	
	//name 속성이 생략 될 경우 참조변수의 이름과 일치하는 빈이 주입됩니다.
	@Resource
	StreamingService videoStreamingService;
	
	//name 속성이 생략 될 경우 참조변수의 이름과 일치하는 빈이 주입됩니다.
	@Resource
	StreamingService musicStreamingService;
	
	//참조변수의 이름과 일치하는 빈이 존재하지 않으면 타입으로 주입됩니다.
	@Resource
	VideoStreamingService video;

	
	@Test
	public void resourceTest() {
		StreamingService.streaming("name속성을 사용해서 이름을 기준으로 주입");
		videoStreamingService.streaming("name속성을 생략하고 이름을 기준으로 주입");
		musicStreamingService.streaming("name속성을 생략하고 이름을 기준으로 주입");
		video.streaming("타입으로 주입");
	}
}

 

@Resource 애너테이션 실행 로그

@Resource의 경우 Java8(JDK1.8)까지만 지원을 하고 Java9(JDK1.9)에서는 삭제되었습니다. 

아직까지 실무에서는 Java 8을 많이 쓰고 계시겠지만 최근 11로도 많이 프로젝트를 진행하고 있으므로 @Resource을 사용하실 때는 유의하시길 바랍니다.

 

 

3. @Inject

@Inject 은 기본적으로 @Autowired와 동일하게 사용할 수 있으며 추가적으로 @Named을 같이 사용할 수 도 있습니다.

특징은 다음과 같습니다.

  • Field, Setter Method, Constructor에 사용 가능
  • 기본적으로 타입을 기준으로 의존성을 주입
  • 동일한 타입의 빈이 여러 개 존재할 경우 기본적으로 참조 변수의 이름과 동일한 빈을 찾아서 주입
  • 이름을 기준으로 의존성을 주입할 때 @Qualifier을 사용해서 주입될 빈 지정 가능
  • 동일한 타입의 빈이 여러 개 존재할 경우 @Primary을 사용해서 주입될 빈 지정 가능
  • 이름을 기준으로 의존성을 주입할 때 @Named을 사용해서 주입될 빈 지정 가능

@Inject은 앞서 테스트한 @Autowired의 테스트 코드에서 @Autowired를 @Inject으로 바꿔도 모두 정상적으로 동작하므로 @Named을 사용한 경우만 추가적으로 테스트 코드를 작성해 보겠습니다.

 

InjectTest Class

package com.nojam.coding;

import javax.inject.Inject;
import javax.inject.Named;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.nojam.coding.service.MusicStreamingService;
import com.nojam.coding.service.StreamingService;
import com.nojam.coding.service.VideoStreamingService;


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
public class InjectTest {

	// #### 타입 별로 빈이 1개만 존재할 경우 #### 
	
		/*
		타입을 기준으로 의존성이 주입됩니다.
		@Inject
		MusicStreamingService music; 
		
		//타입을 기준으로 의존성이 주입됩니다.	
		@Inject
		VideoStreamingService video; 
		*/

		
		// #### 동일한 타입의 빈이 2개이상 존재 할 경우  ####  

		/*
		타입만으로는 구분이 안되서 에러가 발생합니다.
		@Inject
		StreamingService StreamingService;
		
		//참조변수의 이름과 일치하는 빈이 존재할 경우 해당 빈이 주입됩니다.
		@Inject
		StreamingService videoStreamingService;
		
		//참조변수의 이름과 일치하는 빈이 존재할 경우 해당 빈이 주입됩니다.
		@Inject
		StreamingService music;

		// @Qualifier("빈 이름") 애너테이션을 통해서 주입 될 빈을 명시합니다.
		@Inject
		@Qualifier("music")
		StreamingService streamingService;
		
		// @Priamry 애너테이션이 붙은 클래스가 항상 우선 주입됩니다.
		@Inject
		StreamingService streamingService2;
		
		// 참조변수의 이름과 동일한 빈이 있어도 @Primary가 붙은 클래스가 우선 주입됩니다.
		@Inject
		StreamingService music ;
		*/
		
        // @Named(value="빈 이름") 지정한 이름의 빈이 존재하면 주입됩니다.
		@Inject
		@Named(value="videoStreamingService")
		StreamingService streamingService ;
	
    
	@Test
	public void injectTest() {
		streamingService.streaming("이름을 기준으로 빈 주입");
	}
}

실행하면 정상적으로 빈이 주입되는 걸 볼 수 있을 겁니다.

@Inject 실행 로그

 

@Inject과 @Autowired는 서로 대체가 가능한데 무슨 차이점이 있을까요??

먼저 @Inject은 Java에서 지원하고 @Autowired는 SpringFramwork에서 지원을 합니다.

두 어노테이션의  FQCN (fully Qualified Class Name)을 보면 바로 아실 겁니다.

@Inject : javax.inject.Inject 

@Autowired : org.springframework.beans.factory.annotation.Autowired

그래서 @Inject은 Spring에 종속적이지 않은 어노테이션이고, @Autowired는 Spring에서만 사용 가능합니다.

 

추가적으로 @Inject은 nullable 하게 만들 수가 없습니다. 무조건 빈을 주입받아야 하고 해당하는 빈이 없다면 에러가 발생합니다. (Spring 4.xxx , Java 8 이상 일 경우 Optional을 사용해서 할 순 있지만 어노테이션 자체에서 사용 가능한 속성은 없습니다.)

그러나 @Autowired은 @Autowired (required = false)와 같이 선언하면 참조 변수에 빈을 주입하지 못해도 에러가 발생하지 않고 null로 처리됩니다.

 

 

4. @Autowired vs @Resource vs Inject

앞서 확인했던 내용을 하나의 표로 정리하면 다음과 같습니다.

  @Autowired @Resource @Inject
FQCN org.springframework.beans.
factory.annotation.Autowired
javax.annotation.Resource javax.inject.Inject
Target Field, Setter Method
,Constructor
Field, Setter Method Field, Setter Method
,Constructor
의존성
주입순서
타입 -> 이름 이름 -> 타입 타입 -> 이름
빈 지정
방법
1. @Qualifier("빈 이름")
2. @Primary 사용
@Resource(name="빈 이름") 1. @Qualifier("빈 이름")
2. @Primary 사용
3. @Named(value="빈 이름")
Nullable required=false 속성 사용 X X
비고 SpringFramwork
안에서만 사용 가능
Java 9 이후로 삭제 특정 프레임워크에
종속적이지 않음

 

지금까지 각 어노테이션에 대해서 알아보았습니다.

개인적인 견해로는 @Resource는 사용하지 않는 것이 좋을 것 같습니다.

저는 @Autowired를 선호하는데 Spring으로 프로젝트를 진행하다가 중간에 프레임워크를 바꾸는 경우는 거의 없을 것 같고, Java언어로 웹 개발하는데 Spring을 안 쓰는 경우도 좀 드물 것 같네요.

그리고 사실 처음부터 @Autowired를 써왔어서 이게 제일 편하기도 합니다.

@Autowired와 @Inject은 각자의 주관에 따라 사용을 하시면 될 것 같습니다.


728x90

댓글