ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 애플리케이션 컨텍스트와 빈팩토리
    🌱 spring 2022. 11. 16. 11:23

    ❓애플리케이션 컨텍스트 (Application Context)


    애플리케이션 컨텍스트는 빈들의 생성과 의존성 주입 등의 역할을 하는 일종의 DI 컨테이너이다.

    SpringBoot를 이용한다면 애플리케이션 종류에 따라 각기 다른 종류의 ApplicationContext가 내부에서 만들어진다.

    • 웹 애플리케이션이 아닌 경우
      • 애플리케이션 컨텍스트 : AnnotationConfigApplication
      • 웹 서버 : ❌
    • 서블릿 기반의 웹 애플리케이션인 경우
      • 애플리케이션 컨텍스트 : AnnotationConfigServletWebServerApplicationContext
      • 웹 서버 : Tomcat
    • 리액티브 웹 애플리케이션인 경우
      • 애플리케이션 컨텍스트 : AnnotationConfigReactiveWebServerApplicationContext
      • 웹 서버 : Reactor Netty

    일반적인 ApplicationContext 관련 클래스들은 spring-context 프로젝트에 존재한다.

    • spring-core
    • spring-bean
    • spring-aop

    를 추가하면 같이 불러와진다.

     

    AnnotationConfigServletWebServerApplicationContext 나 AnnotationConfigReactiveWebServerApplicationContext는

    Springboot에서 추가된 클래스이므로

    spring-boot-starter-web 또는 spring-boot-starter-webflux 같은 spring-boot 의존성을 추가해야 한다.

     

    기존 Spring 프로젝트와 달리 SpringBoot는 내장 웹서버를 가지고 있다.

    • 따라서, 타입에 맞는 웹서버를 만들고 애플리케이션 실행과 함께 내장 웹서버가 시작된다.

     

    DI 컨테이너와 애플리케이션 컨텍스트

    애플리케이션 컨텍스트는 애플리케이션을 실행하기 위한 환경이다.

    • 그럼에도 애플리케이션 컨텍스트가 DI 컨테이너라고 불리고 그런 역할을 할 수 있는 이유는 ApplicationContext 상위에 빈들을 생성하는 BeanFactory 인터페이스를 부모로 상속받고 있기 때문이다.

     

    ❗ 빈팩토리(BeanFactory)


    BeanFactory는 애플리케이션 컨텍스트의 최상위 인터페이스 중 하나이며, 다음과 같이 1개의 빈을 찾기 위한 메소드들을 갖고 있다.

    • 빈을 등록하고 생성하고 조회하고 돌려주는 등 빈을 관리하는 역할을 한다.

    BeanFactory는 애플리케이션 컨텍스트의 최상위 인터페이스 중 하나이다.

    • 1개의 빈을 찾기 위한 메소드들을 갖고 있다.
    • getBean() 메소드를 통해 빈을 인스턴스화할 수 있다.

    BeanFactory 인터페이스

    public interface BeanFactory {
    
    	String FACTORY_BEAN_PREFIX = "&";
    
    	Object getBean(String name) throws BeansException;
    
    	<T> T getBean(String name, Class<T> requiredType) throws BeansException;
    
    	Object getBean(String name, Object... args) throws BeansException;
    
    	<T> T getBean(Class<T> requiredType) throws BeansException;
    
    	<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
    
    	<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
    
    	<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
    
    	boolean containsBean(String name);
    
    	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
    
    	boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
    
    	boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
    
    	boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
    
    	@Nullable
    	Class<?> getType(String name) throws NoSuchBeanDefinitionException;
    
    	@Nullable
    	Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;
    
    	String[] getAliases(String name);
    }

    스프링은 동일한 타입의 빈이 여러개 존재할때에도 List로 빈을 찾아서 주입해준다.

    • 최상위 BeanFactory는 단일 빈을 처리하기 위한 퍼블릭 인터페이스를 갖고 있지만
    • ListableBeanFactory는 빈 리스트를 처리하기 위한 퍼블릭 인터페이스를 갖는다.
    • HierarchicalBeanFactory는 여러 BeanFactory들 간의 계층 (부모-자식) 관계를 설정하기 위한 퍼블릭 인터페이스를 갖는다.

     

    ListableBeanFactory

    public interface ListableBeanFactory extends BeanFactory {
    
    	boolean containsBeanDefinition(String beanName);
    
    	int getBeanDefinitionCount();
    
    	String[] getBeanDefinitionNames();
    
    	<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType, boolean allowEagerInit);
    
    	<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType, boolean allowEagerInit);
    
    	String[] getBeanNamesForType(ResolvableType type);
    
    	String[] getBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit);
    
    	String[] getBeanNamesForType(@Nullable Class<?> type);
    
    	String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit);
    
    	<T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException;
    
    	<T> Map<String, T> getBeansOfType(@Nullable Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
    			throws BeansException;
    
    	String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType);
    
    	Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException;
    
    	@Nullable
    	<A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType)
    			throws NoSuchBeanDefinitionException;
    
    	@Nullable
    	<A extends Annotation> A findAnnotationOnBean(
    			String beanName, Class<A> annotationType, boolean allowFactoryBeanInit)
    			throws NoSuchBeanDefinitionException;
    }

     

    HierarchicalBeanFactory

    public interface HierarchicalBeanFactory extends BeanFactory {
    
    	/**
    	 * Return the parent bean factory, or {@code null} if there is none.
    	 */
    	@Nullable
    	BeanFactory getParentBeanFactory();
    
    	boolean containsLocalBean(String name);
    
    }
    

    ApplicationContext는 BeanFactory를 바로 상속받는 것이 아니라 BeanFactory의 자식 인터페이스인 ListableBeanFactory 와 HierarchicalBeanFactory를 통해 상속받는다.

    • 이를 통해 애플리케이션 컨텍스트는 단일 빈 외에도 다양하게 처리할 수 있다.

     

    AutowireCapableBeanFactory

    • @Autowired 처리를 위한 BeanFactory

     

    ApplicationContext 코드는 AutowireCapableBeanFactory 를 상속받지 않는다.

    • 그럼 어떻게 @Autowired 를 처리할 수 있나
    public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
    		MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
    
    	@Nullable
    	String getId();
    
    	String getApplicationName();
    
    	String getDisplayName();
    
    	long getStartupDate();
    
    	@Nullable
    	ApplicationContext getParent();
    
    	AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
    }

    ❗ 애플리케이션 컨텍스트에는 @Autowired를 처리하기 위한 AutowireCapableBeanFactory를 합성(Has-A) 관계로 가지고 있기 때문.

    • 따라서 AutowireCapableBeanFactory를 반환하는 퍼블릭 인터페이스를 가지고 있다.

    ⇒ Spring의 애플리케이션 컨텍스트는

    • BeanFactory
    • ListableBeanFactory
    • HierarchicalBeanFactory

    를 상속받아 애플리케이션 컨텍스트를 통해 빈을 찾을 수 있다.

     

    하지만, 스프링의 빈들은 실제로 애플리케이션 컨텍스트에서 관리되는 것은 아니다.

    • ApplicationContext 하위 다양한 구현체들이 존재
    • 일반적으로 스프링부트가 만들어내는 3가지 애플리케이션 컨텍스트는 모두 GenericApplicationContext 라는 애플리케이션 컨텍스트를 부모로 가지고 있다.

    GenericApplicationContext

    public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
    
    	private final DefaultListableBeanFactory beanFactory;
    
    	@Nullable
    	private ResourceLoader resourceLoader;
    
    	private boolean customClassLoader = false;
    
    	private final AtomicBoolean refreshed = new AtomicBoolean();
    
      // 생성자
    	public GenericApplicationContext() {
    		this.beanFactory = new DefaultListableBeanFactory(); 
    	}
    
    	public GenericApplicationContext(DefaultListableBeanFactory beanFactory) {
    		Assert.notNull(beanFactory, "BeanFactory must not be null");
    		this.beanFactory = beanFactory;
    	}
    
    	public GenericApplicationContext(@Nullable ApplicationContext parent) {
    		this();
    		setParent(parent);
    	}
    
    	public GenericApplicationContext(DefaultListableBeanFactory beanFactory, ApplicationContext parent) {
    		this(beanFactory);
    		setParent(parent);
    	}
    
    	@Override
    	public void setParent(@Nullable ApplicationContext parent) {
    		super.setParent(parent);
    		this.beanFactory.setParentBeanFactory(getInternalParentBeanFactory());
    	}
    
    	@Override
    	public void setApplicationStartup(ApplicationStartup applicationStartup) {
    		super.setApplicationStartup(applicationStartup);
    		this.beanFactory.setApplicationStartup(applicationStartup);
    	}
    	...
    }
    • 클래스의 생성자를 보면 내부에서 진짜 빈들을 등록하여 관리하고 찾아주는 DefaultListableBeanFactory 를 생성한다.

    즉, 애플리케이션 컨텍스트는 빈들을 관리하는 BeanFactory 구현체인 DefaultListableBeanFactory를 합성(Has-A) 관계로 내부에 가지고 있다.

    • 애플리케이션 컨텍스트에 빈을 등록하거나 찾아달라는 빈 처리 요청이 오면 beanFactory로 이러한 요청을 위임 하여 처리한다.
    • 애플리케이션 컨텍스트가 @Autowired를 처리해주는 빈 팩토리를 반환하는 getAutowireCapableBeanFactory를 퍼블릭 인터페이스로 가지고 있는데, 해당 메소드를 호출하면 반환되는 빈 팩토리가 DefaultListableBeanFactory 이다.

     

    DefaultListableBeanFactory 는 상위에

    • @Autowired 처리를 위한 인터페이스인 AutowireCapableBeanFactory와
    • 그에 대한 추상 클래스인 AbstractAutowireCapableBeanFactory 를 상속받고 있다.

    → 애플리케이션 컨텍스트로 getAutowireCapableBeanFactory를 요청하면 AutowireCapableBeanFactory 타입으로 추상화된 DefaultListableBeanFactory 구현체 객체를 반환받는다.

     

    ConfigurableApplicationContext

    💡 ConfigurableApplicationContext 는 거의 모든 애플리케이션 컨텍스트가 갖는 공통 애플리케이션 컨텍스트 인터페이스이다.
    • ApplicationContext, Lifecycle, Closable 인터페이스를 상속받는다.
    • 애플리케이션 컨텍스트가 시작되고 종료될 때 사용되는 메소드들을 가진다.
    • 처음 본 3가지 애플리케이션 컨텍스트 모두 ConfigurableApplicationContext 인터페이스를 직접 구현한다.

    스프링부트 애플리케이션을 실행하는 run 메소드 호출시 받는 반환 타입이 ConfigurableApplicationContext이다.

    ConfigurableApplicationContext는 Closable 인터페이스를 상속받고 있다.

    • 애플리케이션을 실행하고 종료하고 반복해야 하는 경우 try-with-resources 를 사용하면 작업을 조금 편리하게 만들 수 있다.

     

    @SpringBootApplication(proxyBeanMethods = false)
    public class TestApplication {
    
        public static void main(String[] args) {
            try (ConfigurableApplicationContext ctx = SpringApplication.run(TestApplication.class,args)) {
    
            } catch (Exception e) {
    
            }
        }
    }
    

    ⇒ run() 호출 후 try-with-resources 로 인해 자동으로 close가 호출된다.

    • 따라서, 매번 끄고 재시작하지 않고 1번 실행 후에 자동 종료되도록 한다.

     

    실제 close 메소드 - AbstractApplicationContext에 구현되어 있다.

    @Override
    public void close() {
        synchronized (this.startupShutdownMonitor) {
            doClose();
            // If we registered a JVM shutdown hook, we don't need it anymore now:
            // We've already explicitly closed the context.
            if (this.shutdownHook != null) {
                try {
                    Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
                }
                catch (IllegalStateException ex) {
                    // ignore - VM is already shutting down
                }
            }
        }
    }
    

     

    📌 정리


    ApplicationContext는 BeanFactory의 기능을 상속받는다.

    • ApplicationContext는 빈 관리기능 + 기타 기능(MessageSource, EnvironmentCapable 등)을 제공

    BeanFactory - 단일 빈을 처리

    ListableBeanFactory - 여러 빈을 처리

    HierarchicalBeanFactory - 계층 관계를 처리

    • 이를 분리하여 인터페이스를 나누고 있다.

     

    참고 자료


    https://mangkyu.tistory.com/210

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

    https://catsbi.oopy.io/0f28d626-febb-421d-91c8-5d44c6df7d1f

     

    '🌱 spring' 카테고리의 다른 글

    JDBC ❓  (0) 2023.01.13
    Spring Interceptor에서 Request 데이터 처리  (1) 2022.12.06
    JPA - @ElementCollection  (0) 2022.11.02
    JPA - Embedded Type  (0) 2022.11.02
    Spring Scheduler  (0) 2022.10.24

    댓글

Designed by Tistory.