[번역] Spring (1) 역사, 핵심 요소

rin·2020년 4월 10일
12

Document 번역

목록 보기
1/22
post-thumbnail

개발에 들어가기전, 내가 사용하는 기술에 대한 이해를 돕기위해 시리즈를 작성합니다.

목표

  1. Spring 역사를 정리한다.
  2. Spring 도큐먼트에서 필요한 내용을 번역하고 요약한다. ref. Spring Document 5.2.5.RELEASE
  3. Spring의 컨셉과 핵심 요소를 이해한다.
  4. Spring의 동작원리를 이해한다.
  5. .xml 파일의 작성 방법을 정리한다.

Spring 역사


CGI - Servlet - JSP

CGI

  • Common Gateway Interface
  • 처음의 웹서버는 정적 데이터(HTML, XML, Image .. )만 전달할 수 있는 수준이었다.
  • 사용자가 동적 데이터를 요구하기 시작하자, 서버에서 다른 프로그램을 동적으로 실행시켜 결과를 전달해 주는 형식을 채택 = CGI
  • 클라이언트의 요구를 응용프로그램에 전달하고, 그 결과를 사용자에게 되돌려주기위한 표준적인 방법
  • 즉, 서버와 외부 응용 프로그램을 연결해준다.
  • interface일 뿐이고, 당시에 C, Peal을 주력으로 여러 언어로 구현됐다.

Servlet

  • CGI는 서버에서 Process 단위로 실행됐다. → 클라이언트 요청 하나에 하나의 프로세스가 생성 → 요청 늘어나면 프로세스 계속 증가 → 스레드 단위로 동작하여 서버부하를 줄이기 위해 Java Servlet이 고안된다.
  • Java에서는 Servlet을 이용해 CGI 프로그래밍을 할 수 있게 되었다.
  • Servlet도 CGI에 따라 데이터를 주고 받지만 → 이를 서블릿을 가지고 있는 컨테이너에 위임
    • 웹서버와 웹어플리케이션은 서로 다른 언어, 체계로 만들어짐 = 데이터 교환 어렵다 🤔
    • 서블릿 컨테이너와 서블릿 사이의 규약을 이용하도록 하였다.
  • 각각의 서블릿은 서블릿 인터페이스를 상속받아 구현, 이를 서블릿 컨테이너가 가지고 있다.

JSP

  • Java Servlet에서 HTML 출력을 작성하기가 너무 힘듦 → HTML에 Java코드를 넣어서 쓸 수 있게 한 것 = JSP
  • HTML 안에 자바 코드를 포함한 서버사이드 스크립트
  • Java Servlet으로 변환돼 실행되므로 서블릿과 유사하다.
    • 클라이언트의 서비스 요청 → JSP 실행 요구 → WAS의 서블릿 컨테이너에서 JSP를 서블릿 원시코드로 변환 → 컴파일 후 실행 → HTML 형태로 클라이언트에 결과 반환
      ✔️서블릿 컨테이너
  • 역할 : 통신지원, 생명주기 관리, 멀티 스레팅 관리, 선언적 보안 관리, JSP 지원
  • WAS가 Servlet container or Web container로 불리곤 한다.

EJB - Spring

EJB

  • Enterprise Java Bean
    • 📌Java Bean : 자바 객체를 재사용 가능하게 컴포넌트화(독립적 단위 모듈화) 시킬 수 있는 코딩 방침을 정의한 것
    • 비즈니스 객체들을 관리하는 컨테이너를 만들어서, 필요할 때마다 컨테이너로부터 객체를 받는 식으로 관리하자는 생각에서 탄생
    • 처음 목적 : 객체간의 의존성 해결
  • EJB가 각광받자 J2EE 서버 개발 벤더들은 EJB 스펙을 구현한 여러 WAS 제품을 출시
    • 벤더들이 제공하는 많은 서비스를 사용하기 위해서는 EJB 스펙을 지켜야했는데, 이는 점점 복잡해짐
    • EJB 컨테이너가 없을 경우 WAS의 서비스들을 이용할 수 없었다.
    • 비즈니스 로직에 특정 기술이 종속돼버림
  • 스펙을 지키기 위해 여러 클래스를 상속 받고, 그에 맞게 구현하면서 실제 비즈니스 로직보다 EJB 컨테이너를 사용하기 위한 코드가 많아지는 현상이 발생하였다.

Spring

  • 스프링 창시자인 로드 존슨은 EJB를 사용하지 않고도 객체간 의존성 해결이 가능한 컨테이너를 개발하고자 하였다.
    • WAS의 기능적인 부분을 유지하되, 기술 침투적 부분을 해결
    • 개발자는 비즈니스 로직에만 집중해라!🔥
  • 자바 엔터프라이즈 개발을 편리하게 해주는 오픈 소스, 경량급 애플리케이션 프레임워크
  • POJO(🔎Plain Old Java Object, 특정 클래스를 상속하거나 인터페이스를 구현하지 않는 평범한 자바 클래스)를 이용해 EJB의 기능을 유지하면서, 복잡성을 제거하고 객체의 라이프 사이클을 관리해준다.
    • Servlet 클래스를 상속받아 구현 → POJO ❌
    • 즉, Servlet에서 요구하는 규칙에 맞춰 클래스 만들 필요없이, POJO만으로 웹 애플리케이션을 구축하게 해준다.

