spring-boot-study
- SRP - 단일 책임 원칙
- OCP - 개방 폐쇄 원칙
- 변경에는 닫겨있고 확장에는 열려있다.
- interface를 통해 역할이 같은 객체로 변경할때는 다른 코드의 변경이 필요없다.
- 다형성을 사용했지만 ocp원칙을 지킬 수 없기 때문에 di 라는 원칙이 생긴다.
- LSP - 리스코프치환원칙
- 상속된(구현된) 자식은 부모를 완전히 대체해야한다.
- 인터페이스를 구현한 구현체는 인터페이스를 완전히 대체한다.
- 컴파일에 단순한게 아니고 기능적인 구현을 보장해야함.
- ex) 자동차 인터페이스의 엑셀은 앞으로 가는 기능인데 뒤로가게 구현하면 위반
- ISP - 인터페이스 분리 원칙
- 특정 클라이언틀르 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다.
- 자동차 인터페이스 -> 운전, 정비 인터페이스
- 사용자 클라이언트를 운전사 클라이언트, 정비사 클라이언트로 분리할 수 있음.
- 분리하면 다른 인터페이스가 변해도 영향을 주지 않는다.
- 인터페이스가 명확해지고, 대체 가능성이 높아진다.
- DIP - 의존 관계 역전 원칙
- 추상화에 의존해야 한다. 구체화는 의존하지 않는다.
- 구현 클래스에 의존하지말고 인터페이스에 의존해라
- 결국 역할에 의존해라. 객체지향에서는 역할이 중요하지 그 역할을 누가하는 지는 중요하지 않다.
- DI를 통해 OCP, DIP를 가능하게 지원
- 클라이언트 코드의 변경 없이 기능 확장
- 모든 설계에
역할
과구현
을 분리하자 - 이상적으로는 모든 설계에 인터페이스를 부여해야함
- 구현기술을 바꾸더라도 변화하기가 쉬움
- 실무적으로 고민
- 추상화 하려면 비용이 들어감
- 뭔가 문제가 생겼을떄도 인터페이스 -> 구현 클래스를 찾아서 들어가야함
- IOC 제어의 역전
- 기본적으로 클라이언트의 구현 객체가 스스로 생성하는 제어 하는 구조이다.
- 하지만, AppConfig를 통해 생성하는 흐름을 다른 객체가 하도록 역전한다.
- 클라이언트 조차도 AppConfig가 생성한다.
- 프레임워크 vs 라이브러리
- 내가 작성한 코드를 다른 곳에서 실행하면 프레임워크 ( 제어의 흐름이 내가 작성한 코드가 아니다.)
- 라이브러니는 내가 작성하고 내가 사용한다. ( 제어의 흐름이 내가 작성한 코드이다.)
- DI 의존관계 주입
- 클라이언트는 인터페이스에 의존하고, 실제 어떤 구현 객체가 사용될지는 모른다.
- 정적인 클래스 의존관계와 런타임에 결정되는 동적인 객체를 분리해야함
- 정적인 클래스 의존관계
- 코드상에 나오는 import등을 통해 바로 분석가능한 의존관계 MemberService - Member
- 동적인 클래스 의존관계
- 코드 상에서 어떤 의존관계가 생성 될지 알 수 없다.
- 런타임 시에 외부에서 실제 구현 객체를 클라이언트에 주입하는 것이 의존관계 주입이라고 한다.
- 의존관계 중비을 사용하면 정적인 클래스 의존관계를 변경하지 않고, 동적인 객체 인스턴스 의존관계를 변경한다.
- OrderSerivceImpl - DiscountPolicy (FixDiscountPolicy, RateDiscountPolicy)
- IOC 컨테이너, DI 컨테이너
- AppConfig처럼 객체를 생성하고 관리하면서 의존관계를 연결해 주는 것
- 의존관계 주입에 초점을 맞추어 최근에는
DI 컨테이너
라고 한다.
new AnnotationConfigApplicationContext(AppConfig.class)
를 호출하면 appconfig @bean이라고 등록된 클래스 정보를 통해 빈을 등록한다- 무조건 빈은 항상 다른 이름으로 부여해야한다
- 객체를 생성하고 빈들끼리의 의존관계를 설정한다.
- 단순히 자바 코드를 호출하는게 아니고 싱글톤 레지스트리를 통해 조금 다르게 동작한다.
getBean()
- 빈팩토리 -> 어플리케이션 컨텍스트
- 빈팩토리
- 스프링 컨테이너 최상위 인터페이스
- 스프링 빈을 관리하고 조회한다.
- getBean을 제공
- 어플리케이션 컨텍스트
- 빈 조회 기능 이외에 수 많은 부가기능이 필요하다.
- 메시지소스를 활용한 국제화 기능 ( 한국에서 들어오면 한국어로)
- 환경변수 (local, dev, prod) 구분해서 처리
- 애플리케이션 이벤트 - 이벤트를 발생하고 구독하는 모델을 편리하게 지원
- 편리한 리소스 조회 - 파일 클래스패스 외부 등에서 리소스를 편리하게 조회
-
설정 형식을 지원하는 추상화
-
xml을 읽어서 BeanDefinition을 만들기
-
자바코드를 읽어서 BeanDefinition을 만들기
-
빈 설정 메타정보이다.
- @Bean 로 각각 메타정보를 생성한다.
-
스프링 컨테이너는 메타정보를 바탕으로 빈을 생성한다.
-
자바코드 기반은 팩토리 메서드를 통해 빈을 등록함.
- 스프링은 엔터프라이즈로 쓰기 위해 만들어짐
- 웹어플리케이션은 동시 요청이 많음.
- 해당 객체가 한개만 생성되고 공유 되는것 -> 싱글톤
- 클래스의 인스턴스가 한개만 생성되게 보장하는 패턴
- 객체 인스턴스를 2개 이상 생성하지 못하도록 막아야 한다.
- 생성자를 private로 사용한다.
- 외부에서 new로 생성되는 것을 막는다.
- 여러가지 방법이 많음.
- 문제점
- 구현 코드가 많이 들어감
- dip 위반 구체 클래스를 의존함
- ocp 위반 가능성이 높음
- 테스트가 어려움
- 내부 속성 변경하거나 초기화 하기 어려움
- 자식 클래스를 만들기 어렵다
- 결론적으로 유연성이 떨어진다.
- 스프링 컨테이너는 싱글톤패턴의 문제점을 해결하면서 객체를 싱글톤으로 관리한다.
- 싱글톤 객체를 생성하고 관리하는 기능을
싱글톤 레지스트리
라고 한다. - 스프링의 빈은 기본적으로 싱글톤이지만 새로운 객체를 생성하는 반환하는 기능도 제공한다.
- 싱글톤 방식은 여러 클라이언트가 하나의 같은 객체 인스턴스를 공유하기 떄문에 무상태로 설계해아한다.
- 특정 클라이언트에 의존적인 필드가 있으면 안됨
- 값을 변경할 수 있는 필드가 있으면 안된다.
- 가급적 readonly
- 공유필드를 진짜 조심해야합니다.
- 스프링은 빈이 싱글톤이 되도록 보장해야한다.
- AppConfig 를 상속해서 바이트코드조작을 통해 만들어진다.
- @Configuration을 사용하지 않고 @Bean만 적용하면
- 싱글톤이 보장되지 않는다.
- 자바 코드를 그대로 실행한다.
- 스프링이 관리하지 않는 클래스가 생성된다.
- 스프링 빈을 나열하지 않고 수십 수백개가 되면 누락하는 문제가 생김
- 컴포넌트 스캔을 사용하면 @Configuration이 붙은 클래스도 등록됨
- TestConfig등도 등록됨
- 실무에서 보통은 제외하지 않지만, 테스트 하기 위해서 excludeFilter를 등록함
- @Component 어노테이션이 붙은 클래스들을 스캔한다.
- 빈의 이름은 클래스명을 사용하되 앞글자만 소문자로 사용한다.
- @Autowired의 기본 조회 전략은 타입으로 조회한다.
basePackages
옵션 을 사용해 탐색 시작 위치를 등록할 수있다.basePackageClasses = {AutoAppConfig.class}
로 클래스 위치 부터 등록한다- 지정하지 않으면 @ComponentScan이 붙은 설정 정보 클래스의 패키지가 시작 위치가 된다.
- 관례상 실무에서는 설정 정보 클래스의 위치를 프로젝트 최상단에 둔다
- @SpringBootApplication 어노테이션을 통해 이 안에 @ComponentScan이 들어 있기 때문에 여기서부터 검색한다.
- 스캔 기본 대상
- @Component
- @Controller - 스프링 mvc 컨트롤러로 인식
- @Service - 다른 추가기능은 없고 개발자들이 보고 비즈니스 로직의 위치를 파악한다.
- @Repository - 스프링 데이터 접근계층으로 인식, 데이터 계층의 예외를 스프링 추상화된 예외로 변환해준다.
- @Configuration
- 자바는 어노테이션 상속관계가 없고 스프링에서 지원하는 기능이다.
- 굳이 필터 쓰지 말자
- 스프링부트가 기존적으로 @ComponentScan을 사용한다.
- 자동 빈 등록 vs 자동 빈 등록
- 컴포넌트 스캔에 의해 자동으로 스프링 빈이 등록되는데, 그 이름이 같을 경우
예외발생
- 컴포넌트 스캔에 의해 자동으로 스프링 빈이 등록되는데, 그 이름이 같을 경우
- 수동 빈 등록 vs 자동 빈 등록
- 수동 빈 등록이 우선권을 가짐 -> 수동 빈이 자동빈을 오버라이딩 해버림
- 잡기 어려운 버그가 많이 발생한다
- 최근 스프링부트는 예외 발생한다!!!!!!!!!!!!!
- 의존관계 주입의 4가지 방법
- 생성자 주입
- 딱 1번만 주입되는 것이 보장된다.
- 불변, 필수 의존 관계에서 사용한다.
- 개발할때 불변은 엄청 중요하다. 변화하는 걸 다 따라가기에는 너무 복잡하다. -> 복잡하면 오류가 난다
- final 키워드를 통해 생성자는 무조건 값을 처리할 수 있다.
- 생성자가
딱 하나
만 있다면 @Autowired를 생략 가능하다.
- 수정자 주입
- 필드의 값을 수정할때 관례상 setter로 사용한다.
- @Autowired + setter를 통해 주입이 가능하다.
선택, 변경
가능한 의존 관계에서 사용한다.
- 필드 주입
- 코드는 간결함
- 외부에서 주입� 불가능함 -> 테스트 만들기가 너무 힘듬
- di 프레임워크가 없으면 사용 불가능
- 테스트 코드에서는 사용가능하다
- @Configuration -> 사용 할 수 있다.(수동 등록 시)
- 일반 메서드 주입
- 한번에 여러 필드 주입 가능
- 쓰지 않는다.
- 생성자 주입
- @Autowired(require=false) : 자동 주입할 대상이 없으면 수정자 메서드 자체 호출 안됨
- @Nullable : null로 넣어줌
- Optional<>
//Member는 스프링 빈이아님.
@Autowired(required = false)
public void setNoBean1(Member noBean1) {
System.out.println("noBean1 = " + noBean1);
}
@Autowired
public void setNoBean2(@Nullable Member noBean2) {
System.out.println("noBean2 = " + noBean2);
}
@Autowired
public void setNoBean3(Optional<Member> noBean3) {
System.out.println("noBean3 = " + noBean3);
}
- 불변
- 제약이 있기 때문에 어디서 수정하는지 생각할 필요가 없다.
- 대부분 의존관계는 애플리케이션 종료까지 변하면 안된다.
- 누군가 수정 할 일이 없다.
- 누락
- 프레임워크 없이 순수한 자바코드를 단위 테스트 하는 경우에 누락할 일이 없다.
- 강제하는 게 없으면 찾아봐야하는 추가 비용이 발생한다.
- final 키워드를 강제로 할 수 있다.
- 초기화 할때 생성자에서 컴파일러가 체크 해준다.
- 컴파일 오류가 제일 좋다..
- 생성자 주입을 제외하면 final키워드를 사용할 수 없다.
- 생성자가 딱 하나만 있다면 @Autowired가 생략 가능하다.
- 생성자는 빈생성과 의존관계 주입에서 일어난다
- 자바 빈 규약에서 정해진 setXxx 메소드를 사용한다.
- 메소드 위에 @Autowired 적용
- 빈이 생성되고 의존관계 주입단계에서 주입을 해준다.
- 선택, 변경 가능성이 있는 의존관계에서 사용한다.
- 주입할 대상이 선택가능하면 @Autowired(required = false)
- 필드 위에 @Autowired를 넣어서 사용한다.
- DI 컨테이너 없이 사용 불가능
- 외부에서 수정할 방법이 없음
- @RequiredArgsConstructor -> final이 붙은 필드의 생성자를 만들어줌
- @Autowired는 타입으로 조회한다.
- 해결 방법
- @Autowired 필드명 매치 : 타입 매칭 -> 두개 이상이면 -> 필드명, 생성자 파라미터명으로 매칭을 시도함
- @Quailifier : 추가 구분자를 붙여주는 방법 - 빈 이름을 변경하는 것이 아님
- 빈으로 등록될 클래스에 @Quailifier("name1")
- di 받는 곳 파라미터 앞에 (, @Quailifier("name1") name1 )로 동작가능
- 못찾으면 name1이라는 이름으로 한번더 찾아본다.
- @Qualifier를 찾을때만 쓰자!!!!!!!!!!!!!
- @Primary -> 우선순위를 지정해버린다. 여러개가 매칭되면 @Primary 등록된 빈을 가져온다.
- @Primary 와 @Quailifier가 중복 날때는 @Primary가 우선순위가 높다.
- 클라이언트가 할인의 종류를 선택할 수 있을떄
- 전략 패턴 구현
- 자동 등록을 사용하자 - @Service, @Component, @Repository, @Controller
- 비즈니스 로직과 기술 지원로직(aop)로 나눌 수 있다
- 비즈니스 로직 : 업무와 관련된 로직
- 코드가 많음
- 기본적인 패턴이 있음 controller / service / repository
- 자동기능 사용하자
- but 다형성을 적극 활용할때는 수동으로 등록해주는 것이 좋다.
- 기술지원 로직 : 데이터베이스 연결 , 공통 로그 처리
- 수가 적음 대신 광범위하게 영향을 미침
- 기술 지원 로직은 파악하기가 힘듬
- 수동 빈등록을 통해 밖으로 보이는게 좋음
- 스프링 빈의 간단한 라이프사이클
- 객체생성 -> 의존관계 주입( 예외 생성자 주입)
- 초기화 : 객체가 생성되고 필요한 데이터를 사용할 수 있는 준비가 완료되어야함
- 의존관계 주입이 완료되어야함
- 스프링이 의존관계 주입이 완료되면 스프링 빈에게 콜백 메서드를 통해서 초기화 시점을 알려주는 기능을 제공
- 스프링 컨테이너가 종료되기 직전에 소멸 콜백을 준다.
- 스프링 빈의 이벤트 라이프사이클
- 스프링컨테이너 생성 -> 스프링 빈 생성(생성자주입) -> 의존관계 주입(세터, 필드) -> 초기화 콜백(초기화 완료 콜백) -> 사용 -> 소멸전 콜백 -> 스프링 종료
- 객체의 생성과 초기화는 분리해야한다
- 생성자는 필수 정보를 받고 메모리를 할당함 -> 값을 세팅하는 정도의 레벨만 하는게 좋다.
- 초기화는 값들을 활용해서 외부 커넥션 연결 등 무거운 동작을 수행한다.
- 인터페이스 InitializingBean, DisposableBean
public class NetworkClient implements InitializingBean, DisposableBean
- 인터페이스의 단점
- 스프링에 의존적임 -> 모든 코드가 스프링에 의존해야함
- 초기화 소멸 메서드 이름을 변경할 수 없음
- 내가 코드를 고칠 수 없는 외부 라이브러리에 적용할 수 없다.
- 요새는 거의 사용하지 않는 방법이다.
- 메서드 호출방법
@Bean(initMethod = "init", destroyMethod = "close")
- 메서드 이름을 자유롭게 줄수있음
- 스프링에 의존성이 없음
- 외부 라이브러리에도 적용할 수 있음
- 종료 메서드 추론 (inferred)
- destroyMethod에 값을 주지않으면
- close, shutdown의 메소드를 자동으로 추론해서 호출해줌
- 쓰기 싫을경우 destroyMethod = ""로 적어주면된다.
- 어노테이션
- 지금은 이거 쓰면 된다
- @PostConstruct / @PreDestroy
- 메서드에다가 어노테이션에만 달아준다.
- 자바 표준이다.
- ComponentScan에 좋다아
- 외부 라이브러리를 사용하지 못하기 때문에 외부라이브러리에 필요하다면 @Bean의 initMehtod, destroyBean을 쓰자
- 위의 빈 사이클은 기본적으로 싱글톤 스코프 이기 때문이다.
- 프로토타입
- 프로토타입은 빈의 생성과 의존관계 주입까지만 관여하고 더는 관리하지 않는다.
- 웹 관련 스코프
- request : 웹 요청이 들어오고 나갈때까지의 스코프
- session : 웹 세션이 생성되고 종료될 때 까지 유지되는 스코프
- application : 웹의 서블릿 컨텍스트와 같은 범위로 유지되는 스코프
- 싱글톤과 다르게 컨테이너에서 요청할때 항상 새로운 것을 반환한다.
- 빈을 생성하고 의존관계 주입 초기화까지만 처리한다!
- 프로토타입 빈을 관리할 책임은 프로토타입 빈을 받을 클라이언트에 있다.
- @PreDestroy 동작 안함
- 종료 메서드는 직접 호출해줘야 한다.
- 싱글톤 빈에서 프로토타입 빈을 사용하면 처음 주입받을때만 싱글톤빈에서 들어와서 싱글톤빈의 필드에 있기 때문에 계속 같은 프로토타입빈이 된다.
- 프로토타입의 의도는 사용할때마다 새로 생성되어야 한다고 생각함.
- 의존관계 검색을 통해 해결
- Prototype을 위해 만들어진게 아니고 의존관계검색을 조금 더 쉽게 해주는 방법
- ObjectFactory, ObjectProvider
- getObject() 동작은 같음
- provider가 편의기능이 조금 더 추가됨
- 스프링에 의존적
- JSR-330 Provider
- 자바 표준 사용방법
- gradle 에 라이브러리 추가해야함
- get() 메서드 사용
- 심플함
- 별도의 라이브러리가 필요하다.
- 스프링이 아니여도 사용할 수 있다.
- 매번 사용할 때 마다 의존관계 주입이 완료된 새로운 객체가 필요하면 사용하면 된다.
- 실무에서 크게 사용할 일이 없다.
- provider는 DL이 필요한 경우 언제든지 사용 할 수 있다.
- 실무에서 쓰게된다면 ObjectProvider를 사용하는 게 좋다아..
- 웹 환경에서만 동작한다.
- 웹 스코프는 프로토타입과 다르게 스프링이 해당 스코프의 종료시점까지 관리한다 -> 종료메서드가 호출된다.
- request : 웹 요청이 들어오고 나갈때까지의 스코프 http 요청 `하나하나` 별도의 인스턴스 생성
- session : 웹 세션이 생성되고 종료될 때 까지 유지되는 스코프
- application : 웹의 서블릿 컨텍스트와 같은 범위로 유지되는 스코프
- websocket : socket과 동일한 생명주기
- proxyMode를 사용
- MyLogger를 상속한 바이트 조작 클래스를 받음
- 의존 관계 주입이 가짜 프록시로 주입됨
- 프록시 객체를 사용하게 되어서 싱글톤 빈을 사용하듯이 편리하게 request scope를 사용할 수 있음
- 중요한 것은 진짜 객체 조회를 꼭 필요한 시점까지 지연한다는 점