Spring/스프링 기본

[Spring] Spring Container, BeanFactory, ApplicationContext 알+ 관련 Annotation 알아보기

공대키메라 2022. 7. 26. 21:45

필자는 Spring을 굉장히 연모(?)하는데 그로 인해 공부도 많이 한 것 같은데... 

 

이 Spring Framework에서 제목과 같은 것에 대해 설명하라고 한다면 필자는 잘 못할거 같았다.

 

그러므로 이번 시간에는 Spring 의 핵심 개념들에 대해 다시 공부할 겸 Spring Container, BeanFactory, ApplicationContext 들을 어떤 친구(니가가라 하와이...)인지 알아올 것이다.

 

참고한 사이트는 다음과 같다.

 

출처:

https://www.tutorialspoint.com/spring/spring_ioc_containers.htm

https://www.baeldung.com/spring-application-context

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/BeanFactory.html

https://mangkyu.tistory.com/75

https://www.tutorialspoint.com/difference-between-bean-and-component-annotation-in-spring

https://galid1.tistory.com/510

https://mangkyu.tistory.com/234

over on GitHub (daeldung 에서 제공하는 예시 코드 주소).

 

필자는 baeldung 사이트를 기반으로 내용을 많이 정리했으니 이점 참고해주시면 감사하겠습니다. 끼에에엑!

1. Spring Container란? 

 

https://data-flair.training/blogs/wp-content/uploads/sites/2/2018/06/spring_ioc_container.jpg

 

Spring Cotainer는 스프링 프레임워크의 핵심이다. 

 

container는 객체를 생성하고 그들을 설정하는데 사용한다.  또한, Spring IoC Container들은 생성(creation)에서 죽음(destruction)까지 완전한 라이프사이클을 관리하기 위해 사용한다. 

 

Spring IoC Container는 component를 관리하기 위해 의존성 주입(DI)를 사용하고, 이러한 객체들은 Spring Beans라고 불린다.  (다시 말하면, 의존성 주입을 위해 사용되는 객체들을 Spring beans라고 부른다.)

 

container는 제공된 설정 metadata를 읽어서 어떤 객체를 초기화하고, 설정하고, 그리고 조립할지에 대한 설명을 얻는다.

 

정리하자면...

 

스프링 컨테이너는 자바 객체의 생명 주기를 관리하며, 생성된 자바 객체들에게 추가적인 기능을 제공하는 역할을 한다. 여기서 말하는 자바 객체를 스프링에서는 빈(Bean)이라고 부릅니다. 

 

스프링은 별개인 두 종류의 container를 제공한다. 

 

1-1. Spring BeanFactory Container

이것은 기본적인 지원을 DI에게 제공하는 가장 간단한 container이고, spring framework의 BeanFactory에 의해 정의된다. 

 

1-2. Spring ApplicationContext Container

이 컨테이너는 properties 파일들부터 textual한 메시지를 가지고 오는 능력 같은 enterprise-specific한 기능들을 제공한다. 

 

ApplicationContext container는 BeanFactory Container의 모든 기능을 포함한다. 그래서 일반적으로 BeanFactory 대신에 사용하는걸 추천한다.

 

BeacFactory는 여전히 데이터의 크기나 속도가 중요한 모바일 기기들이나 applet-based 어프리케이션 같은 경량화된 어플리케이션에서 여전히 이용된다. 

 

2. 빈 등록 관련 Annotation 알아보기

우선 필자가 보이게 ApplicationContext에 대해 좀 더 자세히 이해하기 위해서는 Bean 관련 Annotation 에 대해 이해하고 어떻게 사용하는지 알면 도움이 될 것 같다는 판단이 들었다.

 

그래서 우선 이에 대해 알아볼 생각이다.

 

2-1. @Configuration

 

@Bean annotation을 이용해서 수동으로 스프링 컨테이너에 빈을 등록하도록 도와준다. 

 

@Bean과 함께 사용시 등록할 때는 메소드 이름으로 빈 이름이 결정되므로 주의해야 한다. 

 

이러한 @Bean 어노테이션의 경우는 수동으로 빈을 직접 등록해줘야만 하는 상황인데, 주로 다음과 같을 때 사용한다.

  1. 개발자가 직접 제어가 불가능한 라이브러리를 활용할 때
  2. 애플리케이션 전범위적으로 사용되는 클래스를 등록할 때
  3. 다형성을 활용하여 여러 구현체를 등록해주어야 할 때