🔎Spring Container
✏️ Container

  • 인스턴스의 생명주기를 관리하고 생성된 인스턴스들에게 추가적인 기능을 제공하도록 하는 것
  • response, request 객체를 생성 → 적절한 서블릿을 호출해 넘겨주고, service()를 실행시키는 일련의 서블릿 life cycle을 관장
  • 어플리케이션 운용에 필요한 객체를 생성하고 의존관계를 관리한다는 점에서 스프링도 일종의 컨테이너라고 할 수 있다.

✏️ Bean Factory

  • 스프링 설정파일(applicationContext.xml)에 등록된 빈 객체를 생성/관리하는 가장 기본적인 컨테이너 기능만 제공한다.
  • 컨테이너가 구동될 때 객체 생성하지 않고, 클라이언트가 요청할 시에만 생성한다.

✏️ ApplicationContext

  • Bean Factory(interface)를 확장한 컨테이너
  • 추가적인 기능을 지원한다.
  • 컨테이너가 구동될 때 객체 생성

Spring Document


디자인 철학

프레임 워크에 대해 배울 때는 해당 프레임 워크의 기능뿐만 아니라 그에 따르는 원칙을 아는 것이 중요합니다. Spring Framework의 기본 원칙은 다음과 같습니다.

  • 모든 개발 단계에서 선택(결정)하십시오. Spring을 사용하면 설계에 대한 결정을 최대한 늦출 수 있습니다. 예를 들어, 코드를 변경하지 않고 configuration을 통해 persistence provider를 전환 할 수 있습니다. 다른 많은 인프라 관련 문제와 타사 API와의 통합에서도 마찬가지입니다.

  • 다양한 관점을 받아들이십시오. Spring은 유연성을 수용하며 어떻게 작업을 수행하는 지에 대해서 강제하는 방식이 없습니다. 다양한 관점에서 다양한 어플리케이션의 요구사항을 지원합니다.

  • 이전 버전과의 호환성을 유지하십시오. Spring은 버전을 업그레이드 함에 있어서 과도한 변경 사항을 발생시키지 않도록 신중하게 관리됩니다. Spring은 신중하게 선택된 JDK 버전과 써드 파티 라이브러리를 지원하여 Spring에 의존하는 애플리케이션 및 라이브러리의 유지 보수를 용이하게합니다.

  • API 디자인에 주의를 기울이십시오. Spring 팀은 많은 버전과 수년에 걸쳐 유지되는 직관적인 API를 만드는 데 많은 시간과 노력을 기울였습니다.

  • 코드 품질에 대한 높은 표준을 설정하십시오. 스프링 프레임 워크는 의의를 가지고, 정확하고, 최신인 javadoc에 중점을 둡니다. Spring은 패키지간에 순환 종속성이없는 깨끗한 코드 구조를 주장 할 수 있는 몇 없는 프로젝트 중 하나입니다.

핵심 기술

📌 가장 중요한 것은 IoC(Inversion of Control) 컨테이너
📌 이어서 AOP(Aspect-Oriented Programming)에 대한 내용을 전달한다.

IoC 컨테이너

✏️ IoC란

  • 종속성 주입(DI)이라고도 한다.
  • (1)생성자 argument, (2)factory method의 argument, (3)factory method에서 인스턴스가 구성되거나 return된 후에 설정되는 properties을 통해서만 오브젝트가 (스스로의) dependencies(종속성, 해당 오브젝트가 작동하는 다른 객체에 대한 종속)을 정의하는 프로세스
  • 스프링 컨테이너는 Bean을 만들 때, 해당 종속성을 주입한다.
  • 클래스의 direct construction(직접 구성) 또는 Service Locator 패턴 같은 매커니즘을 사용하여 Bean이 종속되는 위치나 인스턴스화를 제어하기에, 제어의 반전(Inversion of Control)이라고 할 수 있다.

✏️ 핵심 요소

유형이름
packageorg.springframework.beans
packageorg.springframework.context
interfaceBeanFactory
interfaceApplicationContext

BeanFactory : 모든 유형의 객체를 관리할 수 있는 configuration 매커니즘을 제공

  • configuration framework 와 기본 기능을 제공

ApplicationContext : BeanFactory의 하위 인터페이스

  • BeanFactory의 완전한 superset
  • IoC 컨테이너의 핵심으로서 IoC를 이해하는데 반드시 필요한 요소.
  • Spring의 AOP 기능을 사용한 간편한 통합
  • Message resource handling (for use in internationalization)
  • Event publication
  • 웹 응용 프로그램에서 사용하기 위한 WebApplicationContext와 같은 application-layer 별 context

