3가지 주입 방식이 있다.
필드 주입
@Service
public class TestService2 {
@Autowired
private TestService3 testService3;
...
}
필드 주입을 하면 순환 참조를 잡아내지 못한다는 글이 있다. 확인해보자.
@Service
public class TestService2 {
@Autowired
private TestService3 testService3;
...
}
@Service
public class TestService3 {
@Autowired
private TestService2 testService2;
...
}
위와 같은 코드로 run을 하면
다음과 같이 빌드에서 에러를 발견할 수 있다. 예전 버전의 spring boot에선 잡아내지 못했던 것 같다.
spring boot:3.1.2 에선 필드 주입에서도 순환 참조를 방지하는 걸 볼 수 있다.
수정자 주입
@Service
public class TestService2 {
private TestService3 testService3;
@Autowired
public void setTestService3(TestService3 testService3){
this.testService3 = testService3;
}
...
}
생성자 주입
@Service
public class TestService2 {
private final TestService3 testService3;
public TestService2(TestService3 testService3){
this.testService3 = testService3;
}
...
}
생성자가 하나라면 @Autowired를 명시하지 않아도 된다. 확인해보자.
@Service
public class TestService2 {
private Object testService;
public TestService2(TestService3 testService3){
this.testService = testService3;
}
public TestService2(TestService1 testService1){
this.testService = testService1;
}
public void sayClass(){
System.out.println(testService.getClass());
}
}
위와 같이 작성하고 testService2.sayClass()를 호출하면 다음과 같은 에러가 나온다.
NullPointerException: Cannot invoke "Object.getClass()" because "this.testService" is null
생성자가 2개 이상일 때 @Autowired를 달지 않으면 어떤 의존성 주입도 되지않고 null인 것을 확인할 수 있고
2개의 생성자 중 하나에 @Autowired를 달아주면 해당 생성자의 의존성이 주입되고 getClass가 잘 찍히는 것을 볼 수 있다.
생성자가 하나일 때, lombok으로 생성자 주입
@Service
@RequiredArgsConstructor
public class TestService2 {
private final TestService3 testService3;
...
}
@RequiredArgsConstructor는 필수 필드(final)를 인자로 생성자를 자동 생성해준다.
@Service
public class TestService2 {
private final TestService3 testService3;
public TestService2(final TestService3 testService3) {
this.testService3 = testService3;
}
}
빌드된 코드는 위와 같다.
@Service
@RequiredArgsConstructor
public class TestService2 {
@NonNull
private TestService3 testService3;
...
}
@NonNull 어노테이션을 사용하는게 더 좋아보인다.
@Service
public class TestService2 {
private @NonNull TestService3 testService3;
public TestService2(final @NonNull TestService3 testService3) {
if (testService3 == null) {
throw new NullPointerException("testService3 is marked non-null but is null");
} else {
this.testService3 = testService3;
}
}
}
빌드된 코드는 위와 같다. final이여도 null을 인자로 넘길 수 있는데 그 부분에 대한 예외처리를 해준다.
순환 의존에 대해선 3가지 방법 모두 실행 전에 에러를 뱉는다.
기본적으로 불변성을 유지하고자 한다면 생성자 주입을 사용하고 수정이 필요하면 필드 주입을 사용하면 될 것 같다.
생성자 주입은 생성자가 여러개가 아니면 lombok을 이용한 방법이 편하고 안전해보인다.
'Spring' 카테고리의 다른 글
lombok @Builder (0) | 2023.08.06 |
---|---|
Json 직렬/역직렬, @RequestBody, @ModelAttribute (0) | 2023.08.04 |
스프링) Spring jpa 순환참조 (23-07-07) (0) | 2023.07.13 |
스프링) Spring boot dto/lombok/validation (23-06-30) (0) | 2023.07.13 |
스프링) Spring boot ExceptionHandler/ Optional (23-06-28) (0) | 2023.07.13 |