[SpringBoot] Spring 에서 자동설정의 이해와 구현 (AutoConfiguration)

2020. 5. 3. 11:39개발/Spring

😀 Spring 자동 설정의 이해

본 포스팅은 백기선님의 inflearn 강의인 "스프링 부트 개념과 활용" 을 정리한 내용입니다.

@SpringBootApplication

@SpringBootConfiguration @ComponentScan @EnableAutoConfiguration 을 내포하고 있다.

🥕@SpringBootConfiguration

@SpringBootConfiguration Document

@SpringBootConfiguration is a class-level annotation that is part of the Spring Boot framework. It indicates that a class provides application configuration.
Document에서 보면 @SpringBootConfiguration은 Application Class에 사용되고 , 클래스에 @Bean이 선언되어 있는 Spring Container 인 것을 나타낸다. 라고 나와있다.

🤔Bean 은 두 단계로 나눠서 읽힌다

🥕@ComponentScan

Bean을 Scan 하는 Annotation. @Component, @Configuration @Repository @Service @Controller @RestController 라는 Annotation을 가진 Class들을 스캔해서 Bean에 등록. @Filter 를 추가해서 @ComponentScan 에 의해서 Bean에 등록되는 일을 막을 수 있음.

😬 주의 !

@ComponentScan 이 정의된 Class와 같은 혹은 하위 패키지에 있는 것들만 Bean에 등록한다.

Example

  • com.example.ddingg 안에 Application.java 가 있다. 이 클래스에는 @ComponentScan이 선언되어 있다.
  • com.example.choi 안에는 @Component 가 정의된 Category.java 파일이 있다.
  • @ComponentScan 이 선언된 Application.java 부터 Bean을 읽기 시작하기 때문에 Category.javaBean에 등록 될 수 없다.

🥕@EnableAutoConfiguration

여러가지 설정이 읽히면서 Application이 동작하는데 그렇게 할 수 있는 이유가 @EnableAutoConfiguration 때문이다.

Bean을 Scan하는 Annotation. @ComponentScanBean을 읽어드린 다음에 추가적으로 Bean들을 등록한다.

image

META-INF 내부의 spring-factories 라는 파일이 있다. 파일 안의 Key 값들 중에 org.springframework.boot.autoconfigure.EnableAutoConfiguration 라는 Key가 있는데 여기안에 정의된 모든 Class(뜯어보면 모든 클래스에 @Configuration 이 선언되어 있다.)들이 AutoConfiguration된다.

😀 자동 설정 구현

xxx-Spring-Boot-Autoconfigure 모듈 : 자동설정

xxx-Spring-Boot-Starter 모듈 : 필요한 의존성 정의

build.gradle 추가
  • org.springframework.boot:spring-boot-autoconfigure:2.2.6.RELEASE
  • org.springframework.boot:spring-boot-autoconfigure-processor:2.2.6.RELEASE

🤔Bean이 중복되서 정의되면?

Bean을 생성할때 위에서는 @ComponentScan 이후에 @EnableAutoConfituration 가 실행되어 거기에 정의된 @Configuration 파일들을 읽어서 Bean에 등록한다고 했다. 실제로는 어떻게 적용이 될까?

예를들어 보자. A project 에서 Holoman 이라는 클래스에 @Configuration을 설정하고 Bean을 등록해 Local 저장소에 배포했다. (maven은 LifeCyclep에서 install 하면 되던데 gradle은 어떻게 하는걸까..)

그리고 B project에서 Dependency를 추가해 방금 배포한 jar 파일의 의존성을 추가한다. 그리고 B project 내부에서 똑같이 Holoman 이라는 타입을 가지는 Bean을 생성한다.

🤔🤔🤔

이때 B project를 실행시키면 어떤 Bean이 추가되어 있을까?

🥕🥕🥕

답은 A projectBean이다. @ComponentScan 이후에 @EnableAutoConfituration 가 실행되기 때문에 Bean이 덮어쓰기 되는 것이다.

그렇다면 B projectBean을 쓰기위해서는 어떻게 해야할까?

A project 에서 Bean으로 만들기 위한 객체 위에 @ConditionalOnMissingBean을 적어 줌으로써 해결 할 수 있다. 이 말은, 이 타입의 Bean이 없을때만 이 Bean을 쓰도록 해! 라는 말이다. 즉, LifeCycle에 의해 덮어쓰기 당하는 일을 없에는 것이다.

@Configuration
public class HolomanConfiguration {
    @Bean // Holoman 클래스에 데이터 저장 후 Bean에 등록하겠다고 표시
    @ConditionalOnMissingBean // 이 Bean을 추가할때 똑같은 타입의 다른 Bean이 있으면 그거 써!
    public Holoman holoman(){
        Holoman holoman = new Holoman();
        holoman.setHowLong(5);
        holoman.setName("ddingg");
        return holoman;
    }
}

🙄🙄 어.. 나는 Bean 재정의 안하고 그냥 Bean 안에 값을 수정하고 싶은데?

방법이 있습니다. 아까 위에서 B project에서 Bean을 재정의 해서 덮어 씌우는 걸 봤는데, 이렇게 Bean을 재정의 하려면 장황하게 뭘 또 써야한다. 이때 우리는 @ConfigurationPropertiesapplication.properties 파일을 통해 얼마든지 Bean안의 값을 다시 수정할 수 있다.

먼저 Getter/Setter Class를 만들어 @ConfigurationProperties를 적용시켜준다.

@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 를 등록하려고 하면 Spring Boot Configuration Annotation Processor not found in classpath 와 같은 에러가 나올 텐데, Open Document로 가서 보려했는데 안열리더라. 수동으로 찾아찾아가서 문서를 열어 확인했다. 여기 로 가서 Maven 혹은 Gradle에 맞는 의존성을 추가해주자. 나는

dependencies {
    annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
}

Gradle 4.6이후면 위와 같은 의존성을 사용하면 된다.

그리고 B project 로 가서 /src/main/resource안에 application.properties파일을 만들어 준다. (Spring initailizer를 사용했다면 이미 파일이 있을것이다. 그냥 Gradle이나 Maven 프로젝트로 Spring Project를 시작했다면 따로 만들어 주어야 한다.)

//application.properties
holoman.name = 바꾼이름
holoman.how-long = 100 // 변수명을 camelCase로 썻다면 하이픈으로 바꾸어 준다.(kebab-case 라고한다)

이런식으로 접근 할 수 있다.