Bean : Spring에서 Application의 backbone(척추-핵심)을 형성하고 Spring IoC 컨테이너에 의해 인스턴스화 되고 조립 및 관리되는 객체

  • IoC 컨테이너에게 관리 받지 않는 Bean은 단순한 Application의 Object의 하나일 뿐이다.
  • Bean과 그 사이의 종속성은 컨테이너가 사용하는 configuration metadata에 반영된다.

✏️ Container

  • ApplicationContext interface = Spring IoC Container
  • 객체의 인스턴스화, configuring, Bean 조합
  • configuration metadata는 인스턴스화, configure, 조합할 오브젝트에 대한 지시 사항을 가지고 있으며 이는 XML, Java annotations, Java code로 쓰여져있다.
  • 이를 통해 오브젝트 간 상호 의존성을 표현할 수 있다.
  • XML은 configuration metadata를 표현하는 전통적인 형식이지만, 소량의 XML 구성을 작성함으로써 추가적인 메타 데이터 형식에 대한 지원을 선언적으로 활성화하면 메타 데이터 표현에 Java annotations, Java code를 사용할 수 있다.
  • ApplicationContext가 생성되고 초기화 된 후, application 클래스는 configuration metadata와 결합되므로 완전히 설정된 실행가능한 시스템/어플리케이션을 얻을 수 있다.
    설명

✏️ configuration metadata

  • 컨테이너에 지시할 사항 나열
  • 전통적으로 XML 사용
  • Bean을 정의해야 container가 이를 관리한다.
  • 📌최상위 레벨 요소인 하위의 을 사용하여 나타낸다.
  • Spring 3.0+ 사용가능한 Java 기반 configuration에서는 @Configuration 클래스 내의 @Bean 어노테이션을 사용한다.
@Configuration
public class AppConfig {

    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }
}
<!-- 이는 XML에서는 아래와 같이 표현된다.
id : Bean의 식별자(문자열)
class : Bean의 type을 정의하는 완전한 (path를 포함한)클래스 이름 -->
<beans>
    <bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>
  • 이 Bean 정의는 어플리케이션을 구성하는 실제 오브젝트에 해당한다.
  • 일반적으로 Bean으로 선언하는 것 : 서비스 계층 오브젝트, 데이터 엑세스 오브젝트(DAO), Struts Action 인스턴스와 같은 프레젠테이션 오브젝트, Hibernate SessionFactories와 같은 인프라 스트럭처 객체, JMS Queues 등
  • 일반적으로 컨테이너에 세분화 된 도메인 객체를 포함시키지는 않는다. 이를 만들고 가져오는 것은 DAO 및 비즈니스 로직의 역할이기 때문이다.
  • XML 기반으로 작성할 시 여러 XML 파일에 걸쳐 Bean을 정의할 수 있다.
  • 를 사용해 다른 파일에서 Bean 정의를 가져올 수 있다.
<beans>
    <import resource="services.xml"/> <!-- 동일한 디렉토리 or 클래스 path -->
    <import resource="resources/messageSource.xml"/> <!-- resource 하위 -->
    <import resource="/resources/themeSource.xml"/> <!-- resource 하위 : 선행 슬래시('/') 무시됨, 하지만 이렇게 쓰면 상대경로로 오해할 수 있으므로 사용하지 않는 것이 좋다.-->

    <bean id="bean1" class="..."/>
    <bean id="bean2" class="..."/>
</beans>

✏️ 컨테이너 사용하기

  • T getBean(String name, Class<T> requiredType)를 사용해 Bean의 인스턴스를 검색할 수 있다.
  • ApplicationContext를 사용하면 다음처럼 Bean을 읽어와 엑세스할 수 있다.
// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

// retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class);

// use configured instance
List<String> userList = service.getUsernameList();

Bean

  • 컨테이너 내에서 Bean 정의는 BeanDefinition 오브젝트로 표시되며 다음과 같은 메타 데이터가 포함된다.
    • 패키지가 명시된 클래스 이름 : 일반적으로, 정의된 Bean의 실제 구현 클래스
    • Bean 동작 configuration elements : Bean이 컨테이너에서 어떻게 작동할 것인지 (scope, lifecycle callbacks 등)
    • Bean이 작업을 수행하는데 필요한 다른 Bean에 대한 참조 (collaborators나 dependencies라고 부르기도 한다.)
    • 새로 설정된 오브젝트의 기타 구성 (ex. pool의 크기 제한 또는 connection pool을 관리하는 Bean에서 사용할 connection 개수)
    • 이 메타 데이터는 각 Bean 정의를 구성하는 property set으로 변환된다.
      • Class, Name, Scope, Constructor arguments, Properties, Autowiring mode, Lazy initialization mode, Initialization method, Destruction method

의존성

📌일반적인 엔터프라이즈 어플리케이션은 단일 오브젝트(Spring에서의 Bean)로 구성되지 않는다. Bean은 함께 작동하는 몇 가지 객체를 가진다.

