본문 바로가기
  • 분조장의 개발 블로그
Spring Boot

인프런 - 예제로 배우는 스프링 입문 (개정판) 정리(2)

by 무아니 2021. 6. 6.

5. 스프링 IoC <스프링 IoC 컨테이너>

IoC 컨테이너는 ApplicationContext 또는 BeanFactory를 사용한다.(주로 전자)

  ㄴ 소스코드를 직접 살펴보기엔 수준이 어렵다.

  ㄴ 대신 알아 둘 만한 점은, 문서를 보면 ApplicationContext 가 BeanFactory를 상속받는다.

 

IoC 컨테이너의 주요한 일

  ㄴ빈 만들고, 빈들 사이의 의존성을 엮어줌, 빈들 제공

  의존성 주입은 빈 끼리만 가능하다!! 즉, IoC컨테이너 안의 객체끼리만 주입해준다.

(빈으로 등록되어있는지 확인하는 법? -> Intellij 사용할 시 클래스 이름 옆에 콩 아이콘을 확인할 수 있음..)

 

모든 빈 살펴보기(테스트코드)

public class FooTest{
	
	@Autowired
	ApplicationContext applicationContext;

	@Test
	public void getBean(){

		//applicationContext.getBeanDefinitionNames() // 모든 bean 가져오기

		TrialController bean = applicationCONtext.getBean(TrialController.class); // 특정 빈 가져오기
		assertThat(bean).isNotNull(); // -> 잘 주입됐다면 Null이 아닐 예정
	}
}

그러나.. ApplicationContext 타입을 직접 사용할 일은 없음. ApplicationContext가 등록된 Bean을 알아서 주입해주기 때문.

-> 직접 IoC 컨테이너에서 꺼내나(위 예제 코드), 자동 주입해주나 객체 해시값 찍어보면 똑같음!


6. 스프링IoC<스프링 빈(Bean)>

빈: IoC 컨테이너에서 관리하는 객체

  ㄴ TrialController con = new TrialController(); // new로 만든건 빈이 아님

 

어떻게 특정한 인스턴스를 빈으로 등록하는가? -> ComponentScan 또는 직접 빈으로 등록

 

ComponentScan 으로 등록하기 

    ㄴ 애노테이션 프로세서 중 IoC컨테이너에 빈 등록할 때 사용하는 인터페이스가 많은데 이 인터페이스들을 라이프 사이클 콜백이라고 부르고,

    ㄴ @Component라는 어노테이션이 붙어있는 클래스를 찾아서 인스턴스 생성하여 빈으로 등록하는 애노테이션 프로세서가 작동한다.

    ㄴ @SpringBootApplication -> 안에 @ComponentScan 어노테이션이 어디부터~어디까지 스캔하라고 알려준다.

    ㄴ @ComponentScan이 붙어있는 클래스부터 하위 클래스까지 다 훑어보면서 @Component를 사용한 어노테이션이 붙은 모든 클래스를 찾아서 빈으로 등록해준다. 스프링이 알아서 IoC 컨테이너를 만들 때 빈 등록해준다.

      ㄴ 빈 등록에 사용할 수 있는 어노테이션으론 @Repository, @Service, @Controller, @Configuration 등 이 있다.

** 특이한 JpaRepository 구현체 등록
        ㄴ Reposiitory는 특이한 형태로 Spring Data Jpa가 제공해주는 기능에 의해 빈으로 등록된다. 특정한 애노테이션이 없어도 특정 인터페이스를 상속받고 있는 클래스를 찾아서 내부적으로 구현체를 만들어서 빈으로 등록한다.

 

직접 빈으로 등록하기

    ㄴ 스프링부트 기반으로 테스트 작성하는 방법

@RunWith(SpringRunner.class)
@SpringBootTest
public class FooTest{
	//...
}

 

    ㄴ 빈 설정 파일 작성(자바 설정파일 많이 쓰는 추세, 또는 xml파일): 자바 설정 파일-> @Configuration 붙인다.

@Configuration
public class SampleConfig { // 빈 설정 파일

  @Bean
  public TrialController trialController(){  // 1. 리턴하는 TrialController를 빈으로 등록.
      return new TrialController();
  }
}  

//@Controller -> 2. 더 이상 어노테이션을 붙일 필요가 없다.
public class TrialController{
	//...
}

=> 이렇게 작성하면 @Configuration도 빈이기 때문에 ComponentScan에 읽히게 되고, IoC컨테이너 안에 빈으로 등록된다. 

 

빈 꺼내서 쓰기

  ㄴ 지금까진 ApplicationContext에서 직접 getBean으로 꺼내서 썼지만

  ㄴ @Autowired 를 사용해서도 꺼내 쓸 수 있다. 

  ㄴ 직접 꺼내서 쓰기보단 스프링 IoC가 빈 주입해주는 방법(@Autowired사용)을 더 많이 쓰게된다.

 


