스프링 컨테이너와 스프링 빈에 대하여 with 싱글톤

 

Bean 수동 등록1)

bean 관련 대부분의 기능은 BeanFactory에
ApplicationContext(스프링 컨테이너)는 그 모든 기능을 상속받아 제공함
또한 필요한 부가기능을 제공(like 환경변수(로컬,개발,운영 나눠서), 애플리케이션 이벤트 등등)
=>> 웬만하면 ApplicationContext만 사용한다.

 

ApplicationContext를 구현한 것들중 AnnotationConfigApplicationContext는 자바코드로 이루어진
AppConfig.class를 사용한다.
구현한 것들중 GenericXmlApplicationContext는 xml로 이루어진 appConfig.xml을 사용한다.

(이것이 사람들이 말하는 자바코드로 설정하는 빈과 xml로 설정하는 빈)

 

AnnotationConfigApplicationContext는 내부의 AnnotatedBeanDefinitionReader를 통해
AppConfig.class를 읽어서 BeanDefinition이라는 빈 메타정보를 생성한다.
스프링 컨테이너가 이 메타정보를 기반으로 스프링 빈을 생성한다.
xml쪽도 같은 동작원리를 가지고 있다.

 

 

Bean 수동 등록2)

이러한 AppConfig.class를 매번 호출하면 매번 받아오는 객체가 달라진다.

따라서 이를 해결하기 위해서 싱글톤 디자인 패턴을 적용

//싱글톤 디자인 패턴 직접 구현
구현하고자 하는 곳 내부에서 private 생성자를 통해 밖에서 함부로 객체를 호출 못하도록 막음.
구현하고자 하는 곳 내부에서 private static final로 객체를 생성함.
외부에서는 getInstace()를 통해서만 호출할 수 있도록 한다.
=>>혹시라도 외부에서 new로 객체 인스턴스가 생성되는 것을 방지할 수 있음,

호출했을경우 같은 객체만 호출된다.

//간단한 싱글톤 코드 예시
package hello.core.singleton;

public class SingletonService {

    private static final SingletonService instance = new SingletonService();

    public static SingletonService getInstance() {
        return instance;
    }

    private SingletonService() {}

    public void logic() {
        System.out.println("싱글톤 객체 로직 호출");
    }
}

 

싱글톤과 관련된 스프링의 역할

스프링이 @Configuration내부의 proxyBeanMethods()로 CGLIB라는 바이트코드를 조작하는 라이브러리를 사용해서 AppConfig 클래스를 상속받은 임의의 다른 클래스를 만들어 그 클래스를 빈으로 등록하는 것이다.

//관련된 이론적 설명 https://parkrc1201log.tistory.com/17

직접 getClass()를 통해 출력해본 결과 그냥 AppConfig가 아닌 CGLIB을 달고나온 클래스를 확인할 수 있다.

해당 클래스가 AppConfig 클래스를 상속받은 임의의 다른 클래스다.

이러한 CGLIB의 바이트코드 조작 내부 원리는 싱글톤의 핵심 동작이라는 점에서

빈이 스프링 컨테이너에 이미 등록되어 있다면 해당 빈을 반환하고 등록되어 있지 않다면 기존 로직을 수행하는 방식이라는 것을 알 수 있다.

스프링 컨테이너가 이러한 싱글톤 패턴을 모든 bean 객체 인스턴스에 자동으로 디폴트로 적용해준다(해당 기능을 사용안하는 것도 가능하다)..역시 스프링 짱이구만

 

주의할점

싱글톤을 적용할 때는 공유필드를 사용하지 않는 무상태stateless를 가질 수 있도록 주의해야한다.

싱글톤으로 만들어 놓은 곳에서 필드의 값이 변경되지 않도록 해야한다.

 

 

 


 

 

 

Bean 이름과 타입)
보통 빈의 타입을 해당 빈 이름으로 설정하지 않고
부모 타입으로 설정하는 이유는
의존관계가 부모 쪽을 참고하고 있기때문이다

 

빈 조회)
빈을 조회하는 기능이 존재한다.(getBean())
테스트할때 가끔 빈 이름을 조회하는 경우가 있다.
@Configuration안에 부모 타입을 가진 빈이 둘 이상일 경우
이름을 설정해서 조회해야한다.

안그러면 오류가 발생한다.

 

스프링을 쓰는 이유들)
오래된 만큼 오류 데이터가 방대하여 오류를 자세히 알려줌
스프링부트의 어노테이션들을 통해 빈과 의존성 관리를 매우 편하게 만들어줌

 