✏️ 의존성 주입

  • 다음을 통해서만 개체의 종속성이 정의된다.
    • 생성자 arguments
    • factory method arguments
    • factory method에서 생성되거나 리턴된 후 오브젝트 인스턴스에 설정된 properties
  • 컨테이너는 Bean을 만들 때마다 해당 종속성을 주입한다.
  • Bean의 종속성 위치나 인스턴스화를 제어한다.
  • 생성자 기반 의존성 주입과 setter 기반 의존성 주입, 두 가지로 존재한다.
    • Spring에서는 일반적으로 생성자 주입을 권고한다.
      • 애플리케이션 컴포넌트를 변경 불가한 객체로 구현한다.
      • 필요한 의존성이 null이 아님을 보장한다.
      • 생성자 주입 구성 요소는 항상 완벽하게 초기화된 상태로 호출 코드로 반환된다.
      • 주의 : 생성자에 argument가 많으면 코드가 좋지 않을 수 있으며 클래스가 너무 많은 책임을 가질 수 있다. 이는 적절한 분리 문제를 해결하기 위해 리팩토링이 불가피함을 의미한다.
    • setter 주입은 합리적인 기본값을 할당 할 수 있는 곳에서만 사용하도록 한다. 그렇지 않으면 의존성을 사용하는 모든 코드에서 null 검사를 수행해야한다.

✏️ 컨테이너의 Bean 의존성 분석

  1. ApplicationContext는 모든 Bean을 설명하는 configuration metadata로 생성되고, 초기화된다. configuration metadata는 XML, Java code, annotations으로 지정할 수 있다.
  2. 각 Bean의 의존성은 property 형식, 생성자 arguments, 정적 팩토리 메소드(일반 생성자 대신 이를 사용하는 경우)에 대한 arguments 형식으로 표현된다. 이러한 의존성은 Bean이 실제로 생성될 때 Bean에 제공된다.
  3. 각 property 또는 생성자 argument는 (1)설정하려는 값의 실제 정의이거나 (2)컨테이너의 다른 Bean에 대한 참조이다.
  4. 값인 각 property 또는 생성자 argument는 지정된 형식으로부터 해당 property나 생성자 argument의 실제 유형으로 변환된다. 기본적으로 Spring은 문자열 형식으로 제공된 값을 int, long, String, boolean 등과 같은 모든 내장 타입으로 변환할 수 있다.

🔪스프링 컨테이너는 컨테이너가 생성 될 때 각 Bean의 configuration을 확인하지만, Bean property는 실제로 Bean이 생성되기 전까지 설정되지 않는다.(중요✔️)
단, Bean의 생성은 컨테이너 생성과 동일하다.(<Default>Singleton-scope, 사전 인스턴스화 인 경우)

  • Bean의 스코프는 사용자가 설정할 수 있으며 이를 설정하지 않는다면 Bean은 요청된 경우에만 생성된다.
  • Bean의 생성은 Bean의 의존성 및 해당 종속성의 종속성(또한, 재귀적으로 유사한 것들) 등이 생성되고 할당될 때 잠재적으로 Bean graph를 그린다.
  • 따라서 영향을 받는 Bean을 처음 생성할 때, 그러한 의존성들이 표현되는 데에 불일치가 발생할 수 있다.

🤔순환 종속

  • 클래스 A는 생성자 주입을 통해 B 인스턴스가 필요하고, 동시에 클래스 B 또한 생성자 주입을 통해 A 인스턴스가 필요한 경우
  • Spring IoC 컨테이너는 런타임에 순환 참조를 감지한다.
  • BeanCurrentlyInCreationException 발생
  • 해결책
    • 한 쪽 이상을 생성자 주입이 아닌 setter 주입을 사용한다.

  • Spring은 컨테이너를 불러올 때, 존재하지 않는 Bean과 순환 종속 참조와 같은 configuration 문제점을 감지한다.
  • Spring은 Bean이 실제로 생성될 때 property를 설정하고 가능한 한 늦게 의존성을 해결함으로써 객체 혹은 의존성 생성에 문제가 있는 객체를 요청할 때 Spring 컨테이너(컨테이너는 올바르게 로드됐음)가 예외를 생성할 수 있다.
  • 이로 인해 일부 configuration 문제에 대한 가시화가 지연될 수 있다
  • 따라서, ApplicationContext 구현시 기본적으로 사전 인스턴스화된 싱글톤 Bean으로 설정된다.
  • Bean이 실제로 필요하기 전, 생성하기 위한 약간의 시간과 메모리가 요구되므로 ApplicationContext가 만들어 질 때 configuration 이슈를 알아챌 수 있다.
  • singleton Bean이 사전 인스턴스화되지 않고 느리게 초기화되도록 기본동작을 계속 무시할 수 있다.
  • 📌순환 종속성이 없는 경우 하나 이상의 collaborating beans가 종속 Bean에 주입될 때, 각 collaborating bean은 종속 Bean에 주입되기 전에 완전히 구성된다.
    즉, Bean A가 Bean B에 종속된 경우 Spring IoC 컨테이너는 Bean A에서 setter 메소드를 호출하기 전에 Bean B를 완전히 구성한다.
  • 해당 종속성이 설정되고 관련 life cycle 메소드 ( e.g. 설정된 init 메소드 또는 InitializingBean 콜백 메소드 )가 호출된다.

