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;
}
}