![]()
1. MVC
1). 동작방식
- 디스패처 -> 핸들러 매핑 -> 핸들러 어댑터 조회 -> 핸들러 어댑터 -> 뷰리졸버
- 스프링 부트는
DispacherServlet을 서블릿으로 자동 등록하면서 모든 경로를 매핑한다
(1). 핸들러 매핑 우선순위
- @RequestMapping
- 스프링 빈 이름으로 핸들러를 찾는다
(2). 핸들러 어댑터 우선순위
- @RequestMapping
- HttpRequestHandler 인터페이스
- controller 인터페이스(어노테이션 x)
1. Controller
- Controller가 ViewResolver에게 넘겨준다

2. ResponseBody
- responseBody가 있으면
ViewResolver 대신에 httpMessageConverter가 동작
- 기본 문자처리:
StringHttpMessageConverter
- 기본 객체처리:
MappingJackson2HttpMessageConverter
- byte처리 등 기타 여러 HttpMessageConverter가 기본으로 등록되어 있다

3. 참고
- 클라이언트의 HTTP Accept 헤더와 서버의 컨트롤러 반환 타입 정보 둘을 조합해서
HttpMessageConverter가 선택된다
2). 요청 매핑 핸들러 어댑터 구조
- RequestParam, RequestBody 등 ArgumentResolver에서 다 해준다
- 해당 파라미터를 지원하는지 체크하고 지원하면 resolveArgument를 호출해서 실제 객체를 생성
이렇게 생성된 객체가 컨트롤러 호출시 넘어가는 것이다.

2. 스프링 컨테이너와 빈
- @Controller, @Service, @Component 등 특정 애노테이션을 넣으면 스프링 빈으로 자동 등록된다
- 관리를 해준다는 것은 컨테이너에 객체를 등록하고 필요한 곳에 넣는다는 것이다
스프링 빈으로 등록이 되어야 스프링 컨테이너에 관리가 된다
- 컴포넌트 스캔과 자동 의존관계 설정
- @Service, @Component 등 애노테이션을 넣는다
- 자바 코드로 직접 스프링 빈 등록하기
- @Configuration 클래스안에 @Bean으로 등록해주기
1). 원리
ApplicationContext를 스프링 컨테이너라 한다
- 스프링 컨테이너는 XML을 기반 또는 애노테이션 기반의 자바 설정 클래스로 만들 수 있다
- 정확히는 스프링 컨테이너를 부를 때
BeanFactory,ApplicationContext로 구분해서 이야기 한다
BeanFactory를 직접 사용하는 경우는 거의 없다
- 스프링 컨테이너를 생성할 때는 구성정보를 넘겨줘야 한다
- 해당 구성정보를 가지고 스프링 빈 저장소에 저장시킨다 (지금은 AppConfig에 있는 정보를 저장)
- Web 라이브러리가 있으면
AnnotationConfigServletWeb어쩌고를 기반으로 애플리케이션을 구동한다
BeanFactory와 ApplicationContext
- BeanFactory는 스프링 컨테이너의 최상위 인터페이스이다
- 스프링 빈을 관리하고 조회하는 역할을 담당

ApplicationContext
- BeanFactory 기능을 모두 상속받아서 제공
- 애플리케이션을 개발할 때는 수많은 부가기능이 필요하다
- 메시지소스, 환경변수, 애플리케이션 이벤트, 편리한 리소스 조회 등 다양한 기능을 제공한다

다양한 설정 형식 지원
- 스프링 컨테이너는 다양한 다양한 형식의 설정 정보를 받아들일 수 있다
- 자바코드, XML, Groovy 등