Examples of Dependency Injection

Setter 주입을 사용할 경우

public class ExampleBean {

    private AnotherBean beanOne;

    private YetAnotherBean beanTwo;

    private int i;

    public void setBeanOne(AnotherBean beanOne) {
        this.beanOne = beanOne;
    }

    public void setBeanTwo(YetAnotherBean beanTwo) {
        this.beanTwo = beanTwo;
    }

    public void setIntegerProperty(int i) {
        this.i = i;
    }
}
<!-- 
*
*
* setter 주입을 사용한 경우 -->
<bean id="exampleBean" class="examples.ExampleBean">
    <!-- setter injection using the nested ref element -->
    <property name="beanOne">
        <ref bean="anotherExampleBean"/>
    </property>

    <!-- setter injection using the neater ref attribute -->
    <property name="beanTwo" ref="yetAnotherBean"/>
    <property name="integerProperty" value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

생성자 주입을 사용할 경우

public class ExampleBean {

    private AnotherBean beanOne;

    private YetAnotherBean beanTwo;

    private int i;

    public ExampleBean(
        AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
        this.beanOne = anotherBean;
        this.beanTwo = yetAnotherBean;
        this.i = i;
    }
}

<!-- 
*
*
* 생성자 주입을 사용한 경우 -->
<bean id="exampleBean" class="examples.ExampleBean">
    <!-- constructor injection using the nested ref element -->
    <constructor-arg>
        <ref bean="anotherExampleBean"/>
    </constructor-arg>

    <!-- constructor injection using the neater ref attribute -->
    <constructor-arg ref="yetAnotherBean"/>

    <constructor-arg type="int" value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

Static Factory 메소드 호출을 통해 객체 인스턴스를 리턴하는 경우

public class ExampleBean {

    // a private constructor
    private ExampleBean(...) {
        ...
    }

    // a static factory method;
    // 이 메소드에 대한 argument는 해당 인수가 실제로 사용되는 방법에 관계없이 
    // 리턴되는 Bean의 의존성으로 간주될 수 있다.
    public static ExampleBean createInstance (
        AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {

        ExampleBean eb = new ExampleBean (...);
        // some other operations...
        return eb;
    }
}
<!-- 
*
*
* Static Factory를 사용해 인스턴스를 리턴한 경우 -->
<bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance">
    <constructor-arg ref="anotherExampleBean"/>
    <constructor-arg ref="yetAnotherBean"/>
    <constructor-arg value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

depends-on

  • Bean이 다른 Bean에 의존성 주입이 돼야하는 경우 일반적으로 해당 Bean이 property로 설정됨을 의미한다.
  • 일반적으로 XML 기반 configuration metadata의 <ref/>요소 (바로 위 예시 참조)를 이용해 선언한다.
  • 때때로 Bean 간의 종속성이 덜 직접적인 경우가 있다. (e.g. DB Driver 등록같이 클래스의 정적 초기화 프로그램을 트리거하는 경우)
  • depends-on 속성은 이 요소를 사용하는 Bean이 초기화되기 전에 하나 이상의 Bean이 명시적으로 초기화되도록 할 수 있습니다.
<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
    <property name="manager" ref="manager" />
</bean>

<bean id="manager" class="ManagerBean" />
<!-- property가 아닌 외부 Bean을 강제로 우선 초기화 시키게 한다. -->
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />

Bean scopes

ScopeDescription
singleton(default) 단일 Bean 정의의 범위를 각 Spring IoC 컨테이너의 단일 오브젝트 인스턴스로 지정한다.
prototype단일 Bean 정의의 범위를 임의의 수의 오브젝트 인스턴스로 지정한다.
request단일 Bean 정의의 범위를 단일 HTTP 요청의 라이프 사이클로 지정한다. 즉, 각 HTTP 요청에는 단일 Bean 정의의 뒤에서 생성된 고유한 Bean 인스턴스가 존재한다. HTTP request가 상주하는 Spring ApplicationContext 내에서만 유효 객체를 생성하고 재사용한다. (web-aware)
session단일 Bean 정의의 범위를 HTTP 세션의 라이프 사이클로 지정한다. Bean을 해당 세션이 가진 ApplicationContext에 바인딩한다. (web-aware)
application단일 Bean 정의의 범위를 ServletContext의 라이프 사이클로 지정한다. Only valid in the context of a web-aware Spring ApplicationContext
websocket단일 Bean 정의의 범위를 WebSocket의 라이프 사이클로 지정한다. Only valid in the context of a web-aware Spring ApplicationContext

🔎Singleton-scope
단일 Bean의 공유 인스턴스 하나만 관리되며, 하나의 특정 Bean 인스턴스의 정의와 일치하는 ID 또는 ID들을 가진 Bean에 대한 모든 요청은 Spring 컨테이너에 의해 리턴된다.

다시 말해서 Bean의 정의와 범위를 singleton으로 정의할 때, Spring IoC 컨테이너는 해당 Bean 정의에 의해 정의된 오브젝트의 인스턴스를 정확히 하나 만든다. 이 단일 인스턴스는 이러한 싱글 톤 Bean의 캐시에 저장되며 해당 캐시에 등록된 Bean의 이름과 일치하는 모든 후속 요청 및 참조는 캐시 된 오브젝트를 리턴한다.


Annotation-based Container Configuration

🤔 Spring을 구성하기에 Annotation-based가 XML보다 났나요?
👉 "it depends."
각 방법마다 장단점이 있으며 일반적으로 어떤 전략이 더 적합한지 결정하는 것은 개발자의 몫이다.
어노테이션이 정의된 방식 덕에, 어노테이션은 선언에 많은 컨텍스트를 제공하여 더 짧고 간결한 configuration을 만들 수 있게 한다. 하지만 XML은 소스코드를 수정하거나 다시 컴파일하지 않고도 컴포넌트를 연결하는데 탁월하다. 일부 개발자는 소스에 가까운 연결 방식을 선호하는 반면, 다른 일부는 어노테이션이 달린 클래스가 더 이상 POJO가 아니며 configuration이 분산돼 제어하기가 더 어렵다고 주장한다.

선택에 관계없이 Spring은 두 스타일을 모두 수용하고 함께 혼합 할 수 있다. Spring은 JavaConfig 옵션을 통해 대상 컴포넌트 소스 코드를 건드리지 않고, 어노테이션을 코드에 직접 개입하지 않는 방식으로(non-invasive way ) 사용할 수 있으며, 툴링 측면에서 모든 configuration 스타일이 Spring Tools for Eclipse에서 지원되고 있다.

📌어노테이션 삽입은 XML 삽입 전에 수행된다. 따라서 XML configuration은 두 방법 모두를 통해 연결된 속성에 대한 어노테이션을 무시한다.

@Required

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Required
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}

