Spring

[Spring] 스프링 빈과 의존성 주입 해보기

Don't stop 훈 2023. 11. 3. 13:15

📌 스프링 빈을 만드는 방법 2가지

 

스프링 컨테이너가 관리하는 객체를 스프링 빈이라고 한다.

스프링 빈으로 만들고, 스프링 컨테이너가 관리하도록 해보자.

 

스프링 빈을 만들려면 어떻게 해야 될까? 2가지 방법이 있다.

  1. @Component
  2. @Configuration + @Bean

 

1. @Component를 통해 Bean 등록하기

@RestController
public class DemoController {

    @GetMapping(path = "/test")
    public String demo() {
        return "test";
    }
}

// @RestController -> @Controller -> @Component 를 포함한다.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // -> 스프링 빈으로 등록해줘!
public @interface Controller {
	@AliasFor(annotation = Component.class)
	String value() default "";
}

 

@Controller를 뜯어보면 @Component를 가지고 있고, 스프링 빈을 만드는 1번째 방법을 사용하고 있다.

@Component 어노테이션을 가지고 있는 클래스는 스프링 애플리케이션이 실행될 때 스프링이 빈으로 등록해준다.

스프링 애플리케이션을 실행하면 아래의 그림처럼 DemoController class의 객체이 생성되고 컨테이너 관리하에 존재하게 된다.

DemoController 빈이 등록되었다.

 

 

 

2. @Configuration + @Bean을 통해 Bean 등록하기

아래처럼 @Configuration이 붙어있는 클래스에서, @Bean이 붙어있는 메서드의 반환값이 빈으로 등록된다.

@Configuration
public class Config {
    // 15 원시값은 Integer객체가 되어 빈으로 등록된다.
    @Bean
    public int age() {
        return 15;
    }

    // Person 객체가 빈으로 등록된다.
    @Bean
    public Person person() {
        return new Person("hun", 20);
    }
}

 

또한 @Configuration을 뜯어보면 @Component 가 붙어있다. 즉, 이 Configuration 클래스도 객체로 생성되어 빈으로 등록된다.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
	. . .
}

 

3개의 빈이 등록되었다.

 

 

 


📌 Controller -> Service -> Repository에서 DI 구현하기

아래 클래스는 @RestController, @Service, @Repository를 붙여줌으로써 컨트롤러, 서비스, 레포지토리 계층으로 사용하겠다는 의미가 담겨있다.

@RestController
public class DemoController {
    @Autowired // -> 필드 초기화할 객체가 필요한데 Spring님! 저에게 DemoService타입의 빈(객체)을 주세요!
    DemoService demoService; // -> 필드 초기화를 해주지 않았다..

    @GetMapping(path = "/test")
    public String demo() {
        String response = demoService.find();
        return response;
    }
}

@Service
public class DemoService {
    @Autowired
    DemoRepository demoRepository;

    public String find() {
        String data = demoRepository.find();
        return data;
    }
}

@Repository
public class DemoRepository {
    public String find() {
        return "TEST";
    }
}

 

또한 3개 모두 내부에 @Component를 가지고 있기 때문에 스프링 컨테이너가 관리하는 빈으로 등록될 것이다.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
	. . .
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {
	. . .
}

 


여기서 컨트롤러의 필드인 DemoService demoService필드를 보면 초기화를 해주지 않았다.

하지만 이미 DemoService라는 클래스는 @Service를 통해 빈으로 등록되어 있기 때문에, Spring이 DemoService 빈을 주입해 준다.

즉, 원래라면 DemoController가 new DemoService(); 를 통해 생성해줘야 했던 것을, Spring이 외부에서 주입하는 방식으로 DI를 구현한다.

 

이게 가능하기 위해서는 주입받아야 하는 곳에 @Autowired를 붙이면 된다.

@Autowired는 Spring에게 "필요한 객체를 주세요"라고 요청하는 것과 같다. 여기서는 "DemoService 타입의 빈을 주세요"라고 하는 것과 같다.

 

DemoService 또한 @Autowired를 통해 DemoRepository를 주입(DI) 받고 있다.

 

 

이제 애플리케이션을 실행시키고 localhost:8080/test라고 요청을 보내면 아래와 같이 응답이 오는 것을 확인할 수 있다.

localhost:8080/test GET요청 결과

 


요청-응답 흐름

 

빈들 사이의 의존관계