자동 의존성 주입 어노테이션에 대하여(@Autowired vs @Resource vs @Inject)

2022. 5. 24. 15:45 Spring Framework/Spring 개념

자동 의존성 주입 어노테이션 정리

@Autowired

검색 순서 : 타입 -> 이름 -> @Qualifier -> 검색 실패시 예외 처리 또는 null 처리

@Autowired는 주입하려고 하는 객체와 타입이 일치하는 객체를  자동으로 주입한다.

최근에는 대부분 빈 주입시 생성자 주입을 선택하고 있기 때문에 생성자 주입을 기준으로 설명하겠다.

public class AutowirdService { 
    private final AutowiredRepsository autowiredRepository;

    @Autowired
    public AutowiredService(AutowiredRepository autowiredRepository) {
        this.autowiredRepsository = autowiredRepository;
    }
}

단, 생성자 주입시 그 빈을 주입받는 생성자가 단 1개뿐이라면 @Autowired를 생략할 수 있다.

동일한 타입의 빈이 여러개 등록되어있으면 이름이 일치하는 빈을 주입한다.

 

@Primary로 주입 될 빈의 우선순위를 정할 수 있으며, @Qualifier("이름") 으로 빈의 이름을 설정해서 우선적으로 주입시킬 수 있다.

 

만약 찾는 빈이 존재하지 않는다면 예외를 발생시키는데, @Autowired(required=false)를 처리하게 된다면 예외를 발생시키지 않고 null을 주입(=주입하지 않음) 처리한다.

스프링에서 지원하는 어노테이션이다.

@Resource

검색 순서 : 이름으로 검색 -> 타입으로 검색 -> @Qualifier -> 검색 실패시 예외 처리

@Resource는 주입하려 하는 객체의 [변수명]을 우선으로 주입한다.

@Autowired는 객체의 타입을 우선으로하고, 동일한 객체가 있으면 이름을 기준으로 주입한다고 했다.

하지만 @Resource는 필드명 또는 생성자 파라미터의 변수명을 우선순위로 주입한다. 만약 매칭되는 이름이 없다면 그때서야 타입을 기준으로 검색한다. @Autowired와 반대라고 생각하면 된다.

마찬가지로 @Qualifier 을 사용할 수 있으며, Bean을 찾는 순서는 아래와 같다.

참고로 @Resource는 생성자주입을 사용할 수 없다. (필드 주입 / setter 주입)

public class ResourceService{
    private ResourceRepository resourceRepository;

    @Resource  // resourceRepository 라는 이름의 Bean을 주입한다.
    public void setResourceService(ResourceRepository resourceRepository)
        this.resourceRepository = resourceRepository;
}

하지만, 타입 또는 이름이 항상 맞으면 좋겠지만 오버라이딩을 사용하는 경우 변경될 가능성이 높다. 따라서 아래 코드와 같이 name 속성을 사용해서 사용하는 것이 좋다. (사이드이펙트 방지)

//..생략
@Reource(name="resourceRepo") //resourceRepo라는 이름의 Bean을 주입한다.
private ResourceRepository resourceRepository;

Java 에서 지원하는 어노테이션이다.

@Inject

검색 순서 : 타입 -> @Qualifier -> 이름 -> 실패시 예외 처리

@Autowired와 작동 방식이 거의 일치한다. 하지만 @Inject는 javax 라이브러리 추가가 필요하다.

@Inject
@Named("injectRepo") //injectRepo라는 이름의 Bean을 주입한다.
private InejctRepository injectRepository;

@Resource 방식과 마찬가지로 @Named 어노테이션을 사용해서 이름을 직접 지정한 후 사용하는 것이 좋다.

거의 사용되지 않는다.

 

 

참고 : 빈 이름은 어떻게 정의될까?

예전에는 XML파일이나 @Configuration 이 붙은 빈 설정 자바파일을 만들어서 스프링 빈을 관리했다.

하지만 최근에는 주요 비지니스 로직(Controller/Service/Repository 등)은 @Component 어노테이션을 사용해서 @ComponentScan으로 스프링 컨테이너에 빈을 등록한다.

이 경우 스프링 컨테이너에는 다음과 같이 클래스 이름의 맨 앞글자만 소문자를 사용해 빈 이름이 정해진다.

@Service
public class MemberService {
    //...
}

 

만약 직접 빈 이름을 정의하고 싶다면 다음과 같이 사용하면 된다.

@Service("memberServiceBean") 
public class MemberService {
    //...
}

이렇게 설정하면 빈 이름이 memberServiceBean로 등록된다.

 

@Resource를 사용하는 이유

위 설명들을 보면 @Resource는 빈의 이름을 우선순위로 하여 DI를 하기때문에 같은 타입의 빈이 두 개 이상일 경우 충돌을 방지하기 위해 사용한다고 했다.

 

하지만 @Autowired 만으로도 @Quilfier와 @Primary를 사용한다면 위에서 설명한 @Resource의 이점을 똑같이 사용할 수 있는게 아닌가 하는 생각이 들어 열심히 검색해보았다.

 

추가적으로 @Resource는 스프링에 종속적이지 않기때문에 스프링 프레임워크를 사용하다가 다른 프레임워크로 변경할 경우에 유리한 점이 있다고는 하지만 이런 상황은 거의 없다고 한다.

 

정답은 간단하다. 우리가 같은 타입의 빈이 수십,수백개 이상이 있는 대규모 프로젝트를 진행한다고 가정해보자.

이렇게 같은 타입의 Bean들이 많은 프로젝트를 다루다보면 @Primary 어노테이션으로 우선순위를 정하게 될 경우 개발자 자신도 모르는 사이(단순한 개발자의 실수)에 빈들이 꼬일 수 있다.

또한 @Qulifier를 사용할 경우에도 개발자가 직접 Bean의 이름을 지정해줘야 하기 때문에 실수가 있을 수 있다.

유지 보수성을 높이기 위해서는 명확성이 중요하다. 따라서 @Resource를 사용하는 것이다.

 

결론

사실 @Resource를 사용하는 이유에 대해서도 적긴 했지만 기술적인 측면의 이유는 아니다.

단순히 '사람이 하는 일에는 실수가 있을 수 있다.' 라는 명제를 바탕으로 적은 글이다.

@Autowired를 사용하던, @Resource를 사용하던 정답은 없다고 생각한다.

그때 그때 상황에 맞게 사 자신만의 주관을 정해서 사용하자.

 

 

Reference


https://stackoverflow.com/questions/4093504/resource-vs-autowired

 

출처 : https://1-7171771.tistory.com/120?category=885255