출처: https://mangkyu.tistory.com/75 [MangKyu's Diary:티스토리]

 

 

2-2. @Component

 

@Component는 우리의 custom bean들을 spring 이 자동적으로 감지하도록 도와주는 annotation이다. 

 

이 어노테이션이 붙은 클래스가 "스프링 컴포넌트"임을 나타낸다.

 

다른 말로, 어떤 정확한 코드를 적을 필요 없이, Spring은 다음을 해준다.

 

  • @Component annotation이 붙은 클래스들을 우리 application에서 스캔한다.
  • 찾은 클래스들을 초기화하고 그들안에 구체적인 의존성(dependency)을 주입(inject)한다. (줄여서 DI)
  • 필요한곳 어디서든 그들을 주입(inject)한다.

즉, Spring에서 @Component를 사용하게 되면 빈 스캐너를 통해 자동으로 빈이 등록된다는 말이다!

 

하지만, 대부분의 개발자들은 이 기능을 서비스하기위해 더 구체적인 stereotype annotation을 사용하길 선호한다. 

 

2-3. Spring Stereotype Annotation

 

stereotype의 단어 뜻을 봐보면 편견 혹은 정형화된 생각이나 이미지를 뜻한다. 

 

spring은 몇개의 구체화된 stereotype annotation들을 제공해왔다. 

 

@Controller, @Service 그리고 @Repository다. 

 

이것들은 모두 @Component 처럼 같은 기능을 제공한다. 

 

그들은 모두 동일하게 작동하는데 이유는 그들의 각각에서 meta-annotation처럼 @Component를 가진 annotation으로 구성되어 있기 때문이다. 

 

2-4. ComponentScan

 

@Component 어노테이션 및 stereotype 어노테이션이 부여된 class들을 자동으로 scan하여 Bean으로 등록해주는 어노테이션이다.

 

직접 패키지의 경로를 적어줘서 스캔 위치를 정할 수 있다. 

 

2-5. @Component vs @Bean

 

 

갑자기 왠 @Bean 등록법이 나오지 ? 할 수 있는데 필자는 이해가 잘 안되서 그렇게 했다....(결국 내가 잘 몰라서 ㅎㅎ...)

 

3. 컨테이너에 빈 설정하기(Configuring Beans in the Container)

ApplicationContext의 주된 일은 bean들을 관리하는 것이다. (빈 생성법은 위에서 간단히 봣쥬? ㅎㅎ )

 

어플리케이션은 ApplicationContext container에 빈 설정을 제공해야만 한다. 

 

Spring bean 설정은 하나 혹은 여러개의 빈 정의로 구성된다. 

 

이에 더해, Spring 은 빈들을 설정하는 다양한 방법을 제공한다. 

 

3-1. 자바 기반의 설정

먼저, 빈 설정에서 가장 최신이고 가장 선호되는 자바 기반 설정을 시작할 것이다. 

 

Spring 3.0 이상부터 가능하다. 

 

Java Configuration은 전형적으로 @Bean annotaion 메소드를 @Configuration class와 함께 사용한다. 

 

메소드에서 @Bean annotation은  method가 Spring bean을 생성한다는 것을 나타낸다.

 

더욱이, @Configuration으로 annotated된 클래스는 이것이 Spring bean 설정들을 포함한다는 것을 나타낸다. 

 

이제, Spring bean으로 AccountService의 정의를 위해 configuration class를 생성하자.

 

@Configuration
public class AccountConfig {

  @Bean
  public AccountService accountService() {
    return new AccountService(accountRepository());
  }

  @Bean
  public AccountRepository accountRepository() {
    return new AccountRepository();
  }
}

 

3-2. Annotation 기반 설정

 

Spring 2.5는 java 에서 bean 설정을 가능하게 하는 첫번째 단계로 어노테이션 기반 설정을 소개한다. 

 

이러한 접근으로, 우리는 xml 설정을 통해 annotation 기반 설정을 가능하게 할 수 있다. 

 

그리고 나서 우리 java 클래스, 메소드, 생성자 혹은 필드위에서 빈을 등록하기 위해서 일련의 어노테이션들을 사용한다.

 

