[Spring]@Autowired와 @AllArgsConstructor의 차이
[Spring]@Autowired와 @AllArgsConstructor의 차이
공부를 하면서 객체를 주입 받을때 주입 받는 방법은 총 3가지 방법이 존재한다.
나는 @Autowired 를 이용하여 필드에 붙이는 방식이 편하여 그동안 이렇게 주입했었는데
이 방법은 좋지 못한 방법이라는 글을 보았다. 그래서 정리하고자 한다.
| 생성자 주입(Constructor Injection)
단일 생성자인 경우에는 @Autowired 어노테이션 조차 붙이지 않아도 되지만 생성자가 2개 이상인 경우에는 생성자에 어노테이션을 붙여주어야 한다.
예를들어 단일 생성자 같은 경우
@Component public class MadExample { // final로 선언할 수 있는 보너스 private final HelloService helloService; // 단일 생성자인 경우는 추가적인 어노테이션이 필요 없다. public MadExample(HelloService helloService) { this.helloService = helloService; } }
| 필드 주입(Field Injection)
필드주입 같은 경우 사용법이 매우 간단하고 내가 자주 사용했던 방법이다.
@Autowired 어노테이션을 붙여주면 자동으로 의존성이 주입이 된다. 이 방식을 고수했던 이유는 편리하기 때문에 고집했다.
@Component public class fieldExample { //filed 주입(@Autowired)을 통한 주입 @Autowired private example example; }
| 수정자 주입 (Setter Injection)
수정자(Setter)를 이용한 주입 방법이다.
꼭 setter 메서드일 필요는 없는데 메서드 이름이 수정자 네이밍 패턴인 (setXxx)가 아니어도 동일한 기능만 하면 됩니다.
보통은 일관성과 코드를 명확하게 하기 위하여 정확한 이름 사용을 권장드립니다.
@Component public class Example { private Example example; //setter 를 이용한 방식 @Autowired public void setExample(Example example) { this.exampleService = exampleService; } }
이렇게 3가지 방법이 존재합니다.
다시 본론으로 돌아와 @Autowired 어노테이션을 냅두고 왜 왜 !! 생성자 주입 방법을 권장할까?
| 생성자 주입 방법이 주는 장점
순환 참조를 방지할 수 있다. 순환 참조가 발생하는 경우 애플리케이션이 구동되지 않는다.
테스트 코드 작성이 편리하다. 단순 POJO를 이용한 테스트 코드를 만들 수 있다.
나쁜 냄새를 없앤다. 조금 더 품질 좋은 코드를 만들 수 있다.
immutable 하다. 실행 중에 객체가 변하는 것을 막을 수 있다. 오류를 사전에 방지할 수 있다.
1.순환 참조를 방지할 수 있다.
개발을 하다 보면 여러 컴포넌트 간에 의존성이 생기는데 그중에서도 A가 B를 참조하고 B가 다시 A를 참조하는 순환 참도 발생할 수 있습니다.
빈이 생성된 후에 비지니스 로직으로 인해 서로의 메서드를 순환 참조하는 형태입니다.
Test1이 test2를 순환 참조 하는 경우
@Service public class Test1 { //순환 참조 @Autowired private Test2 test2; //Test1 class 메소드 에서 Test2 class의 메소드를 호출하고 있다. public void test1Method() { test2.test2Method() } }
Test2가 test1을 순환 참조 하는 경우
public class Test2 { @Autowired private Test1 test1; public void test2Method() { Test1.test1Method(); //Test2 클래스에서 Test1 클래스의 메소드 호출 즉 순환참조 되는 상황 } }
이런 상태에서 실행 할 경우 다음과 같은 오류메시지가 전달된다. (실행이 된다는 점!! 유의하자 )
java.lang.StackOverflowError: null
여기서 유의 깊게 봐야할 것은 애플리케이션이 실행시 아무런 오류나 경고가 없이 구동이 된다는 것이다.
( 코드가 호출되기 전까지 문제를 발견할 수 없다.)
그렇다면 생성자 주입을 할 경우에는 ?
@Service public class Test1 { //final 선언 private final Test2 test2; public Test1(Test2 test2) { this.test2 = test2; } }
@Service public class Test2 { //final 선언 private final Test1 test1; public Test2(Test1 test1) { this.test1=test1; } }
이런 경우에는 BeanCurrentlyInCreationException 오류가 발생하여 애플리케이션이 실행조차 되지 않는다.
(사전에 오류를 방지할 수 있다.)
| 실행을 똑같이 했는데 왜 오류가 나고 안나고 차이점이 뭘까?
->빈을 주입하는 순서가 다르기 때문이다.
수정자 주입(Setter Injection)은 주입 받으려는 빈의 생성자를 호출하여 빈을 찾거나 BeanFactrory에 등록을 한다.
그 후 생성자 인자에 사용하는 빈을 찾거나 만든다. 다음 주입하려는 빈 객체의 수정자를 호출하여 주입한다.
필드주입(Field Injection)은 수정자 주입 방법과 동일하게 먼저 bean을 생성 후 어노테이션이 붙은 필드에 해당하는 bean을 찾아 주입하는 방법이다. setter Injection과 같이 bean을 먼저 생성 후 필드에 대해 주입한다.
반면,
생성자 주입(Constructor Injection)은 생성자로 객체를 생성하는 시점에 필요한 bean을 주입한다.
생성자의 인자에 사용되는 bean을 찾거나 beanFactory에 만든다. 그 후 찾은 인자 bean으로 주입하려는 bean의 생성자를 호출한다.
즉, 먼저 빈을 생성하지 않는다.
그렇기 때문에 순환 참조는 생성자 주입에서만 문제가 된다.
객체 생성 시점에 bean을 주입하기 때문에 서로 참조하는 객체가 생성되지 않은 상태에서 그 bean을 참조하기 때문에 오류가 발생한다.
( * Lombok을 이용할 경우 다음과 같은 어노테이션을 사용해서 생성자를 만들지 않아도 된다는 점 참고 바랍니다. )
@RequiredArgsConstructor - 초기화 되지 않은 final 필드와 @NonNull 어노테이션이 붙은 필드에 대한 생성자를 생성합니다.
@AllArgsConstructor - 모든 필드에 대한 생성자를 생성합니다. 또한 의존성 주입 할 대상이 많아졌을 때 훨씬 깔끔합니다.
from http://2ham-s.tistory.com/278 by ccl(A) rewrite - 2020-03-11 16:54:28
댓글
댓글 쓰기