spring 개발을 하다보면 코드에서 Bean을 많이 볼 수 있다.
빈이란 추상적으로 말하자면 spring이 만들어주는 객체이다.
우리가 보통 객체를 생성한다고 하면 어떻게 하는가?
예시를 들어 생각해보자. 예시는 저번 포스트에서 이어진다. ==> 보고 오자!
public interface Product {
// void ~
}
public class Paper implements Product {
//public void ~
}
public class Store{
private Product product;
public Store (Product product){
this.product = product;
}
//product 객체를 이용한 구현부
}
public class Main {
public static void main(String[] args) {
Product product = new Paper(); // product 객체를 주입
Store store = new Store(product);
}
}
보통 객체의 생성에 있어서는 사용자가 관리한다. 쉽게 new를 붙여서 객체 생성을 해야한다고 옛날의 java 수업을 기억해보자.
bean이 spring이 만든 객체라는 뜻은
spring이 객체를 생성해주고 또 초기화해주고 소멸시키는 통상 객체의 생체주기를 관여하는 행동이라고 불리는 행위에서의 객체에 해당하는 것이 바로 bean이다.
스프링으로 간단한 프로젝트를 클론코딩이라도 해봤던 사람이라면 알만한 방법이다.
생각해보자. 우리는 서비스 객체, 컨트롤러 객체, 레포지토리 객체를 구현하고 실제로 웹이 돌아가는 걸 알면서도 의문을 품지 않았다. 혹은 품었더라고 넘어갔었다.
나는 이런 질문이 떠오른다.
이 컨트롤러 클래스는 도대체 어디서 만들어졌고 호출되거나 주입됐길래 알아서 돌아가는 거지??
그렇다. 우리가 넣었던 @Service @Repository @ Controller 혹은 @Component 어노테이션이 붙은 클래스들ㅇ bean이 되는 것이다. 스프링은 이들을 찾아내서 bean으로 등록한다.
bean 으로 등록된 객체는 spring에서 만들어주고 초기화해주는 것이다. 정확히는 spring IoC 컨테이너에서 해주는 것이다.
bean에 대해서 찾아볼 때 빈을 등록해왔던 방법이 많이 바뀌었다는 것을 알 수 있었다.
본디 bean을 등록하려면 xml 설정 파일에 정의를 해야했었고 차츰 우리가 아는 어노테이션 기반 방법도 생겨났고 configuratoin 어노테이션을 활용해서 config 클래스 안에 bean을 등록하는 방법도 생겼다.
사실 이 부분에 대해서는 다음 포스트에서 자세히 다루려고 한다.
더 찾아봐야하기 때문에,,,,나는 아무것도 모르고 스프링을 한다고 했었나😭.
그래서 bean 이야기를 왜 했냐?
바로 spring을 사용하면 쉽게 할 수 있는 의존성 주입에 대해서 설명하기 위해서였다. 의존성 주입에 대해서는 간단하게 정리한 포스트가 있었는데 방법론만 설명한 것이고 뿌리를 제대로 이야기 안 한 것 같아서이다.
스프링의 IoC 컨테이너는 객체의 생체주기를 관리해준다고 위에서 설명했었다. 과연 그뿐일까?
당연히 아니다. spring은 bean 객체에서 의존하는 객체의 의존성 주입에 대한 사용자의 역할도 뺐어가버린다.
public interface Product {
// void ~
}
public class Paper implements Product {
//public void ~
}
@Component
public class Store {
private Product product;
@Autowired
public Store(Product product) {
this.product = product;
}
public void sellProduct() {
product.sell();
}
}
자 게시물 초반의 코드와 비교해서 바뀐 점을 찾아보자.
어노테이션이 추가되지 않았는가? 또 무슨 변화가 있을까?
바로 main 메서드 부분을 쓰지 않았다. 그냥 빠트린 것이 아니라 이제는 필요없어진 것이다.
@Component 로 인해서 Store store = new Store가 필요 없어졌고 @AutoWired 덕분에 그 뒤에 Product product = new Paper();를 하고 생성자에 추가해주는 메서드도 필요없어진 것이다.
좋은 질문이다!!Product product = new Paper(); 이 코드가 없어져 버려 product의 구현체에서 무엇을 택할지가 모호해졌다. 인터페이스에 대한 구현체가 하나일 땐 알아서 단일의 구현체를 찾아 주입해준다. 하지만 구현체가 2개 이상이면? 이에 대한 방법으로 2가지가 있다.
@Primary 어노테이션을 활용할 수 있다. 이 어노테이션은 스프링에게 말한다.
"얘가 우선적인 구현체야!"
spring은 한 interface에 여러 구현체가 있다면 저 어노테이션이 붙은 구현체로 주입해준다.
@Component
public class Store {
private Product product;
@Autowired
@Qualifier("paper") // Bean 이름을 지정하여 Paper 객체 주입
public Store(Product product) {
this.product = product;
}
public void sellProduct() {
product.sell();
}
}
@Qualifier 어노테이션은 구현체에 붙여주는 어노테이션이 아니라 해당 클래스(인터페이스)를 의존하는 객체(여기선 Store)의 의존성 주입부에 붙는다. 그리고 @Qualifier의 구현체의 이름인 인자를 보고 찾아서 의존성을 주입한다.
꽤 긴 시간동안 찾아보고 블로그를 작성해봤는데 모르고 쓰던 것을 이제서야 알 게 된 것이 부끄러웠다. 앞으로도 clean 한 코드를 위해서 물심양면의 노력을 하겠다.
https://velog.io/@seasame_oil/Spring-bean
https://velog.io/@falling_star3/Spring-Boot-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B9%88bean%EA%B3%BC-%EC%9D%98%EC%A1%B4%EA%B4%80%EA%B3%84
https://velog.io/@falling_star3/SpringBoot-%EC%8A%A4%ED%94%84%EB%A7%81%EA%B3%BC-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8
그리고 Loving ChatGPT..