이러한 어노테이션들의 몇가지 예는 @Component, @Controller, @Service, @Repository, @Autowired and @Qualifier가 있다. 

 

특히, 우리는 java 기반 설정에서 또한 이러한 annotation들을 사용한다. 또한, 말할 가치가 있는데, Spring 은 계속해서 매 release마다 이러한 어노테이션들의 기능을 더 추가한다. 

 

이제, 이 설정의 예를 보자. 

 

먼저, XML configuration인 user-bean-config.xml을 생성하고 annotation들을 사용 가능하게 한다. 

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">
  
  <context:annotation-config/>
  <context:component-scan base-package="com.baeldung.applicationcontext"/>

</beans>

 

여기서, annotation-config는 annotation 기반 mapping을 가능하게 한다. component-scan tag는 또한 Spring이 annotation을 가진 class들을 어디서 찾아야 하는지 알려준다. 

 

두번째로, UserService 클래스를 생성할 것이고 @Component 어노테이션을 사용하는 Spring bean으로 그것을 정의할 것이다. 

 

@Component
public class UserService {
  // user service code
}

 

그리고 이 설정을 테스트 하기위해 간단한 테스트를 작성할 것이다. 

 

ApplicationContext context = new ClassPathXmlApplicationContext("applicationcontext/user-bean-config.xml");
UserService userService = context.getBean(UserService.class);
assertNotNull(userService);

 

4.3. XML-Based Configuration

 

마지막으로, XML 기반의 설정을 볼 것이다. 이것은 Spring에서 빈을 설정하기 위한 전통적인 방식이다. 

 

명확하게, 이 접근에서, 우리는 XMl 설정 파일을 모두 bean으로 mapping 한다. 

 

그러면 xml 설정 파일을 account-bean-config.xml로 만들어보고 AccountService.class로 빈들을 정의해보자.

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="
    http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans.xsd">
	  
  <bean id="accountService" class="com.baeldung.applicationcontext.AccountService">
    <constructor-arg name="accountRepository" ref="accountRepository" />
  </bean>
	
  <bean id="accountRepository" class="com.baeldung.applicationcontext.AccountRepository" />
</beans>

 

필자는 이런 기능이 있구나 오~ 하고 회사에선 무조건 stereotype annocation만 사용한다. 

이렇게 쓰는데가 아직도 있다면 알려주세요 ^^

 

5. ApplicationContext의 종류

 

Spring은 다양한 요구사항을 위해 ApplicationContext container에 알맞는 다양한 종류의 ApplicationContext를 제공한다. 

 

이러한 것들은 ApplicationContext 인터페이스의 구현체들이다. 

 

ApplicationContext들의 흔한 종류들 몇개를 보자. 

 

5-1. AnnotationConfigApplicationContext

먼저, Spring 3.0에서 도입된 AnnotationConfigApplicationContext 클래스를 보자. 

 

이것은 @Configuration, @Component annotation와 입력으로 JSR-330 metadata를 가진 클래스들이다. 

 

ApplicationContext context = new AnnotationConfigApplicationContext(AccountConfig.class);
AccountService accountService = context.getBean(AccountService.class);

 

5-2. AnnotationConfigWebApplicationContext

AnnotationConfigWebApplicationContext는 AnnotationConfigApplicationContext의  웹 기반 변수이다. 

 

우리는 아마 이 클래스를 ContextLoaderListen servlet listener에 설정할 때나 Spring MVC의 DispatcherServlet을 설정할 때 사용할 것이다. 

 

더욱이, Spring 3.0 이상에서누느 우리는 프로그래밍적으로 이 application context tainer를 설정할 수 있다. 

 

우리가 필요로 하는 것은 WebApplicationInitializer 인터페이스를 구현하는 것이다. 

 

public class MyWebApplicationInitializer implements WebApplicationInitializer {

  public void onStartup(ServletContext container) throws ServletException {
    AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
    context.register(AccountConfig.class);
    context.setServletContext(container);

    // servlet configuration
  }
}

 

5-3. XmlWebApplicationContext

만약 XML 기반의 설정을 web application에서 사용한다면, 우리는 XmlWebApplicationContext 클래스를 사용할 수 있다. 

 

