기술 면접에서 막혔던 부분 공부
기존에 몇번 코드를 까본적은 있지만, 전체적으로 정리한 적이 없어서 파편적으로 답변을 했다.
ApplicationContext는 스프링 컨테이너 자신이며, 자기 자신을 Bean으로 등록 해놓았다.
하지만 이름을 등록해 두지 않았기 때문에 타입으로 주입 받으면 된다.
주입시에는 @Autowired, @Resource 둘다 사용가능하다. @Resource 는 @Autowired와 비슷하지만, Java EE 컨테이너와도 호환된다.
또 다른 방식으로는
ApplicationContextAware을 구현하면, setter로 주입해준다.

ApplicationContext의 getBean메서드를 통해서 Bean을 직접 가져올 수 있는데, 이는 BeanFactory인터페이스를 상속하고 있기 때문이다.
다만, ApplicationContext 내부에 이를 직접 구현해 놓지는 않았고, 내부에 BeanFactory를 만들고, 이를 위임하는 방식으로 구현되어 있다.
@Autowired BeanFactory beanFactory;
@Autowired ApplicationContext applicationContext;
따라서 위 두개는 서로 다른 오브젝트이다.
만약 내부에 만들어진 BeanFactory 오브젝트를 직접 사용하고 싶다면, BeanFactory를 주입 받아서, DefaultListableBeanFactory로 캐스팅해서 사용하자
ApplicationContext는 ResourceLoader이기도 하다. 더 정확히 말하면, 이를 상속한 ResourcePatternResolver 이다. (이 둘의 차이는 이름 그대로 패턴(**)으로 리소스 선택 여부)
이를 통해 클래스 리소스나, 파일 리소스 처럼 다양한 리소스를 로딩할 수 있다.
@Autowired ResourceLoader;
물론 ApplicationContext로 받아도 상관은 없지만, ISP(InterfaceSegregation원칙)에 따라 최소한의 인터페이스에 의존하는 것이 좋다.
Spring에서는 일종의 이벤트 브로커 pub/sub 구조를 제공한다.
ApplicationListener를 구현한 빈이 특정 이벤트에 반응하게 해준다.(@EventListener도)
하지만 주의할 점은 기본 구현체의 경우 리스너가 하나씩 순차적으로 실행한다는 점(오래걸리는 리스너 조심) 또 이벤트는 하나의 로컬 프로세스에 한정된다는 점을 조심하자. 또 트랜잭션 또한 전파범위를 고려해야 한다.
또 sync/blocking 방식으로 처리된다는 점(이벤트 발생시, 모든 리스너 수행이 끝날 때까지 이벤트 발생 코드가 블로킹 됨)도 정말 주의하자
이 기능을 잘 활용하면, 사이드 이펙트 류의 작업을 의존성 낮게 처리할 수 있고, 확장이 용이해진다. 실제로 framework 단에서 다양한 이벤트(빈 컨텍스트 초기화 됨, 요청 받음, 인증 실패, 어플리케이션 시작됨 등)가 발생하므로, 코드 수정없이 프레임워크 자체를 확장할 수 있다.

스프링 3.0 까지는 사용하는 프로퍼티 종류가 바뀌면 설정을 바꿔줘야 했다(예를들어 환경변수 -> JNDI)
이를 해결하기 위해, 스프링 3.1 부터는 이 모든 녀석들을 프로퍼티 소스라는 이름으로 추상화 하였다.
Environment 에서 특정 프로퍼티를 찾으면, 우선순위 순서대로 프로퍼티를 찾고 가장 먼저 찾아진 속성을 리턴한다.

그래서 어떤 소스에서 가져오던지, 일관된 방법으로 가져올 수 있다.
ApplicationContext ac;
//...
ac.getEnvironment().getProperty("os.name"); // 시스템 프로퍼티
ac.getEnvironment().getProperty("ㅔㅁ소"); // 환경변수
프로퍼티를 추가할 때는, MapPropertySource나 PropertiesPropertySource를 사용해서 추가할 수 있다.
// 프로퍼티 소스 생성
Properties p = new Properties();
p.put('db.username','test');
PropertySource<?> ps = new PropertiesPropertySource("propertySourceName",p);
// 추가
ApplicationContext ac;
ac.getEnvironment().getPropertySources().addFirst(ps);
이 방법 외에도 @Configuration 클래스에 @PropertySource 어노테이션을 붙이는 방법이 있다.
이 경우, 우선순위는 기본 프로퍼티 소스 보다 뒤에 있게 된다.
우리가 작성하는application.properties파일도 이러한 방식으로 PropertiesSource 중 하나가 되어 등록 된다.
국제화를 할 때 주로 사용되는 인터페이스이다.
스프링 컨테이너가 직접 등록하는 빈으로, 타입으로는 접근이 불가능하고 이름으로만 접근 가능하다.
둘은 모두 System.getProperties()가 리턴하는 시스템 프로퍼티를 담은 빈으로, 차이점은 타입만 다르다.
systemProperties -> Properties
systemEnvironment -> Map
// 코드 내부에서 가져오기
System.getProperties();
// 빈으로 주입 받기
@Resource Properties systemProperties; // Autowired도 가능
// Spel 표현식으로 특정 값 가져오기
@Value("#{systemProperties['os.name']}") String osName;
@Value("#{systemEnvironment['os.name']}") String osName;