Inversion of Control : 제어권이 역전됐다.
일반적인 제어권 : "내가 사용할 의존성은 내가 만든다."
class QwnerController {
private OwnerRepository repo = new OwnerRepository();
}
IoC : "내가 사용할 의존성을 누군가 준다."
생성자를 통해.
class QwnerController {
private OwnerRepository repo;
public QwnerController(OwnerRepository repo){
this.repo = repo;
}
//repo를 사용합니다.
//dependancy injection도 일종의 IoC.
}
public class OwnerControllerTest {
@Test
public void testDo(){
OwnerRepository repo = new OwnerRepository();
OwnerController ownerController = new OwnerController(repo);
}
}
생성자를 한 가지만 생성할경우, null-point-exception은 발생하지 않고 내부 함수들은 안전하다.
IoC Container
Bean
class OwnerControllerTest {
@MockBean
private OwnerRepository owners; // 자동 Bean으로 등록
의존성 주입(Dependancy injection)
public class SampleControllerTest {
@Test
public void testDosomething(){
SampleRepository repo = new SampleRepository();
SampleController sampleController = new SampleController(repo);
sampleController.doSomething();
}
}
Bean을 만들고 Bean 사이의 의존성을 엮어 제공한다.
ApplicationContext (or BeanFactory(상속))를 사용
OwnerController OwnerRepository PetController PetRepository는 모두 Bean에 들어있다.
아래 사진과 같이 콩표시 뜨면 Bean이다.(Intellij Community는 표시를 지원하지 않고 Ultimate에서 지원한다.)

의존성 주입은 Bean끼리만 가능하다.
OwnerController
@Controller
class OwnerController {
private final ApplicationContext applicationContext;
public OwnerController(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@GetMapping("/bean")
@ResponseBody
public String bean(){
return "bean: " + applicationContext.getBean(OwnerController.class);
}
}
위와 같이 추가하면, http://localhost:8080/bean 에 bean: org.springframework.samples.petclinic.owner.OwnerController@31694483과 같이 클래스에 해당하는 hashCode가 뜬다.
아이러니하게도 실제로 ApplicationContext를 사용하지 않고 아래와 같이 해보면,
@Controller
class OwnerController {
private static final String VIEWS_OWNER_CREATE_OR_UPDATE_FORM = "owners/createOrUpdateOwnerForm";
private final OwnerRepository owners;
private VisitRepository visits;
private final ApplicationContext applicationContext;
public OwnerController(OwnerRepository clinicService, VisitRepository visits, ApplicationContext applicationContext) {
this.owners = clinicService;
this.visits = visits;
this.applicationContext = applicationContext;
}
@GetMapping("/bean")
@ResponseBody
public String bean(){
return "bean: " + applicationContext.getBean(OwnerRepository.class) + "\n"
+ "owners: " + this.owners;
}
}

IoC 컨테이너가 관리하는 일반적인 객체
test\java\org...\owner\OwnerControllerTest
@Test
public void getBean(){
OwnerController ownerController = new OwnerController(); //일반적인 객체, new를 포함
OwnerController bean = applicationContext.getBean(OwnerController.class); // Bean, new (x)
assertThat(bean).isNotNull();
}
빈을 등록하는 2가지 방법
(1) Component Scaning : 어느 지점부터 찾아 등록할것인지 알려줌
@Component 사용(모든 @이 달려있는 것을 찾아 자동 빈에 등록)@Repository@Sevice@Controller@Configuration 등@은 위와 같은 경우들.(2) 직접 일일히 XML이나 자바 설정 파일에 등록
test\java\org...\sample\SampleConfig
@Configuration
public class SampleConfig {
**@Bean**
public SampleController sampleController(){
return new SampleController();
}
}
test\java\org...\sample\SampleControllerTest 여기 에서 위에서 만들어 놓은 Bean을 사용
@RunWith(SpringRunner.class)
@SpringBootTest
public class SampleControllerTest {
@Autowired
ApplicationContext applicationContext;
@Test
public void testDI(){
SampleController bean = applicationContext.getBean(SampleController.class);
assertThat(bean).isNotNull();
}
}
OwnerRepository는 특이한 형태로 즉, JPA 기능에 의해 등록된다. 특정한 @ 이 없더라도 인터페이스를 상속받고 있는 클래스(인터페이스)를 찾아서 구현체를 내부적으로 만들어 빈에 등록.
public interface OwnerRepository extends Repository<Owner, Integer> {
}
그렇다면 등록한 빈을 어떻게 꺼내서 받아올까?
(아래, 의존성 주입)
생성자
@Controller
class OwnerController {
private static final String VIEWS_OWNER_CREATE_OR_UPDATE_FORM = "owners/createOrUpdateOwnerForm";
private final OwnerRepository owners;
private VisitRepository visits;
private final ApplicationContext applicationContext;
public OwnerController(OwnerRepository clinicService, ApplicationContext applicationContext) {
this.owners = clinicService;
this.applicationContext = applicationContext;
}
/*@GetMapping("/bean")
@ResponseBody
public String bean(){
return "bean: " + applicationContext.getBean(OwnerRepository.class) + "\n"
+ "owners: " + this.owners;
}*/
}
필드
@Controller
class OwnerController {
private static final String VIEWS_OWNER_CREATE_OR_UPDATE_FORM = "owners/createOrUpdateOwnerForm";
@Autowired //여기부분
private OwnerRepository owners;
private ApplicationContext applicationcontext;
}
Setter
@Controller
class OwnerController {
private static final String VIEWS_OWNER_CREATE_OR_UPDATE_FORM = "owners/createOrUpdateOwnerForm";
private OwnerRepository owners;
@Autowired
public void setOwners(OwnerRepository owners){
this.owners = owners;
}
}
가장 권고하는 사항 ⇒ 생성자로 주입
class OwnerController {
private static final String VIEWS_OWNER_CREATE_OR_UPDATE_FORM = "owners/createOrUpdateOwnerForm";
private final OwnerRepository owners;
private VisitRepository visits;
private final ApplicationContext applicationContext;
public OwnerController(OwnerRepository clinicService, ApplicationContext applicationContext) {
this.owners = clinicService;
this.applicationContext = applicationContext;
}
}
OwnerController에 PetRepository 주입하기
생성자
@Controller
class OwnerController {
private final PetRepository petRepository;
public OwnerController(PetRepository petRepository) {
this.petRepository = petRepository;
}
}
필드
@Controller
class OwnerController {
@Autowired
private PetRepository petRepository; //final이 붙으면 안된다.
}
Setter
@Controller
class OwnerController {
private PetRepository petRepository;
@Autowired
public void setPetRepository(PetRepository petRepository) {
this.petRepository = petRepository;
}
}