영향을 받는 Bean property(예제에서 MovieFinder)을 구성시, Bean 정의의 명시적 property value 또는 autowiring을 통해 채워야 함을 나타낸다.
만약 그렇게 하지 않을 경우, 컨테이너에서 예외가 발생한다.
init메소드 등을 활용해 Bean 클래스 자체에 해당 property가 채워졌는지 확인하는 로직이 필요하다.
❌필수적인 setting에는 생성자 주입을 사용하기 위해 Spring Framework 5.1부터 공식적으로 사용하지 않는다.

@Autowired

public class MovieRecommender {

    private final CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}

생성자, setter, class내 의존성 property (심지어 Array, Collection, Map에도) 사용할 수 있다.
어노테이션이 달린 메소드 및 필드가 required = true 가 default로 씌어진다.

  • @Autowired(required = false)로 주입할 Bean을 만족하지 못하는 경우에는 뛰어넘게 할 수도 있다.

지정된 주입 지점에 대해 일치하는 후보 Bean이 없으면 Autowiring은 실패한다. 선언된 배열, 컬랙션 또는 맵의 경우 하나 이상의 일치하는 요소가 필요하다.
❗️Spring Framework 4.3부터 대상 Bean이 하나의 생성자만 가진다면 이 생성자에 대한 @Autowired 어노테이션은 필요하지 않다. 그러나 여러 생성자를 사용할 수 있는 경우, 컨테이너에 사용할 생성자를 지시하기 위해 적어도 하나에 @Autowired 어노테이션을 추가해야한다.

@Primary

@Configuration
public class MovieConfiguration {

    @Bean
    @Primary
    public MovieCatalog firstMovieCatalog() { ... }

    @Bean
    public MovieCatalog secondMovieCatalog() { ... }

    // ...
}

/**************************/
public class MovieRecommender {

    @Autowired
    private MovieCatalog movieCatalog;

    // ...
}

Type별 autowiring은 여러 후보로 이어질 수 있으므로 선택 프로세스를 세밀하게 조정해야할 경우가 있다.
@Primary는 여러 Bean이 단일 값 의존성에 자동 연결될 후보인 경우 특정 Bean에 우선 순위가 있음을 나타낸다. (MovieConfiguration에는 MovieCatalog Bean이 두 개 존재하지만 @Primary를 이용해 firstMovieCatalog()가 MovieRecommender의 의존성으로 들어감을 명시한다.)
여러 후보 중 하나의 @Primary Bean이 존재하면 이는 자동연결 값이 된다.

@Qualifier

public class MovieRecommender {

    @Autowired
    @Qualifier("main")
    private MovieCatalog movieCatalog;

    // ...
}

/**************************/
public class MovieRecommender {

    private MovieCatalog movieCatalog;

    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public void prepare(@Qualifier("main") MovieCatalog movieCatalog,
            CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}

@Primary는 단 하나에만 설정할 수 있다.
@Qualifier는 이보다 더 세밀하게 제어하는 경우로써, Qualifier 값을 특정 property와 연관시켜 각 유형에 대해 원하는 Bean이 선택되도록 type 일치 set을 좁힐 수 있다.
ref. Custom Qualifier와 자가 주입

@Resource

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Resource(name="myMovieFinder") 
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}

