스프링 빈?
: 스프링 컨테이너에 등록된 클래스를 말합니다.
서버가 시작되면 스프링 내부에는 컨테이너가 만들어지는데, 해당 컨테이너 안에는 클래스가 위치하며 클래스의 인스턴스화도 이루어집니다.
스프링 컨테이너/스프링 빈을 왜 사용할까?
: 스프링 컨테이너를 사용하면 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 |