[스프링 입문] 스프링 예제 프로젝트 PetClinic 2

최진민·2021년 1월 15일
0

스프링 입문

목록 보기
2/4
post-thumbnail

스프링 IoC

  • 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

    • Spring에 있는 Ioc Container가 Bean(객체)을 알맞게 주입(사용처에 따라)
    • Bean들의 의존성을 관리
  • Bean

    class OwnerControllerTest {
    
    	@MockBean
    	private OwnerRepository owners; // 자동 Bean으로 등록
    • @MockBean : mock 객체를 만들어줘서 bean으로!
    • Bean : Spring이 관리하는 객체
  • 의존성 주입(Dependancy injection)

    public class SampleControllerTest {
    
    	@Test
    	public void testDosomething(){
    		SampleRepository repo = new SampleRepository();
    		SampleController sampleController = new SampleController(repo);
    		sampleController.doSomething();
    	}
    }

스프링 IoC 컨테이너

  • 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/beanbean: 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 Container를 사용하는 큰 이유 중 하나)

스프링 빈(Bean)

  • 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> {
      }
  • 그렇다면 등록한 빈을 어떻게 꺼내서 받아올까?
    (아래, 의존성 주입)


의존성 주입(Dependancy Injection)

  • 생성자

    @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;
    	}
    }
    • 필드, Setter는 의존성이 없이도 만들 수 있다. 그렇기에 생성자를 사용해 강제하는 것이다.
    • 단, 생성자의 의존성 주입은 순환참조를 야기할 수 있다.
      • ex) A → B, B → A 참조하면 만들지 못한다.
      • 이럴때, 필드, Setter로 인스턴스를 만든다.
      • 되도록이면 Circular dependancy를 발생하지 않게 생성자를 사용하는 것이 좋다.

과제

  • 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;
      	}
      }
profile
열심히 해보자9999

0개의 댓글

관련 채용 정보