field나 Bean property setter 메소드에서 @Resource 어노테이션을 사용하여 주입을 지원한다.
이는 JavaEE의 일반적인 패턴이며, Spring 관리 객체에 대해서도 이 패턴을 지원한다.
name 속성을 갖는데, 기본적으로 이를 삽입할 Bean의 이름으로 해석한다.

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Resource
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}

이름이 명시적으로 지정되지 않은 경우엔 기본 이름은 필드 이름 또는 setter 메소드의 Bean property name을 사용한다.(위 예제는 @Resource(name="movieFinder")와 같다.)

@Value

//@Value is typically used to inject externalized properties :
@Component
public class MovieRecommender {

    private final String catalog;

    public MovieRecommender(@Value("${catalog.name}") String catalog) {
        this.catalog = catalog;
    }
}

//with the following configuration :
@Configuration
@PropertySource("classpath:application.properties")
public class AppConfig { }

//application.properties file :
catalog.name=MovieCatalog

catalog 매개 변수와 필드는 MovieCatalog(← bean이다.)값과 같다.
Spring은 내장 value resolver을 지원하며, property value를 분석하지 못하면 property name(e.g. ${catalog.name})이 값으로 삽입된다.
존재하지 않는 값을 대체할 수 없도록 엄격하게 제어하려면 다음처럼 PropertySourcesPlaceholderConfigurer Bean을 선언해야한다.

//javaConfig를 사용하여 PropertySourcesPlaceholderConfigurer를 구성할 때 @Bean 메소드는 정적이어야한다.
@Configuration
public class AppConfig {

     @Bean
     public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
           return new PropertySourcesPlaceholderConfigurer();
     }
}

위 configuration을 사용하면 어떤 ${} placeholder를 확인할 수 없는 경우 스프링 초기화 오류가 발생한다. → setPlaceholderPrefix, setPlaceholderSuffix, setValueSeparator같은 메소드를 사용해 placeholder를 커스텀으로 정의할 수도 있다.

@PostConstruct & @PreDestroy

✔️@PostConstruct

  • 객체의 초기화 부분
  • 객체가 생성된 후 별도의 초기화 작업을 위해 실행하는 메소드를 선언한다.
  • @PostConstruct 어노테이션을 설정해놓은 init 메소드는 WAS가 띄워질 때 실행된다.
    ✔️@PreDestroy
  • 마지막 소멸 단계
  • 스프링 컨테이너에서 객체(Bean)를 제거하기 전에 실행돼야 할 메소드에 사용하는 어노테이션
  • close()하기 직전에 실행 👉 ((AbstractApplicationContext)context).close()
public class CachingMovieLister {

    @PostConstruct
    public void populateMovieCache() {
        // populates the movie cache upon initialization...
    }

    @PreDestroy
    public void clearMovieCache() {
        // clears the movie cache upon destruction...
    }
}

CommonAnnotationBeanPostProcessor는 @Resource 어노테이션 뿐만 아니라 JSR-250 lifecycle 어노테이션(javax.annotation.PostConstruct 및 javax.annotation.PreDestroy)도 인식한다.
Spring 2.5에서 도입된 이러한 어노테이션은 초기화 콜백 및 소멸 콜백에 설명된 lifecycle 콜백 메커니즘의 대안을 제공한다.
CommonAnnotationBeanPostProcessor가 Spring ApplicationContext에 등록된 경우, 이런 어노테이션 중 하나를 전달하는 메소드는 해당 Spring lifecycle 인터페이스 메소드 또는 명시적으로 선언된 콜백 메소드와 동일한 시점에서 lifecycle에서 호출된다.
위의 코드에서 캐시는 초기화시 미리 채워지고, 소멸시 지워진다.
❌ @Resource와 마찬가지로 @PostConstruct 및 @PreDestroy 어노테이션 유형은 JDK 6에서 8까지 표준 Java 라이브러리의 일부였다. 그러나 전체 javax.annotation 패키지는 JDK 9의 핵심 Java 모듈과 분리되어 결국 JDK 11에서 제거되었다. 필요한 경우 javax.annotation-api 아티팩트를 Maven Central을 통해 확보해야하며 다른 라이브러리와 같이 애플리케이션의 클래스 경로에 추가하면 사용할 수 있다.

Classpath Scanning and Managed Components

💁Classpath를 스캔하여 후보 컴포넌트의 감지를 암시하는 옵션에 대해 설명한다.

  • 후보 컴포넌트는 필터 기준과 일치하고 컨테이너에 등록된 해당 Bean 정의가 있는 클래스 → Bean 등록을 수행하기 위해 XML을 사용할 필요가 없다.
  • 대신 어노테이션(e.g. @Component), AspectJ 타입의 표현식, 커스텀 필터링 기준을 사용해 컨테이너에 Bean 정의가 등록된 클래스를 선택할 수 있다.

