Spring Core
Last updated
Last updated
영한님의 강의를 요약한 내용입니다.
Required
Spring Framework
Spring Boot
Optional
Spring Data
Spring Session
Spring Security
Spring Rest Docs
Spring Batch
Spring Cloud
SRP: 단일 책임 원칙(single responsibility principle)
하나의 클래스는 하나의 책임만
가져야 한다.
관심사를 분리시켜 변경이 있을 때 파급 효과를 줄일 수 있다.
클라이언트 객체는 실행하는 책임만 담당
DIP: 의존관계 역전 원칙 (Dependency inversion principle)
프로그래머는 “추상화에 의존해야지, 구체화에 의존하면 안된다.”
구현 클래스에 의존하지 말고, 인터페이스에 의존
하라.
역할(Interface)에 의존
해야 구현의 변경에 유연
해질 수 있다.
의존성 주입(DI)은 DIP 원칙을 따름
OCP: 개방-폐쇄 원칙 (Open/closed principle)
소프트웨어 요소는 확장에서는 열려 있으나 변경에는 닫혀 있어야 한다
애플리케이션을 사용 영역
과 구성 영역
으로 나눔
인터페이스를 구현한 새로운 클래스에 새로운 기능을 구현하면 사용 역영의 변경을 닫을 수 있다.
LSP: 리스코프 치환 원칙 (Liskov substitution principle)
프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.
다형성에서 하위 클래스는 인터페이스 규약을 다 지켜야 한다.
ISP: 인터페이스 분리 원칙 (Interface segregation principle)
특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다.
인터페이스의 분리로 인터페이스가 명확해지고, 대체 가능성이 높아진다.
IoC(Inversion of Control, 제어의 역전)
프로그램의 제어 흐름을
직접 제어하는 것이 아니라 외부에서 관리
하는 것
DI(Dependency Injection, 의존관계 주입)
의존관계는 정적인 클래스 의존 관계와, 실행 시점에 결정되는 동적인 객체(인스턴스)
의존 관계 둘을 분리해서 생각
스프링이 다형성 + OCP(개방-폐쇄 원칙), DIP(의존관계 역전 원칙) 가능하도록 지원
정적인 클래스 의존관계
클래스가 사용하는 import 코드만 보고 의존관계를 쉽게 판단
동적인 객체 인스턴스 의존 관계
애플리케이션 실행 시점에 실제 생성된 객체 인스턴스의 참조가 연결된 의존 관계
클라이언트 코드를 변경하지 않고, 클라이언트가 호출하는 대상의 타입 인스턴스 의존관계 변경 가능
DI Container(IoC Container)
객체를 생성하고 관리하면서 의존관계를 연결해 주는 것을 의미
의존관계 주입에 초점을 맞추면 DI 컨테이너라고 불리는게 적합
스프링은 DI, DI Container로
다형성 + OCP, DIP
을 적용시켜 클라이언트의 코드 변경 없이 기능 확장이 가능하도록 지원
도메인 설계
도메인 협력, 역할, 책임
관계 (기획자 시점)
역할과 구현을 분리
하여 자유롭게 구현 객체를 조립할 수 있도로 설계
클래스 다이어그램 (개발자 시점)
객체 다이어그램 (인스턴스끼리의 참조)
ApplicationContext = 스프링 컨테이너
스프링 컨테이너는 XML 기반 또는 애노테이션 기반의 자바 설정 클래스로 만들 수 있음
스프링 컨테이너 생성 과정
스프링 컨테이너 생성
@Configuration
선언 객체를 설정 정보
로 사용
또는 new AnnotationConfigApplicationContext(ConfigObject.class)
스프링 빈 등록
Config 객체에서 @Bean
선언된 메서드를 모두 호출해서 반환된 객체를 스프링 컨테이너에 등록
스프링 빈(스프링 컨테이너에 등록된 객체)은 @Bean 선언된 메서드명을 스프링 빈의 이름으로 사용하거나 @Bean(name="beanName")
처럼 직접 부여도 가능
빈 이름은 항상 다른 이름을 사용하자.
같은 이름을 사용하면, 다른 빈이 무시되거나, 덮어쓰이거나 설정에 따라 오류가 발생
스프링 빈 의존관계 설정
설정 정보를 참고해서 의존관계 주입(DI)
빈 조회 방법
빈 이름과 타입으로 조회: ac.getBean(빈이름, 타입)
해당 타입의 모든 빈 조회: ac.getBeansOfType()
조회 대상 스프링 빈이 없으면 예외 발생
NoSuchBeanDefinitionException: No bean named 'xxxxx' available
BeanFactory
스프링 컨테이너의 최상위 인터페이스
스프링 빈을 관리하고 조회하는 역할 담당(getBean() 제공)
BeanFactory <- ApplicationContext <- AnnotationConfig, ApplicationContext
ApplicationContext
BeanFactory 기능을 모두 상속받아서 제공
빈을 관리하고 검색하는 기능뿐만 아니라, 수 많은 편리 부가 기능 제공
MessageSource
메시지소스를 활용한 국제화 기
한국에서 들어오면 한국어, 영어권에서 들어오면 영어 출력
EnvironmentCapable
환경변수
로컬, 개발, 운영등을 구분해서 처리
ApplicationEventPublisher
애플리케이션 이벤트
이벤트를 발행하고 구독하는 모델을 편리하게 지원
ResourceLoader
편리한 리소스 조회
파일, 클래스패스, 외부 등에서 리소스를 편리하게 조회
BeanDefinition
스프링 빈 설정 메타 정보
@Bean
, <bean>
각 하나씩 메타 정보 생성
추상화된 BeanDefinition 덕분에 스프링이 (자바 코드, XML, Groovy 등)다양한 설정 형식을 지원
스프링 컨테이너는 이 메타정보를 기반으로 스프링 빈 생성
Singleton Pattern
클래스의 인스턴스가 딱 한개만 생성되는 것을 보장하는 디자인 패턴
객체 인스턴스를 두 개 이상 생성하지 못하도록 private 생성자 사용
이미 만들어진 객체를 공유 해서 효율적으로 사용 가능하지만 수 많은 문제점 보유
싱글톤 패턴을 구현하기 위해 코드가 길어질 수 있음.
의존관계상 클라이언트가 구체 클래스에 의존(DIP를 위반).
클라이언트가 구체 클래스에 의존해서 OCP 원칙을 위반할 가능성이 높음.
테스트의 어려움.
내부 속성을 변경하거나 초기화 하기 어려움.
private 생성자를 사용하다보니 자식 클래스를 만들기 어려움
결론적으로 유연성이 떨어지고, 안티패턴으로 불리움.
Singleton Container
스프링 컨테이너: 싱글톤 컨테이너 역할
싱글톤 레지스트: 싱글톤 객체를 생성하고 관리하는 기능
싱글톤 패턴의 문제점을 해결하면서, 객체 인스턴스를 싱글톤으로 관리
DIP, OCP, 테스트, private 생성자로 부터 자유로움
싱글톤 객체(스프링 빈)는 상태를 무상태(stateless)
로 설계해야 한다.
특정 클라이언트에 의존적인 필드
가 있으면 안된다.
특정 클라이언트가 값을 변경할 수 있는 필드
가 있으면 안된다.
가급적으로 읽기만 가능
해야 한다.
필드 대신에 자바에서 공유되지 않는
지역변수, 파라미터, ThreadLocal 등을 사용해야 한다.
스프링 설정 정보는 항상 @Configuration
을 사용하여 싱글톤을 보장하자.
@Configuration 선언 시 바이트코드를 조작하는 CGLIB 기술이 적용되어 싱글톤 보장
설정 정보 클래스를 상속받은 xxx@CGLIB 클래스가 싱글톤을 보장
설정 정보 클래스에 @ComponentScan
을 명시해 주면, 자동으로 class path를 탐색해서 @Component
가 명시된 class들을 스캔해서 스프링 빈으로 등록
빈 이름 기본 전략: 앞글자만 소문자로 변경된 클래스명을 사용
빈 이름 직접 지정: @Component("beanName")
의존관계 주입은 @Autowired
가 해결
기본 조회 전략: 타입이 같은 빈을 찾아서 주입
Component Scan 기본 대상
@Component
: 컴포넌트 스캔 탐색 대상
@Controller
: 스프링 MVC 컨트롤러
@Service
: 비즈니스 로직 계층
@Repository
: 스프링 데이터 접근 계층(데이터 계층의 예외를 스프링 예외로 변환)
@Configuration
: 스프링 설정 정보(스프링 빈이 싱글톤을 유지하도록 처리)
Filters
includeFilters : 컴포넌트 스캔 대상 추가 지정
excludeFilters : 컴포넌트 스캔에서 제외할 대상 지정
생성자 주입
⭐️
생성자를 통해 의존 관계를 주입받는 방법
딱 1번 호출 보장. 불변-필수 의존관계
불변하게 설계 가능하고, 의존관계 누락 방지(final)
항상 생성자 주입을 선택하자.
lombok 사용 시 @RequiredArgsConstructor
수정자 주입(Setter)
수정자 메서드를 통해서 의존관계를 주입
선택, 변경 가능성이 있는 의존 관계
선택적으로 사용할 경우(주입할 대상이 없어도 동작하도록 할 경우)
@Autowired(required = false)
단, 중간에 의존관계를 변경할 일은 거의 없음
필드 주입
외부에서 변경이 불가능하여 테스트하기 힘들다는 치명적
가급적 테스트 코드에서만 사용
일반 메서드 주입
일반 메서드를 통해 주입
한번에 여러 필드를 주입 받을 수 있는 특징이 있지만 잘 사용하지 않음
옵션
@Autowired(required=false)
자동 주입할 대상이 없으면 수정자 메서드 자체를 호출하지 않음
org.springframework.lang.@Nullable
자동 주입할 대상이 없으면 null
Optional<>
자동 주입할 대상이 없으면 Optional.empty
@Autowired
타입 매칭을 시도
여러 빈이 조회되면 필드 이름, 파라미터 이름으로 빈 이름을 추가 매칭
@Qualifier
빈 등록 시 @Qualifier 로 추가 구분자 설정
@Qualifier 매칭 ➜ 빈 이름 매칭 ➜ NoSuchBeanDefinitionException 예외
@Primary
@Autowired 시에 여러 빈이 매칭되면 @Primary가 우선권
Database Connection을 가져올 경우 등 은근 사용
참고. 빈 애노테이션 만들기
동적으로 Bean을 선택해야할 때, 다형성 코드를 유지하면서 Bean을 사용할 수 있음
편리한 자동 빈 등록 기능을 기본으로 사용하자.
수동 빈 등록은 기술 지원 객체, 다형성을 활용하는(Bean Map, List) 비즈니스 로직에 적용해보자.
직접 등록하는 빈은 설정 정보에 바로 나타나게 하는 것이 유지보수하기 좋다.
스프링 빈의 이벤트 라이프사이클
스프링 컨테이너 생성
➜ 스프링 빈 생성
➜ 의존관계 주입
➜ 초기화 콜백
➜ 사용
➜ 소멸전 콜백
➜ 스프링 종료
.
객체의 생성과 초기화를 분리하자.
생성자는 객체 생성에 책임
을, 초기화는 생성된 값들을 활용해서 커넥션 열결과 같은 무거운 동작을 수행
객체를 생성하는 부분과 초기화 하는 부분을 명확하게 나누는 것이 유지보수 관점에서 좋음
.
스프링의 빈 생명주기 콜백 지원 방법
@PostConstruct
, @PreDestory
지원
스프링에서 권장하는 방법
메서드에 애너테이션만 선언하면 되는 편리한 방법
스프링에 종속족이지 않은 기술
외부 라이브러리에 적용 불가(@Bean 설정정보 기능 활용)
설정 정보
에 초기화/종료 메서드 지정
@Bean(initMethod = "init", destroyMethod = "close")
destroyMethod 기본값이 inferred(추론)으로 등록
close, shutdown(대부분의 라이브러리가 가지는 종료 메서드 이름) 메서드를 자동으로 호출
메서드 이름의 자유로움
스프링 빈이 스프링 코드에 의존하지 않음
외부 라이브러리에도 적용 가능(코드가 아닌 설정 정보를 사용)
InitializingBean
, DisposableBean
인터페이스
InitializingBean.afterPropertiesSet()
DisposableBean.destroy()
지금은 거의 사용하지 않는 방법
스프링 전용 인터페이스에 의존
초기화, 소멸 메서드 이름 변경 불가
외부 라이브러리에 적용 불가
빈이 존재할 수 있는 범위
스프링은 싱글톤, 프로토타입, 웹 관련 스코프(request, session, application)를 지원
빈 스코프 지정 방법
.
싱글톤
기본 스코프
스프링 컨테이너의 시작~종료까지 유지되는 가장 넓은 범위의 스코프
스프링 컨테이너는 항상 같은 인스턴스의 스프링 빈을 반환
.
프로토타입
스프링 컨테이너는 프로토타입 빈의 생성과 의존관계 주입, 초기화까지만 관여하는 매우 짧은 범위의 스코프(종료 메서드 호출 X)
스프링 컨테이너에 조회할 때마다 새로운 인스턴스를 생성해서 반환
의존성 주입을 받는 시점에 각각 새로운 프로토타입 빈이 생성
프로토타입 빈을 조회한 클라이언트가 관리. 종료 메서드 호출도 클라이언트가 수행
직접적으로 사용하는 일은 매우 드묾
참고. 싱글톤 빈과 함께 사용시 문제점
스프링은 일반적으로 싱글톤 빈을 사용하여 싱글톤 빈이 프로토타입 빈을 사용
싱글톤 빈은 생성 시점에만 의존성 주입을 받으므로, 프로토타입 빈을 사용할 때마다 새로 생성해서 사용하고자하는 의도와 다르게 프로토타입 빈 스코프가 싱글톤 빈과 함께 계속 유지
ObjectProvider 을 활용한 문제 해결
지정한 빈을 컨테이너에서 대신 찾아주는 DL(Dependency Lookup) 서비스 제공
.
웹 스코프
웹 환경에서만 동작
프로토타입 빈과 다르게 스프링이 해당 스코프의 종료시점까지 관리
유지보수를 위해 필요한 곳에만 최소화해서 사용 권장
request:
HTTP 요청 하나가 들어오고 나갈 때까지 유지되는 스코프
각각의 HTTP 요청마다 별도의 빈 인스턴스 생성/관리
session:
HTTP Session과 동일한 생명주기(생성~종료)를 가지는 스코프
application:
서블릿 컨텍스트(ServletContext)와 동일한 생명주기를 가지는 스코프
websocket:
웹 소켓과 동일한 생명주기를 가지는 스코프