ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Spring Boot 핵심 기능 3 - 자동 구성(Auto Configuration)
    BackEnd/Spring Boot 2023. 4. 20. 07:00
    반응형

    스프링 부트의 자동 구성

      스프링 부트는 자동 구성(Auto Configuration) 기능을 제공합니다. 일반적으로 자주 사용하는 수 많은 빈들을 자동으로 등록해주는 기능입니다. JdbcTemplate, DataSource, TransactionManager 등 스프링 부트가 자동 구성을 제공해서 자동으로 스프링 빈으로 등록됩니다.

     

      스프링 부트 자동 구성이 동작하는 원리는 다음 순서로 확인할 수 있습니다.

    1. @SpringBootApplication
    2. @EnableAutoConfiguration
    3. @Import(AutoConfigurationImportSelector.class)
    4. resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 파일의 설정 정보가 스프링 컨테이너에 등록되고 사용

     

      스프링 부트는 spring-boot-autoconfiguration 프로젝트 안에서 수 많은 자동 구성을 제공합니다. JdbcTemplate을 설정하고 빈으로 등록해주는 자동 구성은 다음과 같습니다.

    package org.springframework.boot.autoconfigure.jdbc;
    
    import javax.sql.DataSource;
    
    import org.springframework.boot.autoconfigure.AutoConfiguration;
    import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.boot.sql.init.dependency.DatabaseInitializationDependencyConfigurer;
    import org.springframework.context.annotation.Import;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
    
    @AutoConfiguration(after = DataSourceAutoConfiguration.class)
    @ConditionalOnClass({ DataSource.class, JdbcTemplate.class })
    @ConditionalOnSingleCandidate(DataSource.class)
    @EnableConfigurationProperties(JdbcProperties.class)
    @Import({ DatabaseInitializationDependencyConfigurer.class, JdbcTemplateConfiguration.class,
    		NamedParameterJdbcTemplateConfiguration.class })
    public class JdbcTemplateAutoConfiguration {
    
    }
    • @AutoConfiguration: 자동 구성을 사용하려면 이 애노테이션을 등록해야 합니다.
    • after = DataSourceAutoConfiguration.class: 자동 구성이 실행되는 순서를 지정할 수 있습니다. JdbcTemplate은 DataSource가 필요하기 때문에 DataSource를 자동으로 등록해주는 DataSourceAutoConfigration 다음에 실행하도록 설정되어 있습니다.
    • @ConditionalOnClass({ DataSource.class, JdbcTemplate.class }): IF문과 유사한 기능을 제공합니다. DataSource, JdbcTemplate 클래스가 있는 경우에만 설정이 동작합니다. 만약 없으면 여기 있는 설정들이 모두 무효화 되고, 빈도 등록되지 않습니다.
    • @Import: 스프링에서 자바 설정을 추가할 때 사용합니다.

     

      @Import의 대상이 되는 JdbcTemplateConfiguration은 다음과 같습니다.

    package org.springframework.boot.autoconfigure.jdbc;
    
    import javax.sql.DataSource;
    
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    import org.springframework.jdbc.core.JdbcOperations;
    import org.springframework.jdbc.core.JdbcTemplate;
    
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnMissingBean(JdbcOperations.class)
    class JdbcTemplateConfiguration {
    
    	@Bean
    	@Primary
    	JdbcTemplate jdbcTemplate(DataSource dataSource, JdbcProperties properties) {
    		JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    		JdbcProperties.Template template = properties.getTemplate();
    		jdbcTemplate.setFetchSize(template.getFetchSize());
    		jdbcTemplate.setMaxRows(template.getMaxRows());
    		if (template.getQueryTimeout() != null) {
    			jdbcTemplate.setQueryTimeout((int) template.getQueryTimeout().getSeconds());
    		}
    		return jdbcTemplate;
    	}
    
    }
    • @Configuration: 자바 설정 파일로 사용됩니다.
    • @ConditionalOnMissingBean(JdbcOperations.class): JdbcOperation 빈이 없을 때 동작합니다. JdbcOperations는 JdbcTemplate의 부모 인터페이스입니다. 즉, JdbcTemplate가 빈으로 등록되어 있지 않은 경우에만 동작합니다. 만약 이런 기능이 없으면 내가 등록한 JdbcTemplate과 자동 구성이 등록하는 JdbcTemplate가 중복 등록되는 문제가 발생할 수 있습니다. 개발자가 직접 빈을 등록하면 개발자가 등록한 빈을 사용하고, 자동 구성은 동작하지 않습니다.

     

    자동 등록 설정

      다음과 같은 자동 구성 기능들이 다음 빈들을 등록해줍니다.

    • JdbcTemplateAutoConfiguration : JdbcTemplate
    • DataSourceAutoConfiguration : DataSource
    • DataSourceTransactionManagerAutoConfiguration : TransactionManager

     

    스프링 부트가 제공하는 자동 구성(AutoConfiguration)

      스프링 부트는 수 많은 자동 구성을 제공하고 spring-boot-autoconfiguration에 자동 구성을 모아둡니다. 스프링 부트 프로젝트를 사용하면 spring-boot-autoconfigure 라이브러리는 기본적으로 사용됩니다.

     

    @Conditional

      같은 소스 코드인데 특정 상황일 때(예: 개발 환경에서는 Swagger를 사용하고 운영 환경에서는 Swagger를 사용하지 않을 때)만 특정 빈들을 등록해서 사용하도록 도와주는 기능입니다. 스프링 부트 자동 구성에서 자주 사용합니다. 이 기능을 사용하려면 Condition 인터페이스를 구현해야 합니다.

    @FunctionalInterface
    public interface Condition {
    
    	/**
    	 * Determine if the condition matches.
    	 * @param context the condition context
    	 * @param metadata the metadata of the {@link org.springframework.core.type.AnnotationMetadata class}
    	 * or {@link org.springframework.core.type.MethodMetadata method} being checked
    	 * @return {@code true} if the condition matches and the component can be registered,
    	 * or {@code false} to veto the annotated component's registration
    	 */
    	boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
    
    }
    • matches(): true를 반환하면 조건에 만족해서 동작하고, false를 반환하면 동작하지 않습니다.
    • ConditionContext: 스프링 컨테이너, 환경 정보 등을 담고 있습니다.
    • AnnotatedTypeMetadata: 애노테이션 메타 정보를 담고 있습니다.

     

      다음은 환경 정보가 memory=on이라고 되어 있을 때만 동작하는 샘플 코드입니다.

    package memory;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.context.annotation.Condition;
    import org.springframework.context.annotation.ConditionContext;
    import org.springframework.core.type.AnnotatedTypeMetadata;
    
    @Slf4j
    public class MemoryCondition implements Condition {
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            //-Dmemory=on
            String memory = context.getEnvironment().getProperty("memory");
            log.info("memory={}", memory);
            return "on".equals(memory);
        }
    }
    package hello.config;
    
    import memory.MemoryCondition;
    import memory.MemoryController;
    import memory.MemoryFinder;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Conditional;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    @Conditional(MemoryCondition.class)
    public class MemoryConfig {
    
        @Bean
        public MemoryController memoryController() {
            return new MemoryController(memoryFinder());
        }
    
        @Bean
        public MemoryFinder memoryFinder() {
            return new MemoryFinder();
        }
    
    }

     

      스프링은 대부분의 필요한 Condition 인터페이스 구현체를 만들어 제공합니다. 위의 코드는 아래와 같이 사용할 수 있습니다. 별도의 MemoryCondition.class 를 구현할 필요 없이 @ConditionalOnProperty을 사용하면 됩니다.

    @Configuration
    //@Conditional(MemoryCondition.class)
    @ConditionalOnProperty(name = "memory", havingValue = "on") //추가
    public class MemoryConfig {
        ... 생략 ...
    }
    • @ConditionalOnClass , @ConditionalOnMissingClass: 클래스가 있는 경우 동작한다. 나머지는 그 반대
    • @ConditionalOnBean , @ConditionalOnMissingBean: 빈이 등록되어 있는 경우 동작한다. 나머지는 그 반대
    • @ConditionalOnProperty: 환경 정보가 있는 경우 동작한다.
    • @ConditionalOnResource: 리소스가 있는 경우 동작한다.
    • @ConditionalOnWebApplication , @ConditionalOnNotWebApplication: 웹 애플리케이션인 경우 동작한다.
    • @ConditionalOnExpression: SpEL 표현식에 만족하는 경우 동작한다.

    Condition Annotations 공식 메뉴얼

     

    [참고정보]

    스프링 부트 - 핵심 원리와 활용

    반응형

    댓글

Designed by Tistory.