본문 바로가기

Backend/Spring

[Spring/SpringBoot] 코드로 확인하는 Dependency Injection

https://chanho0912.tistory.com/8?category=866707 

 

[SpringBoot] IOC(Inversion Of Control), DI(Dependency Injection)이란?

지난 포스팅까지 SpringBoot의 작동에 핵심적인 역할을 하는 Dispatcher Servlet에 대해 알아보았다. 이번에는 Spring에서 제공하는 핵심적인 기능 중 IOC에 대해 포스팅을 해보려 한다. IOC란? 의존 관계 주

chanho0912.tistory.com

https://chanho0912.tistory.com/9?category=866707 

 

[SpringBoot] IOC 컨테이너와 Bean이란?

본 포스팅은 https://chanho0912.tistory.com/8 [SpringBoot] IOC(Inversion Of Control), DI(Dependency Injection)이란? 지난 포스팅까지 SpringBoot의 작동에 핵심적인 역할을 하는 Dispatcher Servlet에 대해..

chanho0912.tistory.com

 

위 두 포스팅에서 Dependency Injection과 Bean에 대해 간단하게 살펴보았다.

 

이번에는 실제 코드로 한번 Dependency Injection을 확인해보자.

 

/*
 * Copyright 2012-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.samples.petclinic.owner;

import org.springframework.samples.petclinic.visit.VisitRepository;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import javax.validation.Valid;
import java.util.Collection;
import java.util.Map;

/**
 * @author Juergen Hoeller
 * @author Ken Krebs
 * @author Arjen Poutsma
 * @author Michael Isvy
 */
@Controller
class OwnerController {

	private static final String VIEWS_OWNER_CREATE_OR_UPDATE_FORM = "owners/createOrUpdateOwnerForm";

	private final OwnerRepository owners;

	private VisitRepository visits;

	public OwnerController(OwnerRepository clinicService, VisitRepository visits) {
		this.owners = clinicService;
		this.visits = visits;
	}

	...

}

 

위 코드는 spring boot 프로젝트인 pet-clinic 코드의 owner controller를 참고하였다.

 

해당 코드는 

https://github.com/spring-projects/spring-petclinic 

 

spring-projects/spring-petclinic

A sample Spring-based application. Contribute to spring-projects/spring-petclinic development by creating an account on GitHub.

github.com

에서 참고 가능하다.

 

우리가 살펴볼 부분은

 

private final OwnerRepository owners;
private VisitRepository visits;

public OwnerController(OwnerRepository clinicService, VisitRepository visits) {
	this.owners = clinicService;
	this.visits = visits;
}

이 부분이다.

 

보면 OwnerController의 생성자로 OwnerRepository와 VisitRepository를 주입받고 있다.

 

@Autowired를 생성자 위에 붙여서 생성자로 의존성을 주입받는 것을 표시했었는데, Spring 4.3 version부터 어떠한 class에 생성자가 하나뿐이고, 생성자로 주입받는 변수들이 Bean으로 등록되어 있다면 자동으로 주입을 해준다.

 

IOC container가 OwnerController를 생성할 때 해당 Bean들을 주입해준다고 생각하면 된다.

 

위와같이 생성자를 사용하지 않고 의존성을 주입받으려면

 

@Autowired
private OwnerRepository owners;

@Autowired
private VisitRepository visits;

//	public OwnerController(OwnerRepository clinicService, VisitRepository visits) {
//		this.owners = clinicService;
//		this.visits = visits;
//  }

위와 같이 생성자를 지우고, @Autowired Annotation을 활용하여 기존의 Bean들을 주입받아 사용할 수 있다.

 

혹은 Setter를 활용하여 주입받는 방법도 유효하다.

 

private OwnerRepository owners;

private VisitRepository visits;

@Autowired
public void setOwners(OwnerRepository owners) {
	this.owners = owners;
}

@Autowired
public void setVisits(VisitRepository visits) {
	this.visits = visits;
}

위와 같이 Setter method를 활용하여 기존의 Bean들을 주입받을 수도 있다. 이 경우 Set method에 @Autowired Annotation을 붙여주어야 한다.

 

위와 같은 여러 방법이 있지만, Spring에서 권장하는 방법은 생성자를 통한 의존성 주입인데 그 이유는

필수적으로 사용하는 Bean 없이는 생성되지 않도록, 강제할 수 있다.

 

즉 생성자로 의존성을 주입받으면 OwnerRepository가 정상적으로 작동하지 않거나, 존재하지 않으면 Controller를 생성할 수 없다.

 

반대로 @Autowired를 사용하거나 Setter를 사용하게 되면, 일단 IOC Container가 Controller를 생성할 수 있다.

(순환 참조가 발생하는 경우에는 생성자를 통한 주입을 받을 수 없다. 그런 경우가 아니면 일반적으로 생성자를 통한 주입이 권장된다 : A -> B 주입받는데, B -> A 주입받는 경우 서로 생성 불가)

 

위와 같은 안정성의 문제로 아마 생성자를 통한 주입을 권장하는 것 같다.

 

하지만 특별한 경우가 아니면 개발자의 방식이나 회사의 방식에 따르지 않을까 싶다. 저러한 방법들이 있구나 정도를 알아두면 좋을 것 같다.

 

*저의 글에 대한 피드백이나 지적은 언제나 환영합니다.