오늘의 취준/오늘의 공부

[Spring Boot] 스프링 빈 / 의존성 주입

gogoem 2023. 9. 19. 22:42
728x90

스프링 빈?

: 스프링 컨테이너에 등록된 클래스를 말합니다.

서버가 시작되면 스프링 내부에는 컨테이너가 만들어지는데, 해당 컨테이너 안에는 클래스가 위치하며 클래스의 인스턴스화도 이루어집니다.

 

스프링 빈 등록 순서

 

 

 

스프링 컨테이너/스프링 빈을 왜 사용할까?

: 스프링 컨테이너를 사용하면 Controller가 Service를, Service가 Repository를 인스턴스화하여 사용하는 경우에 클래스명 변경 또는 의존하는 클래스 변경이 일어나 인스턴스화 코드를 모두 일일히 바꿔줘야 하는 불상사를 피할 수 있습니다.

 

 

스프링 빈 등록하기

: 스프링 빈을 등록하기 위해서는 어노테이션을 이용할 수 있습니다.

  • @RestController
    • HTTP요청을 처리하는 데에 특화됨.
    • API 진입 지점을 지정하는 동시에 클래스를 Bean에 등록.
  • @Service
    • 개발자가 직접 만든 Service 클래스를 Bean에 등록할 때 사용함.
  • @Repository
    • 개발자가 직접 만든 Repository 클래스를 Bean에 등록할 때 사용함.
  • @Component
    • 주어진 클래스를 컴포넌트로 간주하는 어노테이션.
    • 스프링 서버 시작 시 자동으로 등록하는 기본 Bean으로 등록됨.

  • @Configuration
    • Class에 붙이는 어노테이션. @Bean과 함께 사용해야 함.
  • @Bean
    • 메소드에 붙이는 어노테이션. 메소드에서 반환되는 객체를 스프링 빈에 등록함.

( @Configuration과 @Bean을 통한 스프링 빈 등록은

외부 라이브러리나 프레임워크에서 만든 클래스를 등록할 때 사용)

 

 

 

 

스프링 빈 주입하기

: 스프링 빈으로 등록된 클래스를 의존성 주입하는 것을 뜻합니다.

 

  • 생성자를 이용해 주입하는 방법
    • 가장 권장되는 방법.
    • 과거에는 @Autowired 어노테이션을 붙여줘야 했지만 버전 업데이트를 거쳐 생략 가능해짐.
  • Setter 이용
    • 의존성을 주입하고 싶은 대상 빈 클래스에 setter를 만들고 @Autowired를 붙인다.
    • 빈 주입 이외에 setter을 사용하면 오작동의 가능성이 있음.
    • ex)
@Autowired
public voidsetJdbcTemplate(JdbcTemplate jdvcTemplate){
    this.jdbcTemplate = jdbcTemplate;
}
  • @Qualifier
    • 스프링 빈을 사용하는 쪽, 등록하는 쪽 모두 사용 가능함.
    • 스프링 빈을 사용하는 쪽에서만 사용하면 Bean의 이름을 적어줘야 함.
    • 양쪽에 모두 쓰면 Qualifier 끼리 연결됨.

 

 

 

스프링 컨테이너는 스프링 빈으로 등록된 클래스를 인스턴스화하고 자동으로 의존성을 결정해주기 때문에 편리하게 코드를 변경할 수 있습니다.

이때 스프링 컨테이너는 의존성을 결정하고 의존성 주입(DI Dependency Injection)을 자동으로 해 줍니다.

 

의존성 주입?

: 객체가 객체를 의존할때, 내부에서 의존하는 객체를 생성하지 않고 외부에서 생성해 의존하는 객체로 넣어주는 것을 "주입"이라고 합니다.

이때, 의존성 역전 원칙을 사용해서 interface를 만들고 여러 클래스가 해당 인터페이스를 implement 하게 한 뒤, interface를 구현한 객체를 interface 타입으로 주입해주는 것이 바로 의존성 주입입니다.

 

과일을 선택해 출력하는 코드를 의존성 주입 없이 짠다면 이렇게 짤 수 있습니다.

//FruitController.java  --> FruitService 의존
public class FruitController{
	
    private final FruitService fruitService = new FruitService();
    
