- [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 - 모든 필드에 대한 생성자를 생성합니다. 또한 의존성 주입 할 대상이 많아졌을 때 훨씬 깔끔합니다.
'Spring' 카테고리의 다른 글
@RequestParam과 @PathVariable? (0) | 2020.07.25 |
---|---|
[Spring]ResponseEntity (0) | 2020.03.11 |
[Spring Test]-IntStream의rangeClosed (0) | 2020.03.10 |
[Spring]- UriComponentsBuilder란? (0) | 2020.03.09 |
[Spring]addAttribute와 addFlashAttribute 차이점 (0) | 2020.03.08 |