부트 개념과 활용-03.자동 설정 이해/만들기(Starter와 AutoConfigure/@Configuration)


자동 설정 이해

@EnableAutoConfiguration

  • @SpringBootApplication 안에 @EnableAutoConfiguration이 들어있다. (@ComponentScan도 마찬가지.. 이미 앞에서 정리했으니 생략)

  • 빈은 사실 두 단계로 나눠서 읽힌다.
    • 1단계: @ComponentScan
    • 2단계: @EnableAutoConfiguration
  • @ComponentScan
    • @Component
    • @Configuration @Repository @Service @Controller @RestController (컴포넌트 어노테이션 기반)
  • @EnableAutoConfiguration = org.springframework.boot.autoconfigure.EnableAutoConfiguration
    • spring.factories (설정파일 등..)
    • @Configuration (클래스로 만든 설정빈 등..)
    • @ConditionalOnXxxYyyZzz
//@SpringBootConfiguration
//@ComponentScan // (1) 컴포넌트 스캔으로 등록한 다음
//@EnableAutoConfiguration // (2) 추가적인 빈들을 읽어서 빈들을 등록

@Configuration // 컴포넌트 어노테이션 기반 
@ComponentScan // @EnableAutoConfiguration 없어도 실행 잘됨
public class Application {

    public static void main(String[] args){
        // 웹서버 안띄우고 실행
        SpringApplication application = new SpringApplication(Application.class);
        application.setWebApplicationType(WebApplicationType.NONE);
        application.run(args);

        //SpringApplication.run(Application.class, args);
    }
}

그 빈들은 외부 라이브러리(External Libraries)중 spring-boot-autoconfigure에 정의되어 있다.

EnableAutoConfiguration

  • spring.factories 해당 설정파일 안에는 이런 식으로
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnClassCondition

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguratio
...

