프로그래밍 언어/JAVA, SPRING

[DI] @Autowird vs @RequiredArgsConstructor

doomole 2023. 8. 16. 11:13
728x90

의존성 주입에는 생성자 주입, Setter 주입, 필드 주입이 있다.

방장은 필드 주입을 통해 프로젝트를 수행했었고, @Autowired 어노테이션을 사용했었다.

이직을 하면서 새 프로젝트를 수행하게 되었는데 이 때 @RequiredArgsConstructor 어노테이션을 처음 접하게 되었다.

결론부터 말하면 스프링에서는 생성자 주입을 권고하기 때문에 방장은 앞으로 @RequiredArgsConstructor를 사용할 예정이다. :D

따라서 이에 대해 정리해보려고 한다.

 


@Autowired

의존 객체의 타입에 해당하는 bean을 찾아주는 역할을 한다.

bean으로 등록되어 있지 않은 객체를 의존성 주입할 경우 에러가 발생한다.

(Could not autowire. No beans of 'TestService' type found.)


동작원리

1. BeanFactory( ApplicationContext )가 BeanPostProcessor 타입의 Bean을 찾는다.

2. IoC 컨테이너에 등록되어있는 다른 일반적인 Bean에게 BeanPostProcessor를 적용한다.

3. 다른 Bean에 @Autowired Annotation을 처리하는 AutowiredAnnotationBeanPostProcessor의 로직이 적용된다.

4. 의존성 주입이 일어난다.

 

위는 참고한 블로그에서 가져온 내용이다. 정확하게 이해하지 못했다.


단점

1. 필드 주입 시 final 옵션을 사용할 수 없기 때문에 코드가 변질될 수 있다.

2. 같은 Type의 객체가 여러개일 경우 에러가 발생한다.


사용 방법

생성자

@Repository
public class TestRepository {
	...
}

public class TestService {
	private TestRepository testRepository;
    
    @Autowired
    public TestService(TestRepository testRepository) {
    	this.testRepository = testRepository;
    }
}

TestService 객체의 생성자가 한개 뿐이라면 @Autowired를 생략 가능하다.

 

Setter

@Repository
public class TestRepository {
	...
}

public class TestService {
	@Autowired(required = false)
    public void setTestRepository(TestRepository testRepository) {
		this.testRepository testRepository;
    }
}

사용해본적이 없어 required를 쓰는 이유에 대해 이해가 안갔다.(댓글로 설명 부탁드립니다..)

 

필드

@Repository
public class TestRepository {
	...
}

public class TestService {
	@Autowired
	private TestRepository testRepository;    
}

주로 사용했던 방법으로 가장 간결하게 의존성 주입이 가능하다.

만약 TestRepository 객체가 interface이고 이를 구현하는 interface가 여러개일 경우 에러가 발생한다.

 

이를 방지하기 위해 아래 3가지 방법이 있다.

@Primary

public interface TestRepository {
	...
}

@Repository @Primary
public class TestRepo01 implements TestRepository {
	...
}

@Repository
public class TestRepo02 implements TestRepository {
	...
}

public class TestService {
	@Autowired
	private TestRepository testRepository;    
}

@Qulifier

public interface TestRepository {
	...
}

@Repository
public class TestRepo01 implements TestRepository {
	...
}

@Repository
public class TestRepo02 implements TestRepository {
	...
}

public class TestService {
	@Autowired @Qulifier("testRepo01")	// lower camel case 사용
	private TestRepository testRepository;    
}

전체 주입

public interface TestRepository {
	...
}

@Repository
public class TestRepo01 implements TestRepository {
	...
}

@Repository
public class TestRepo02 implements TestRepository {
	...
}

public class TestService {
	@Autowired
	private List<TestRepository> testRepository;    
}

 


@RequiredArgsConstructor

Lombok에서 지원하는 어노테이션으로, 필드 주입이 아닌 생성자로 의존성 주입을 한다.

final 키워드가 붙은 필드에 대해 생성자를 만들어준다.

스프링에서 생성자 주입을 권고하고 있기 때문에 해당 어노테이션을 사용하는 것이 좋아보인다.

* 생성자로 의존성 주입을 받은 객체는 프로그램이 끝날 때까지 변하지 않는다.


사용방법

public class TestRepository {
	...
}

@RequiredArgsConstructor
public class TestService {
	private final TestRepository testRepository;
}

장점

1. final 옵션을 사용하기 때문에 코드 변이가 될 가능성이 없다.

2. 생성자 주입은 순환 참조가 일어날 시 Exception이 발생하여 컴파일 중 에러가 발생하기 때문에 Test단계에서 문제를 파악할 수 있다.

* 순환참조 : A객체가 B객체를 참조할 때, B객체도 A객체를 참조하는 현상

 

* 질문이나 문의사항, 피드백은 댓글로 남겨주세요.


참고

https://jjingho.tistory.com/6 

https://backendcode.tistory.com/209