Spring 3.0+
Spring JavaConfig 프로젝트가 제공하는 많은 기능은 핵심 Spring Framework을 일부이다!
이를 통해 기존 XML 파일을 사용하지 않고 Java를 사용하여 Bean을 정읳라 수 있다.
ref. @Configuration, @Bean, @import, @DependsOn


추가 스테레오 타입

✔️@Repository 어노테이션은 레포지토리의 역할 또는 스테레오 타입(=DAO)을 충족하는 모든 클래스를 마킹한다.
Spring은 추가 스테레오 타입 어노테이션을 제공한다.

  • @Component
  • @Service
  • @Controller
  • @Component
    위의 네가지는 📌Spring 관리 컴포넌트의 일반적인 스테레오 타입이다.
  • @Repository(persistence-Layer), @Service(service-Layer), @Controller(presentation-Layer)는 보다 구체적인 사용 사례를 위한 @Component의 구체화된 형태이다. 👉 @Component 대신 이 세 가지를 이용하여 처리하려는 측면과 더 잘 연관시킬 수 있다.
    ref. 관련 개념
    ref. java 코드에서 어노테이션을 사용해 spring 컨테이너 구성하기

BeanFactory

BeanFactory

  • Spring의 IoC 기능을 위한 기본 기반을 제공한다.

  • specific contracts(뭐라고 번역해야할지 모르겠음🤔)은 주로 Spring의 다른 부분이나 관련 프레임워크와의 통합에 사용되며, DefaultListableBeanFactory 구현체는 상위레벨의 GenericApplicationContext 컨테이너 내의 주요 위임체이다.

  • BeanFactory 및 관련 인터페이스(e.g. BeanFactoryAware, InitializingBean, DisposableBean)는 다른 프레임워크 컴포넌트와의 중요한 통합 지점이다.

    • 어노테이션이나 reflection이 필요하지 않으므로 컨테이너와 컴포넌트간 효율적이 상호작용이 가능
      ✔️reflection : 객체를 통해 클래스의 정보를 분석해내는 프로그램 기법, 실행중인 자바프로그램 내부를 검사하고 내부의 속성을 수정할 수 있도록한다.
      ✔️BeanFactory는 어플리케이션이 실행된 후 객체가 호출될 때 당시 객체의 인스턴스를 생성하는데, 그 때 사용하는 기술이 Reflection이다.
  • 어플리케이션 레벨의 Bean은 동일한 콜백 인터페이스를 사용할 수 있음 → But, 일반적으로 어노테이션 또는 프로그래밍 configuration을 통해 선언적 의존성 삽입을 선호한다.

  • core BeanFactory API level과 DefaultListableBeanFactory 구현체는 사용될 configuration format이나 컴포넌트 어노테이션에 대해 추정하고 있지 않다.

  • 이러한 모든 특징은 상속(e.g. XmlBeanDefinitionReader, AutowiredAnnotationBeanPostProcessor)을 통해 제공되며 공유 BeanDefinition 객체 핵심 메타데이터 표현으로 제공된다.

  • 이것이 Spring의 컨테이너를 유연하고 확장 가능하게 하는 본질이다.

BeanFactory or ApplicationContext

  • GenericApplicationContext와 이의 서브 클래스인 AnnotationConfigApplicationContext를 커스텀 부트스트랩을 위한 일반적인 구현으로 사용하지 '않아야 하는' 이유가 없다면 ApplicationContext를 사용해야한다.
  • 모든 공통된(일반적인) 목적을 위한 SpringCore container의 주요 진입점
    • configuration 파일 로드
    • 클래스경로 스캔 트리거
    • Bean 정의와 어노테이션된 클래스를 프로그래밍 방식으로 등록
    • (Spring 5.0) 기능적 Bean 정의를 등록
  • ApplicationContext에는 BeanFactory의 모든 기능이 포함돼있다.📌
    • Bean 처리에 대한 모든 제어가 필요한 시나리오를 제외하면, 일반적으로 BeanFactory보다 권장된다.
    • ApplicationContext(e.g. GenericApplicationContext 구현체) 내에서 여러 종류의 Bean이 규칙(Bean name, Bean type에 의해-특히 post-processers에서)에 대해 감지되는 반면 🔪일반 DefaultListableBeanFactory는 특수 Bean에 대해 무시된다.
  • 확장된 컨테이너 기능(어노테이션 처리, AOP proxying 등)의 경우 BeanPostProcessor을 상속하는 것이 필수적이다.
    • 일반 DefaultListableBeanFactory만 사용하는 경우, 이러한 post-processer는 기본적으로 감지되거나 활성화되지 않는다.
    • 이는 오히려 Bean 구성에 아무런 문제가 없어 혼동될 수 있다.
    • 이런 시나리오에서는 추가 설정을 위해 컨테이너를 완전히 부트 스트랩해야한다.

🔎BeanFactory와 ApplicationContext 인터페이스/구현체에서 제공하는 기능

Spring 동작 원리

-추가중-

XML 작성법

-추가중-

profile
🌱 😈💻 🌱

1개의 댓글

comment-user-thumbnail
2022년 3월 12일

좋은 글 잘보고 갑니다!

답글 달기