실제로, 이 컨테이너를 설정하는 것은 AnnotationCOnfigWebApplicationContext 클래스랑 같은데, 이것은 web.xml에서 설정할 수있다는 것을 의미한다. 혹은 WebApplicationInitializr 인터페이스를 구현할 수 있다는 것을 의미한다.

 

public class MyXmlWebApplicationInitializer implements WebApplicationInitializer {

  public void onStartup(ServletContext container) throws ServletException {
    XmlWebApplicationContext context = new XmlWebApplicationContext();
    context.setConfigLocation("/WEB-INF/spring/applicationContext.xml");
    context.setServletContext(container);

    // Servlet configuration
  }
}

 

5-4. FileSystemXMLApplicationContext

우리는 파일 시스템이나 URL들로부터 xml 기반의 Spring 설정을 가져오기 위해 FileSystemXMLApplicationContext를 이용한다. 

 

이 클래스는 우리가 프로그래밍 적으로 ApplicationContext를 사용해야 할 필요가 있을 때 유용하다.

 

일반적으로, 테스트 하네스와 standalone 어플리케이션은 이것을 위해 사용 가능한 몇개이다. 

 

예를 들어, XML 기반 설정으로 어떻게 우리가 spring container를 만들고 bean을 불러오는지 보자. 

 

String path = "C:/myProject/src/main/resources/applicationcontext/account-bean-config.xml";

ApplicationContext context = new FileSystemXmlApplicationContext(path);
AccountService accountService = context.getBean("accountService", AccountService.class);

 

6. ApplicationContext의 추가적인 특징(Additional Features of ApplicationContext)

 

6-1. 메시지 처리(Message Resolution)

ApplicationContext 인터페이스는 message resolution을 지원한다. 긜고 MessageSource 인터페이스를 상속해서 국제화를 지원한다. 

 

더욱이, Spring 은 두개의 MessageSource 구현체를 제공하는데, ResourceBuldneMessageSource과 StaticMessageSource이다. 

 

우리는 StaticMessageSource를 프로그래밍적으로 source 에 메시지를 추가하기 위해 사용한다. 

하지만, 이것은 기본 국제화를 지원하고, production 사용보다 테스트에 더 적합하다. 

 

반면에, ResourceBundleMessageSource는 MessageSource의 가장 흔한 구현체이다. 

 

이것은 JDK의 ResourceBundle 구현체에 의존한다. 또한 MessageFormat이 제공하는 JDK의 표준 메시지 파싱을 사용한다. 

 

이제, 프로퍼티 파일로부터 메시지를 읽는 MessageSource를 어떻게 사용하는지  알아보자.

 

먼저, messages.properties파일을 classpath에 생성한다. 

 

account.name=TestAccount

 

두번째로, 우리는 AccountConfig 클래스를 빈 정의에 추가할 것이다. 

 

@Bean
public MessageSource messageSource() {
  ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
  messageSource.setBasename("config/messages");
  return messageSource;
}

 

세번째로, AccountService에 MessageSource를 주입할 것이다. 

 

@Autowired
private MessageSource messageSource;

 

마지막으로, message를 읽기 위해 AccountService에  어디든지 있는 getMessage 메소드를 사용할 수 있다. 

 

messageSource.getMessage("account.name", null, Locale.ENGLISH);

Spring은 또한 ReloadableResourceBundleMessageSource 클래스를 제공하는데, 이것은 모든 스프링 resource 위치에서 파일을 읽도록 해준다. 그리고 bundle property 파일들의 hot reloading 을 지원한다. 

 

6-2. Event Handling

ApplicationContext는 ApplicationEvent 클래스와 ApplicationListener 인터페이스의 도움으로 이벤트 핸들링을 지원한다. 

 

이것은 ContextStartedEvent, ContextStoppedEvent, ContextClosedEvent 그리고 RequestHandledEvent같은 built-in 이벤트를 지원한다. 

 

더욱이, 비즈니스 사용의 경우에 custom events들을 지원한다. 

 


이번시간에는 Spring Container, BeanFactory, ApplicationContext에 대해 공부했다.

 

절반 이상이 ApplicationContext와 연과된 이야기긴 했는데 그런것이 있구나 했다. 

 

다음 시간에는 Spring Event에 대해 알아볼 것이다.