2) 동작
- 스프링 컨테이너는 기본 싱글톤으로 동작한다
- @Configuration이 프록시기술(CGLIB)를 이용해서 싱글톤이 되도록 유지해준다
- 만약 @Configuration이 없으면 컨테이너에 등록은 되지만 싱글톤은 되지 않는다
- @Configuration을 안해도 @Autowired로 컨테이너에 있는걸 끌고와서 집어넣으면 싱글톤가능
(1). 컴포넌트 스캔
- 등록해야 할 빈이 수십, 수백개가 되면 일일이 등록하기도 귀찮고, 설정 정보도 커지고, 누락하는 문제도 발생한다
- 스프링은 설정정보 없이도 자동으로 스프링 빈을 등록하는
컴포넌트 스캔이라는 기능을 제공한다
- 말 그대로
@Component 애노테이션이 붙은 클래스를 스캔해서 스프링 빈으로 등록한다
- 스프링 빈 기본 이름은 클래스명을 사용하되 맨 앞자리는 소문자로 바껴서 사용된다
- 의존관계도 자동으로 주입하는
AutoWired라는 기능도 제공한다
@Configuration
@ComponentScan(
basePackages = "hello.core.member",
excludeFilters= @ComponentScan.Filter(
type = FilterType.ANNOTATION, classes = Configuration.class)
)
public class AutoAppConfig {
}
(2). DI
- 필드주입, Setter 주입, 생성자 주입 3가지가 있다
- 실행중에 동적으로 변하는 경우는 거의 없기 때문에 생성자 주입을 권장한다
- 필드주입
- @Autowired
- 유연하지 못하다. 테스트하기 어렵다
- 주입 대상이 없다면 오류가 발생한다
- Setter주입
- setter를 만들고 Autowired를 넣으면 된다
- setter를 만들어야 되기 때문에 중간에 바뀌면 문제가 생긴다
- 생성자 주입
- 생성자 만들어서 Autowired를 넣으면 된다
- 기본은 Autowired를 넣어야 하지만 스프링에서 생성자 주입을 많이 쓰다보니까 안넣어도 되게 편의성 패치를 했다
- 단일 생성자만 있어야 한다
조회 대상 빈이 2개 이상일 때 해결 방법
- @Autowired 필드 명 매칭
- 처음 타입 매칭을 시도하고
- 그 다음 필드 이름, 파라미터 이름으로 빈 이름을 추가 매칭한다
- 필드 이름이나 파라미터 이름에 원하는 빈 이름으로 작성
- @Quilifier
- 같은 Qualifier 이름이 붙은 애노테이션끼리 주입을 해준다
- 만약 못찾으면 같은 이름의 스프링 빈을 추가로 찾는다
- 커스텀 애노테이션 만들어서 사용할 수도 있다. 동작 원리는 똑같다
- @Primary
- 여러 빈이 매칭되면
@Primary가 우선권을 가진다
- 자세한 것이 우선권을 가진다. 즉 Quilifier가 Primary보다 우선권을 가진다
3). 빈 생명주기 콜백
- 스프링 빈 라이프 사이클
- 스프링은 객체를 생성하고, 의존관계 주입이 다 끝난 다음에야 필요한 데이터를 사용할 수 있는 준비가 완료된다. 따라서 초기화 작업은 의존관계 주입이 모두 완료되고 난 다음 호출해야한다
- 스프링 빈의 이벤트 라이프사이클
- 스프링 컨테이너 생성 -> 스프링 빈 생성 -> 의존관계 주입 -> 초기화 콜백 -> 사용 -> 소멸전 콜백 -> 스프링 종료
- 의존성이 필요한 빈을 먼저 생성하고 나중에 빈을 주입을 해준다
- 다른 클래스에서
@Component로 @PostConstruct를 해도 문제 없다
3가지 방법으로 빈 생명주기 콜백 지원
- 인터페이스(InitializingBean, DisposableBean)
- 스프링 전용 인터페이스, 해당 코드가 스프링 전용 인터페이스에 의존한다
- 내가 코드를 고칠 수 없는 외부 라이브러리에 적용할 수 없다
- 설정 정보에 초기화 메서드, 종료 메서드 지정
@Bean(initMethod = "init", destroyMethod = "close")
- 스프링에 의존하지 않는다
@Bean으로 등록할때만 발생한다
- @PostConstruct, @PreDestory 애노테이션 지원
- 자바 표준이다
- 외부 라이브러리에 적용하지 못하는 단점이 있지만, 종료 해야 하면
@Bean기능을 사용하자
4). 빈 스코프
- 빈이 존재할 수 있는 범위
- 싱글톤
- 기본 스코프, 스프링 컨테이너의 시작과 종료까지 유지되는 가장 넓은 범위의 스코프
- 프로토타입
- 빈의 생성과 의존관계 주입까지만 관여하고 더는 관리하지 않는 매우 짧은 범위의 스코프
- 웹 관련 스코프
- request: 웹 요청이 들어오고 나갈때까지 유지되는 스코프
- session: 웹 세션이 생성되고 종료될 때까지 유지되는 스코프
- application: 서블릿 컨텍스트와 같은 범위로 유지되는 스코프
@Scope("prototype")
@Bean
public class HelloBean{}
(1). 프로토타입
- 스프링 컨테이너는 빈을 생성하고, 의존관계 주입, 초기화까지만 처리를 한다
- 관리를 안하기 때문에
@PreDestory같은 종료 메서드가 호출되지 않는다
- 같은 요청이 오면 항상 새로운 프로토 타입 빈을 생성해서 반환한다

싱글톤 빈과 사용시 문제점
- 싱글톤 빈 안에 프로토타입 빈을 넣으면 프로토타입이 초기화가 안된다
- 싱글톤은 계속 유지되기 때문에 프로토타입도 같은걸 계속 들고 있는것이다
- 생성시점에 프로토타입 빈을 가지고 있기 때문에 그냥 쭉 들고 가는것이다
해결방법
- 외부에서 DI 받는게 아니라 DL(Dependency Lookup)을 이용하면 된다
- 사용할 때마다 새로 생성해서 사용하면 된다
@Scope("singleton")
static class ClientBean{
@Autowired
private ObjectProvider<PrototypeBean> prototypeBeanProvider;
public int logic(){
PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
prototypeBean.addCount();
return prototypeBean.getCount();
}
}
@Scope("prototype")
static class PrototypeBean {
private int count = 0;
public void addCount() {
count++;
}
public int getCount() {
return count;
}
@PostConstruct
public void init() {
System.out.println("PrototypeBean.init" + this);
}
@PreDestroy
public void destroy() {
System.out.println("PrototypeBean.destroy");
}
}
(2). 웹 스코프
- 스프링이 해당 스코프의 종료시점까지 관리한다. 따라서 종료 메서드가 호출된다
- 웹 환경에서만 동작한다
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
종류
- request
- Http 요청 하나당 들어오고 나갈 때까지 유지되는 스코프
- 각각의 HTTP 요청마다 별도의 빈 인스턴스가 생성되고, 관리된다
- session
- HTTP Session과 동일한 생명주기를 가지는 스코프
- application
- 서블릿 컨텍스트와 동일한 생명주기를 가지는 스코프
- WebSocket