빈이름중복)

빈이름이 자동vs수동에서 중복이 된다면
수동 빈이 자동 빈을 오버라이딩해버린다.
최근 스프링부트에서는 오류를 띄우는 것을 디폴트로 해놨다.

 

@Autowired)
의존성 주입중 생성자 주입을 사용하는 게 좋다.
당연히 @Component를 통해 빈으로 등록되어있는 클래스에서 사용가능(@Test안에서도 가능)

타입이 같은 빈이 여러개일 경우 파라미터명/필드명을 다르게하면 원하는 빈을 가져와 의존성을 설정해줄 수 있다.

 

@Primary)
우선권을 부여하여 우선순위(main)의 빈을 가져오기
더 상세하게 하려면 @Qualifier<- 이 녀석으로 어노테이션을 직접 만들 수도 있다,primary보다 우선

 

빈가져오기)

List와 Map으로 빈 가져오기 가능

 

자동 vs 수동 빈 등록)
업무 로직(유사한 패턴) => 자동 빈 등록
(업무 로직중에서도 다형성이 있는 부분은 수동 빈 등록이 좋다...유지보수하기 좋음)
기술 지원 로직(영향큼,파악어려움) => 수동 빈 등록
이렇게 하는 편이 유지보수가 쉽다.
(스프링(부트)에서 하는 자동등록은 그냥 냅두자)

 

빈 생명주기 콜백)
빈생명주기에 따라
빈생성후 의존관계주입까지 마치면
초기화콜백을 하고
콜백을 받으면 초기화를 한다.
소멸전에는 소멸전콜백을 해서 마무리한다.
이러한 콜백해주는 것에는 인터페이스,메서드,어노테이션이 있다.
인터페이스의 경우 implements해서 override하면 되지만 인터페이스에 의존하게 되어 좋은 방법은 아니다.
메서드같은 경우 직접 콜백 메서드를 만들고 빈에서 해당 메서드를 지정해주면 된다.
어노테이션은 직접 만든 메서드에 콜백 어노테이션을 붙여주면 된다.
해당 어노테이션들은 자바 표준 어노테이션이기 때문에 호환성이 좋다. 어노테이션 권장!
외부 라이브러리를 이용한 코드사용시 빈 메서드 지정 방식 권장!

빈 스코프1)
빈 생명주기의 범위(스코프)는 여러 종류가 있다.
기본적으로 스프링 시작부터 끝까지 있다가 가는 싱글톤.
생성,di주입,초기화까지 하고 더는 관리하지 않는 프로토타입.
웹요청,세션,서블릿컨텍스트와 같이 가는 웹 관련 스코프들도 있다.
싱글톤은 당연히 항상 똑같은 객체이나
프로토타입은 관리하지 않기 때문에 요청할때마다 각기 다른 객체이다.
또한 그냥 프로토타입이기 때문에 자동으로 닫아주지도 않는다.(닫고싶으면 수동으로)
클라이언트빈이 싱글톤일때 프로토타입인 빈을 주입받으면 프로토타입빈 하나를 싱글톤의 특성으로 계속 쓰게 된다.
여러 싱글톤빈에서 프로토타입빈을 주입받는다면 각각 다른 프로토타입빈 하나씩을 계속 쓰게 된다.
근데 어짜피 실무에서는 프로토타입을 거의 사용하지 않는다고 한다!!!

빈 스코프2)
의존관계 조회(dl),지연조회 할때에 ObjectProvider를 사용한다.
이러한 지연 조회 기능을 이용한다면 웹관련 스코프(request스코프)로 로그를 띄울 수 있게된다.
(요청이 들어와야 request 스코프 빈이 생성되므로, ObjectProvider를 사용해 빈 생성 시점을 지연시킴으로써 요청이 들어온 후 정상적으로 로그를 출력할 수 있다.)
스코프 어노테이션에 프록시모드를 설정하면 CGLIB를 통해 가짜 프록시 객체를 만들어서 대체한뒤 기능을 사용할때 진짜를 찾아서 동작한다.(내부에 찾는 기능이 있음)

++이러한 스코프빈은 나중에 테스트하기도 힘들어져서 필요한 곳만 사용하는 것이 좋다고 한다.

 

 

 

//김영한 강사님의 '스프링 핵심 원리 - 기본편을 참고하여 작성하였다.

//https://inf.run/UxwvF

 

 

 

잘못된 정보 말씀해주시면 수정합니다. 읽어주셔서 감사합니다.