	public void selectFruit(){
    	fruitService.selectFruit();
}
----------------------------------------------------------
 //FruitService.java  --> FruitRepo 의존
 public classs FruitService(){
 	private final FruitRepo fruitRepo = new SelectAppleRepo;
    
    public void selectFruit(FruitRepo fruitRepo){
        this.fruitRepo = fruitRepo;
 
 }
 --------------------------------------------------------
 //SelectAppleRepo.java
 public interface SelectAppleRepo {
    public void selectFruit(){
		println("Apple");
       }
	}
}
 --------------------------------------------------------
 //SelectBananaRepo.java
 public interface SelectBananaRepo {
    public void selectFruit(){
		println("Banana");
       }
	}
}
 --------------------------------------------------------
 //SelectOrangeRepo.java
 public interface SelectOrangeRepo {
    public void selectFruit(){
		println("Orange");
       }
	}
}

 

 

해당 코드는 Apple을 출력하게 됩니다.

 

 

만약 Banana, 또는 Orange를 출력하고 싶다면 FruitService를 아래와 같이 바꿔줘야 합니다.

 //FruitService.java
 //Banana 출력
 public classs FruitService(){
 	private final FruitRepo fruitRepo = new SelectBananaRepo;
    
    public void selectFruit(FruitRepo fruitRepo){
        this.fruitRepo = fruitRepo;
 
 }
 
 //Orange 출력
 public classs FruitService(){
 	private final FruitRepo fruitRepo = new SelectOrangeRepo;
    
    public void selectFruit(FruitRepo fruitRepo){
        this.fruitRepo = fruitRepo;
 
 }

FruitService가 Repository 파일인
SelectAppleRepo, SelectBananaRepo, SelectOrangeRepo를 의존하기 때문입니다.
하지만 의존성을 바꾸기 위해서는 Service 파일을 수정해줘야 한다는 어려움이 있습니다.



아래와 같이 Service 와 Repository의 중간에 interface를 넣어주고, 해당 인터페이스를 Service와 Repository가 의존하게 함으로써 의존성을 역전시켜주면 해당 문제를 해결할 수 있습니다.

 

해당 프로세스를 적용하면 코드는 아래와 같이 바뀌게 됩니다.

아래 코드와 같이 인터페이스를 여러 클래스가 implement 하는 상태라 컨테이너가 자동으로 의존성 결정을 하지 못하는 경우, @Primary 어노테이션을 주입할 빈에 붙여줘서 우선순위를 부여할 수 있기 때문에 Service 클래스를 수정하지 않고 @Primary 어노테이션을 이동하는 것으로 의존성을 바꿀 수 있습니다.

 

//SelectFruitController.java  --> SelectFruitService 의존
@RestController                 //스프링빈 등록
public class FruitController{
	
    private final FruitService fruitService = new FruitService();
    
	public void selectFruit(){
    	fruitService.selectFruit();
}
----------------------------------------------------------
 //SelectFruitService.java  --> SelectFruitRepo 의존
 @Service                      //스프링빈 등록
 public classs FruitService(){
 	private final FruitRepo fruitRepo;
    
    public void selectFruit(FruitRepo fruitRepo){
        this.fruitRepo = fruitRepo;
 
 }
 --------------------------------------------------------
 //SelectFruitRepo.java   --> 인터페이스
 public interface SelectFruitRepo {
 	public void selectFruit();
}
---------------------------------------------------------
//SelectAppleRepo.java  --> SelectFruitRepo 구현
@Repository                      //스프링빈 등록
@Primary                         //스프링 컨테이너가 의존성 주입 시 우선권 부여
public class SelectAppleRepo implements SelectFruitRepo {
    @Override
    public void selectFruit(){
		println("Apple");
       }
	}
}
-------------------------------------------------------
//SelectBananaRepo.java  --> SelectFruitRepo 구현
@Repository                      //스프링빈 등록
public class SelectBananaRepo implements SelectFruitRepo {
    @Override
    public void selectFruit(){
		println("Banana");
       }
	}
}
-------------------------------------------------------
//SelectOrangeRepo.java  --> SelectFruitRepo 구현
@Repository                      //스프링빈 등록
public class SelectOrangeRepo implements SelectFruitRepo {
    @Override
    public void selectFruit(){
		println("Orange");
       }
	}
}

 

 

'오늘의 취준 > 오늘의 공부' 카테고리의 다른 글

https 연결하기  (0) 2023.10.05
SOLID원칙/의존성역전의 법칙  (0) 2023.09.17
MVC 패턴  (0) 2023.09.16
비대칭키 암호화, 대칭키 암호화  (0) 2023.09.16
[Spring Boot] JPA 사용하기 / Spring Data JPA  (0) 2023.09.16