-
[Spring] 스프링에서 의존성 주입을 하는 3가지 방법Server/Spring 2021. 11. 29. 01:32728x90반응형
다양한 의존관계 주입 방법생성자 주입수정자 주입(setter 주입)필드 주입일반 메소드 주입
주입 방법은 크게 4가지가 있는데 하나씩 알아보겠습니다.
생성자 주입- 이름 그대로 생성자를 통해 의존 관계를 주입하는 것입니다.
생성자 1개이면 @Autowired를 생략할 수 있습니다.
@Component public class TestComponent { private TestRepository testRepository; // 생성자 주입 public TestComponent(TestRepository testRepository) { this.testRepository = testRepository; } @PostConstruct public void test() { System.out.println(testRepository.getClass()); } }위와 같이
TestComponent와 TestRepository가 Bean으로 등록이 되었을 때 스프링 IoC 컨테이너가 의존 관계 주입을 해줍니다.생성자 주입은 setter, 필드 주입과는 다르게TestComponent객체가 생길 때TestRepository와의 의존 관계 주입이 동시에 발생합니다.TestComponent가 Bean 으로 등록되면 싱글톤으로 관리되기 때문에 생성자는단 한번만호출된다는 것을 알 수 있습니다. (불변을 보장할 수 있습니다.) 그리고필수의존 관계일 때 사용할 수도 있습니다. 예를들어 필드에final키워드를 사용하면 생성자에서 반드시 초기화를 시켜줘야 합니다. (final로 바꾸면 생성자 외에서 다른 곳에서 바꿀 수 없기 때문에 불변도 유지할 수 있습니다.)setter 주입@Component public class TestComponent { private TestRepository testRepository; @Autowired public void setTestRepository(TestRepository testRepository) { this.testRepository = testRepository; } }- setter라 불리는 필드의 값을 변경하는 수정자 메소드를 통해서 의존관계를 주입하는 방법입니다.
선택,변경가능성이 있는 의존관계에 사용합니다.(클라이언트에게 setter를 노출하기 때문에 불변을 유지하지 못해 약간의 불안함이 존재합니다.)
필드 주입- 이름 그대로 필드에 바로 주입하는 방법입니다.
코드가 간결해서 많은 개발자들이 편하게 사용할 수는 있지만 변경이 불가능해서 테스트 하기 힘들다면 치명적인 단점이 있습니다.DI 프레임워크가 없다면 아무 것도 할 수 없습니다.- 애플리케이션과 관련 없는 테스트 코드, 스프링 설정을 목적으로 하는 @Configuration 같은 곳이 아니라면
사용하지 말자.

그리고 필드 주입은
스프링에서 권장하고 있지 않는 방법입니다.필드 주입을 지양해야 하는 이유는 무엇일까?너무나 대충 만든 예제 코드이지만 지양해야 하는 이유를 코드를 보면서 알아보겠습니다.
@Component public class TestComponent { @Autowired private TestRepository testRepository; public void createTest() { Test test = testRepository.findByTest(1); } }public class Test { private String name; private String address; public Test(String name, String address) { this.name = name; this.address = address; } }@Repository public class TestRepository { public Test findByTest(int testId) { return new Test("Gyunny", "Korea"); } }위와 같이 아주 간단한 코드가 있습니다. 이 코드에 대해서 테스트 코드를 작성해보면 아래와 같습니다.
import org.junit.jupiter.api.Test; class TestComponentTest { @Test public void fieldInjectionTest() { TestComponent testComponent = new TestComponent(); testComponent.createTest(); } }여기서는
TestComponent을 직접new를 사용해서 객체를 만들었습니다. 이렇게 직접 객체를 만들면@Autowired가 적용이 되지 않기 때문에 TestComponent의 TestRepository는 주입을 받지 못하게 됩니다.그리고
testRepository.createTest()를 하면 어떻게 될까요? TestRepository가 주입을 받지 못했기 때문에 당연히testRepository.findByTest(1);이 부분에서NullPointerException이 발생하게 됩니다. 그래서NullPointerException의 발생을 막고자 setter를 만들 수 밖에 없습니다. 그러면 그냥 setter 인젝션을 쓰는게 낫습니다.생성자 주입을 사용하는 것이 좋다.public class OrderServiceImpl implements OrderService { private MemberRepository memberRepository; private DiscountPolicy discountPolicy; @Autowired public void setMemberRepository(MemberRepository memberRepository) { this.memberRepository = memberRepository; } @Autowired public void setDiscountPolicy(DiscountPolicy discountPolicy) { this.discountPolicy = discountPolicy; } }위와 같이
setter인젝션을 사용한 후에 테스트 코드를 작성해보겠습니다.@Test void createOrder() { OrderServiceImpl orderService = new OrderServiceImpl(); orderService.createOrder(1L, "itemA", 10000); }위의 테스트를 실행하면 어떻게 될까요? 위에서 계속 말했던 것처럼
NullPointerException이 발생합니다. 위의 코드로만 봐서는 해당 테스트가 제대로 작동하는지 안하는지 파악하는데 한계가 있습니다.OrderServiceImpl내부 코드를 봐야 알 수 있습니다. 즉, setter 주입은 이러한 단점을 가지고 있기에,생성자 주입을 사용해야 합니다.생성자 주입을 사용하면 어떤 이점이 있을까요?@Component public class OrderServiceImpl implements OrderService { private final MemberRepository memberRepository; private final DiscountPolicy discountPolicy; @Autowired public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) { this.memberRepository = memberRepository; } }생성자를 이용하면
OrderServiceImpl객체를 만들 때 어떤 객체가 반드시 필요한지 알 수 있기 때문에NullPointerException을 예방할 수 있습니다.반응형'Server > Spring' 카테고리의 다른 글
[Spring] Spring Security, React를 사용하면서 CORS 허용하는 방법 (0) 2021.12.23 [Spring] AWS EC2에서 Spring Access log, logger log 저장하는 법 (0) 2021.12.09 [Spring] Transactional Propagation 정리하기 (3) 2021.11.28 [Spring] Test에서 public이 아닌 필드 Reflection 으로 값 넣어주는 법 (0) 2021.11.26 [Spring] 프로젝트에서 MyBatis ResultMap을 사용하게된 이유 (2) 2021.11.18