key=value, value2… 타입으로
자동 설정되어서 생성될 빈들이 미리 정의가 되어 있다. (# Auto Configure)

이걸 @EnableAutoConfiguration 가 여기에 미리 정의된 빈들을 가져와서 등록해준다.

자동 설정 만들기 1부 : Starter와 AutoConfigure

  • Xxx-Spring-Boot-Autoconfigure 모듈: 자동 설정
  • Xxx-Spring-Boot-Starter 모듈: 필요한 의존성 정의
  • 그냥 하나로 만들고 싶을 때는?
    • Xxx-Spring-Boot-Starter 하나에 다 넣어버려도 된다.

구현 방법

  1. 의존성 추가
  2. @Configuration 파일 작성
  3. src/main/resource/META-INF에 spring.factories 파일 만들기
  4. spring.factories 안에 자동 설정 파일 추가
  5. mvn install

신규 프로젝트 pom.xml

  1. 의존성 추가
    ~~~xml
4.0.0 me.jaeuk jaeuk-spring-boot-starter 1.0-SNAPSHOT org.springframework.boot spring-boot-autoconfigure org.springframework.boot spring-boot-autoconfigure-processor true org.springframework.boot spring-boot-dependencies 2.0.3.RELEASE pom import

~~~java
public class Holeman {

    String name;
    int howLong;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getHowLong() {
        return howLong;
    }

    public void setHowLong(int howLong) {
        this.howLong = howLong;
    }

    @Override
    public String toString() {
        return "Holeman{" +
                "name='" + name + '\'' +
                ", howLong=" + howLong +
                '}';
    }
}
  1. @Configuration 파일 작성
    // 홀로맨 빈을 리턴하는 설정파일
    @Configuration
    public class HolemanConfiguration {
    
     @Bean
     public Holeman holeman(){
         Holeman holeman = new Holeman();
         holeman.setHowLong(5);
         holeman.setName("jaeuk");
         return holeman;
     }
    }
    
  2. src/main/resource/META-INF에 spring.factories 파일 만들기

resuouces 에 META-INF 디렉토리 생성
spring.facotries 파일 생성 (이건 부트에 특화된게 아니라 스프링에서 있는 것, 스프링의 라이프사이클에 활용되거나 등)

  1. spring.factories 안에 자동 설정 파일 추가
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  me.jaeuk.HolemanConfiguration
<!-- EnableAutoConfiguration 켜져있으면, 키에 해당하는 값들을 쭉 읽게 될 것이고, 해당 빈 설정을 보고 쓴다.(me.jaeuk.HolemanConfiguration) -->
  1. mvn install

maven install 로 jar 파일을 생성하고
본래 프로젝트로 돌아가서
pom.xml 에 의존성 추가

        <dependency>
            <groupId>me.jaeuk</groupId>
                <artifactId>jaeuk-spring-boot-starter</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

[캡처]

@Component
public class HolomanRunner implements ApplicationRunner {

    @Autowired
    Holeman holeman;

    @Override
    public void run(ApplicationArguments args) throws Exception{
        System.out.println(holeman);
    }

}

실행하면

Holeman{name='jaeuk', howLong=5}  

출력되는 것을 확인할 수 있다.

그렇다면

@SpringBootApplication
public class Application {

    public static void main(String[] args){
        // 웹서버 안띄우고 실행
        SpringApplication application = new SpringApplication(Application.class);
        application.setWebApplicationType(WebApplicationType.NONE);
        application.run(args);

        //SpringApplication.run(Application.class, args);
    }

    // 직접 빈으로 선언(컴포넌트 스캔과 순서 동일) / (1) 먼저 컴포넌트 스캔으로 빈 생성 -> (2) 추후 기타 빈 생성 (즉, 이건 덮어쓰인다)
    @Bean
    public Holeman holeman() {
        Holeman holeman = new Holeman();
        holeman.setName("suhun");
        holeman.setHowLong(30);
        return holeman;
    }
}

이 경우에는 어떻게 출력이 될까?

Holeman{name='jaeuk', howLong=5}

로 동일하다. 왜냐햐면 앞서 말했듯,

  1. 컴포넌트 스캔으로 빈을 등록 한 다음
  2. 오토컨피규레이션으로 빈을 등록한다.

그런데 우리는 직접 @Bean 으로 생성이 되는게 더 우선시 되어야 한다.(1.이 더 우선되어야한다.)

그럼 어떤 해결 방법이 있을까?

자동 설정 만들기 2부 : @ConfigurationProperties

  • 덮어쓰기 방지하기
    • @ConditionalOnMissingBean
  • 빈 재정의 수고 덜기
    • @ConfigurationProperties(“holoman”)
    • @EnableConfigurationProperties(HolomanProperties)
    • 프로퍼티 키값 자동 완성

@ConditionalOnMissingBean를 활용하면 된다.

아까 설정파일을 jar로 배포한 그 프로젝트에서 이렇게 수정해주면 된다.

// 홀로맨 빈을 리턴하는 설정파일
@Configuration
public class HolemanConfiguration {

    @Bean
    @ConditionalOnMissingBean // 빈이 없는 것만 등록. (내가 직접 빈 선해 놓은거 있으면 등록안함.=덮어쓰지 않음)
    public Holeman holeman(){
        Holeman holeman = new Holeman();
        holeman.setHowLong(5);
        holeman.setName("jaeuk");
        return holeman;
    }
}

그럼 본 프로젝트로 돌아가서 돌려보면

@SpringBootApplication
public class Application {

    public static void main(String[] args){
        // 웹서버 안띄우고 실행
        SpringApplication application = new SpringApplication(Application.class);
        application.setWebApplicationType(WebApplicationType.NONE);
        application.run(args);
    }

    // (1) 먼저 컴포넌트 스캔으로 빈 생성 -> (2) 추후 기타 빈 생성 (즉, 이건 덮어인다)
    // 설정(컨피규레이션)파일에 @ConditionalOnMissingBean 붙어놓음.
    @Bean
    public Holeman holeman() {
        Holeman holeman = new Holeman();
        holeman.setName("suhun");
        holeman.setHowLong(30);
        return holeman;
    }
}
Holeman{name='suhun', howLong=30}

직접 생성하는 빈으로 적용 된 것을 볼 수 있다.

그렇다면 난 본 프로젝트에서 그냥 저렇게 빈을 새로 생성하는 것 보다,
그냥 프로퍼티에 값만 바꾸어서 사용하고 싶다면?
다른 방법이 있다.

본 프로젝트에서 application.properties를 만들고 사용하는 방법이다.

ex.
holeman.name = 꾸준히 공부를 해야죠.
holeman.how-long = 27

jar 배포한 프로젝트

spring.factorites

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  me.jaeuk.HolomanConfiguration 

HolomanConfiguration를 오토 컨피그레이션(AutoConfiguration)해 주고,

// 홀로맨 빈을 리턴하는 설정파일
@Configuration
@EnableConfigurationProperties(HolomanProperties.class)
public class HolomanConfiguration {

    @Bean
    @ConditionalOnMissingBean // 빈이 없는 것만 등록. (내가 직접 빈 선해 놓은거 있으면 등록안함.=덮어쓰지 않음)
    public Holoman holoman(HolomanProperties properties){
        Holoman holoman = new Holoman();
        holoman.setHowLong(properties.getHowLong());
        holoman.setName(properties.getName());
        return holoman;
    }
}

@EnableConfigurationProperties 로 HolomanProperties.class 프로퍼티 설정파일을 컨피그레이션 해 준다.

@ConfigurationProperties("holoman") 
public class HolomanProperties {

    private  String name;
    private int howLong;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getHowLong() {
        return howLong;
    }

    public void setHowLong(int howLong) {
        this.howLong = howLong;
    }
}

@ConfigurationProperties(“holoman”)를 통해 본 프로젝트에서 설정하는 프로퍼티 파일에 holoman.* 을 참조해서 가져오고, 그렇게 가져간 프로퍼티를 HolomanConfiguration에서 세팅이 된다.

그럼 본 프로젝트에서는

@Component
public class HolomanRunner implements ApplicationRunner {

    @Autowired
    Holoman holoman;

    @Override
    public void run(ApplicationArguments args) throws Exception{
        System.out.println(holoman);
    }
}

설정은 프로퍼티 파일에서 하고, 주입만 받아서 사용하면 된다.

  • 참고 프로퍼티(@ConfigurationProperties 에 해당하는 프로퍼티에 대한) 자동완성 지원을 하려면 선언해주어야 함.
    ~~~xml
org.springframework.boot spring-boot-configuration-processor true

~~~

이걸 의존받지 않으면

holoman.name = 꾸준히 공부를 해야죠.
holeman.

여기서 바로 밑에 리스트가 뜨지 않는다.(자동완성)

@Conditional~~ 굉장히 다양하다
@ConditionalOnMissingBean.. 등등
차차 활용 파트 가면서 정리 예정




© 2019. by jaeuk