7. 스프링 IoC<의존성 주입(DI)>

@Autowired(또는 @Inject) 를 어디에 붙일까?

  ㄴ 필드, setter, 생성자

 

생성자로 주입 (필드에 final 가능)

    ㄴ 생성자에 원랜 @Autowired까지 붙여줘야 빈 주입이됐다. (아래처럼)

@Autowired
public TrialController(TrialRepository repo){
	this.respo = repo;
}

    ㄴ 스프링 4.3버전부터 "어떤 클래스에 생성자가 하나뿐이고 그 생성자로 주입받는 레퍼런스 변수들이 빈으로 등록돼있다면, 그 빈을 자동으로 주입"하도록 업데이트됐으므로 지금은 안붙여줘도된다.

 

필드로 주입 (무조건 인스턴스를 생성 후 주입하기 때문에 final 붙이면 안된다)

@Autowired
private TrialRepository repo;

setter로 주입 (필드 주입과 같은 이유로 필드에  final을 붙이면 안된다.)

@Autowired
public void setTrials(TrialRepository repo){
	this.repo = repo;
}
** 잘 주입되는지 확인하기 : 프로젝트 실행으로 확인
  ㄴ 빈으로 등록되지 않은 객체를 주입하려고 할 경우 “No qualifying bean of type….” 에러 문구가 뜬다.

 

스프링에서 권장하는 빈 주입 방법 -> 생성자 주입

public class TrialController {
	
  private final TrialReposiitory repo;

  public TrialController(TrialRepository repo){
      this.repo = repo;
  }
}

TrialRepository가 없으면 이 클래스를 만들 수 없도록 강제할 수 있기 때문

ㄴ 필드, setter 인젝션은 강제할 수 없다. 의존성 없이도 만들 수 있다.

순환 참조(a->b, b->a참조 방향으로 a,b 둘 다 빈 주입을 못 하는 경우임) 의 발생을 방지할 수 있다.

        ㄴ 이런 경우는 생성자 인젝션보다 필드, setter 인젝션이 나음.

        ㄴ 필드, setter 인젝션은 일단 인스턴스를 만든 다음에 서로 인스턴스 주입을 하기 때문에 순환 참조 해결 가능

        ㄴ but, 가능하면 순환 참조가 생기지 않는 구조를 만드는게 중요하고.. 정 안될 때 필드, setter 주입을 사용

 

과제) OwnerController에 PetRepository주입하기 

더보기

- 필드 인젝션

@Autowired
private PetRepository repo;

생성자 인젝션

private TrialRepository repo;
private PetRepository petRepo;

public TrialController(PetRepository petRepo, TrialRepository repo){
	this.repo = repo;
    this.petRepo = repo;
}

- setter 인젝션

private PetRepository repo;

@Autowired
public void setPetRepository(PetRepostiory repo){
	this.repo = repo;
}

 


8. 스프링 AOP<스프링AOP>

스프링 주요 개념 3가지: IoC, AOP. PSA

AOP: aspect oriented programming (측면.. 방향..관심사.. 지향적인 프로그래밍)

    ㄴ method1, 2, 3에서 공통적으로 수행하는 부분을 공통으로 모은다.

    ㄴ 기존의 코드를 건들이지 않고 객체를 다른 객체로 바꾸는 방법

    ㄴ@Transactional 애노테이션을 Spring AOP가 적용돼있는 예시로 들 수 있음

 

StopWatch (스프링에서 제공하는 성능 측정 시계)을 사용한 예제

public String foo1(){
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();
	
    //로직

	stopWatch.stop();
	System.out.println(stopWatch.prettyPrint());
}

// foo2(), foo3()...에서도 같은 방법으로 시간 측정한다고 가정.

이런 방식은 전혀 AOP가 아님

ㄴ StopWatch에 대한 로직이 없어도 있는것처럼 동작해야함.

 

AOP 구현하는 방법 크게 3가지

    ㄴ 컴파일:   

        ㄴ .java엔 아무 코드 없는데

        ㄴ 컴파일 후 class 엔 코드 생김(StopWatch코드 추가)

        ㄴ 순서: A.java -> (AOP끼워넣기) -> A.class  , AspectJ가 제공    

    ㄴ 바이트코드 조작: A.java -> A.class 

        ㄴ 컴파일 후 .class 파일에도 아무 코드 추가 안됐는데,

        ㄴ classLoader가 클래스를 읽어와서 메모리에 올릴 때 코드가 조작(StopWatch코드가 들어감)

    ㄴ 프록시 패턴(스프링 AOP가 사용)

        ㄴ 디자인 패턴 중의 하나를 사용해서 AOP와 같은 효과를 내는 방법.

 

댓글