싱글톤(Singleton)이란?
# 싱글톤 : 클래스의 인스턴스가 딱 1개만 생성되는 것을 보장해주는 디자인 패턴
- 객체 인스턴스가 현재 jvm안에 하나만 존재함
- 여러 명이 동시에 Service를 요청하면 DI 컨테이너에서 각각의 Service를 생성해서 반환
-> 계속 메모리에 누적되면서 문제가 생겨벌임 ->뿌리에 뿌리까지 재생성
-> 해당 객체가 하나만 생성되고 공유하도록 설계
- 데이터 공유가 쉽다. 싱글톤 인스턴스가 전역으로 사용되는 인스턴스이기 때문에 다른 클래스의 인스턴스들이 접근하여 사용할 수 있다. 하지만 여러 클래스의 인스턴스에서 싱글톤 인스턴스의 데이터에 동시에 접근하게 되면 동시성 문제가 발생할 수 있으니 이점을 유의해서 설계하는 것이 좋다.
# Spring 내부에서 싱글톤 클래스 생성법
public class SingletonA{
// 모든 인스턴스가 이것을 공유
private static final SingletonService instance = new SingletonService();
// 외부에서 필드를 조회하는 함수
public static SingletonService getInstance(){
return instance;
}
// private로 기본 생성자를 선언해 밖에서 생성자를 통해 생성하지 못하도록 막음
private SingletonService(){
}
}
- private static으로 instance를 만들고 public static으로 getInstance를 만들어 외부에서 접근토록 만듬
- 기본생성자를 private로 선언해 외부에서 생성을 막음 ( 중복 생성 방지 )
# 싱글톤의 문제점
- 구현 코드가 많이 들어간다
- 의존관계상 클라이언트 코드가 구체 클래스에 의존한다 DIP 위반
- 클라이언트가 구체 클래스에 의존해서 OCP 원칙을 위반할 가능성이 높다
- 테스트하기 어렵다
- private 생성자로 자식 클래스를 만들기 어렵다
- 유연성이 떨어진다
- 안티패턴으로 불리기도 한다.
스프링 프레임워크에서의 싱글톤
# 싱글톤의 단점을 제거
- 스프링 프레임워크는 위 싱글톤들의 단점을 전부 제거하고 사용한다
- 스프링 컨테이너는 자동으로 싱글톤을 관리해주고 싱글톤 컨테이너 역할을 한다
- 지저분한 코드와 DIP, OCP, test 등을 내부적으로 관리해줌으로서 해결된다 ( 프로그래머가 신경쓰지 않도록 )
# 남은 싱글톤의 단점
- 객체 인스턴스를 하나만 생성해서 공유하는 방식은 여러 클라이언트가 하나의 같은
객체 인스턴스를 공유하기 때문에 싱글톤 객체는 무상태성을 유지해야한다
- 특정 클라이언트에 의존적인 필드가 있으면 안된다
- 특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안된다
- 가급적 읽기만 가능해야한다
- 필드 대신에 자바에서 공유되지 않는, 지역변수, 파라미터, ThreadLocal 등을 사용해야한다.
- 대체적으로 동기화 문제를 고려하며 설계해야한다 ( 트랜잭션같은..? )
특정 클라이언트가 값을 변경하기 때문에 생기는 문제, 그렇기에 공유필드는 항상 무상태로 설계하자
@Configuration
# @Configuration의 효능
- CGLIB라는 바이트코드 조작 라이브러리를 사용하여 AppConfig 클래스를 상속받는 임의의 다른 클래스를 만들고 그 다른 클래스를 스프링 빈으로 등록한 것 ( 이렇게 동적으로 코드를 생성하여 싱글톤을 유지시켜 준다 )
<예상코드>
@Bean
public MemberRepository memberRepository(){
if( 이미 스프링 컨테이너에 등록되어 있으면?)
return 스프링 컨테이너에서 찾아서 반환
else
기존 로직을 호출해서 생성하고 스프링 컨테이너에 등록 후 반환
}
고로 @Configuration을 붙이지 않고 @Bean만 적용한다면 싱글톤이 전혀 적용되지 않는다 ( 순수 자바 코드와 같음 )
@ComponentScan
# @ComponentScan를 쓰게된 이유
- 지금까지는 @Bean으로 직접 등록을 했다 이렇게하면 실무에서는 힘들어진다 ( 누락 문제도 발생 )
- 설정 정보가 없어도 자동으로 스프링 빈을 등록하는 컴포넌트 스캔이라는 기능 제공
- 의존관계도 자동으로 주입하는 @AutoWired 제공
- ComponentScan시 다른 Configuration도 내부에 Component를 포함해서 스캔되므로
이럴 경우 예외처리를 해주어야한다
@ComponentScan(
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
)
- @Component 어노테이션이 붙은 것을 자동으로 등록해줌
-> 그러나 클래스 자체가 빈에 등록이 되기때문에 내부 필드는 어떻게 주입될지 알 수가 없음
-> @AutoWired 사용
# @ComponentScan 동작방식
1. 일단 @Component가 붙은 모든 클래스를 빈에 등록
스프링빈의 기본이름은 클래스명을 사용하되 맨 앞글자만 소문자 사용
이름 지정 @Component("이름")
2. @Autowired로 의존관계 자동 주입
같은 타입이 있는지 조회, 그러나 같은 타입이 여러 개 있을 경우 충돌이 있을 수 있음 따로 처리해주어야함
생성자에 파라미터가 많아도 자동으로 해준다.
3.탐색위치 시작지정
basePackages를 이용해 위치 지정 : 이를 이용해 자바 프로젝트를 모두 탐색하지 않도록 도움
basePackageClasses = AutoAppConfig.class, <- 지정하지 않으면 현재 위치에 모든 하위 패키지를 전부 찾아본다
4. 패키지 위치를 지정하지 않고 설정 정보 클래스의 위치를 프로젝트 최상단에 두는 것이 관례
이렇게 두고 basePackages를 생략 + AppConfig는 대표하는 정보이기 때문에 상단에 두는 것이 가시적으로 좋다
CoreApplication에 @SpringBootApplication이 있는데 이와 같은 곳에 두어야 좋다
# 컴포넌트 스캔 기본 대상
- @Component 컴포넌트 스캔에 사용
- @Controller MVC 컨트롤러
- @Service비즈니스 로직
- @Repository 데이터 접근 계층
- @Configuration 스프링 설정 정보
# 컴포넌트 스캔 필터 종류
- annotation 기본값, 어노테이션을 인식해서 동작
- assignable_type 지정한 타입과 자식 타입을 인식해서 동작
- aspectj aspectj 패턴 사용
- regex 정규식 사용
- custom typefilter라는 인터페이스를 구현해서 처리
어노테이션(Annotation) 추가
# 내부어노테이션
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
- 메타 어노테이션들로 부가적인 기능이나 제약조건을 정의하기 위한 어노테이션이다.
@Target | Annotation이 적용될 범위를 지정한다. value값으로는 ElementType의 enum상수값이 사용되며 다중 선택도 가능하다. |
@Retention | Annotation 의 LifeCycle을 지정한다. 즉, 어노테이션이 메모리에 유지되는 시간을 지정한다. value값으로는 RententionPolicydml enum상수값이 사용된다. |
@Inherited | 어노테이션이 하위 클래스에서도 적용될 수 있도록 상속된다. |
@Documented | JavaDoc 생성시 Annotation에 대한 정보도 함께 생성한다. |
@Repeatable | 지정한 어노테이션을 사용처에서 중복해서 정의할 수 있게끔 해준다. |
# @Target(ElementType.TYPE)
- 해당 사용자가 만든 어노테이션이 부착될 수 있는 타입을 지정하는 것이다.
( 타입이란? -> 클래스 / 생성자 / 메서드 등등 )
여기서 CONSCONSTRUCTOR / METHOD / FIELD는 말 그대로 생성자 / 메서드 / 필드에 어노테이션을 부착할 수 있게 하겠다는 의미이며, TYPE의 경우 조금 생소할 수 있느데, 이는 클래스 / 인터페이스 / 열거 타입(enum)을 뜻한다.
그 외에 @Target의 매개변수로는 아래와 같은 것들이 있다.
- ANNOTATION_TYPE : 어노테이션
- LOCAL_VARIABLE : 지역(로컬) 변수
- PACKAGE : 패키지
# @Retention(RetentionPolicy.RUNTIME)
- 어느 시점까지 어노테이션의 메모리를 가져갈 지(영향을 미치는지) 설정
- SOURCE : 어노테이션을 사실상 주석처럼 사용하는 것. 컴파일러가 컴파일할때 해당 어노테이션의 메모리를 버립니다.
- CLASS : 컴파일러가 컴파일에서는 어노테이션의 메모리를 가져가지만 실질적으로 런타임시에는 사라지게 됩니다. 런타임시에 사라진다는 것은 리플렉션으로 선언된 어노테이션 데이터를 가져올 수 없게 됩니다. 디폴트값입니다.
- RUNTIME : 어노테이션을 런타임시에까지 사용할 수 있습니다. JVM이 자바 바이트코드가 담긴 class 파일에서 런타임환경을 구성하고 런타임을 종료할 때까지 메모리는 살아있습니다.
# @Documented
- 메타 데이터 어노테이션이다. 어노테이션을 정의할 때@documented 형태로 적용하여,
해당 어노테이션을 사용하는 클래스가 javadoc과 같은 문서화 될 때, 해당 어노테이션이 적용되었음을 명시하도록 한다.
#스프링 프레임워크 #스프링 컴포넌트스캔 #싱글톤 #자바 싱글톤 #스프링 싱글톤 #스프링 @Target #스프링@Retention #스프링@Documented #스프링@Indexed #스프링 컴포넌트스캔 #스프링 ComponentScan #Singleton단점
'서버개발' 카테고리의 다른 글
[Spring / JPA] 프로젝트 설계를 위한 어노테이션 (0) | 2022.02.14 |
---|---|
[JPA / DB] JPA 정의, Entity 관계모델(E-R Model) (0) | 2022.02.05 |
[Spring] 빈 생명주기( Bean Lifecycle, Scope ), AssertThat 기능 (0) | 2022.01.23 |
[Spring] Spring 프레임워크와 Bean 등록, 컴포넌트 어노테이션 (0) | 2022.01.16 |
[Spring] Spring 기본개념 (IoC와 DI, 객체지향 4대원칙, SOLID 등) (1) | 2022.01.11 |