SPRING/Spring Boot

[SpringBoot] SpringBoot 자동구성 원리

IT록흐 2023. 5. 4. 09:48
반응형
 

[SpringBoot] 자동구성 라이브러리 만들기

한 SpringBoot 프로젝트에서 추가기능이 필요하여 라이브러리를 하나 만들었다고 가정하자. 라이브러리는 클래스파일 모음으로 POJO이다. IOC컨테이너에 POJO를 Bean으로 등록하려면 설정파일을 읽어

lordofkangs.tistory.com

 

 

지난 포스팅에서 자동구성 라이브러리를 만들어 보았다.

 

자동구성 라이브러리란, 프로젝트가 외부라이브러리를 사용할 때 Spring이 자동으로 필요한 Bean을 등록하도록 구성된 라이브러리이다.  Bean은 POJO에서 Spring이 설정파일을 읽어 등록한다. 설정파일을 자동구성으로 만들면 외부라이브러리를 추가만해도 자동으로 Bean이 IOC컨테이너에 등록된다. 

 

 

 

SpringBoot 프로젝트를 생성할 때 Dependcy를 추가한다.

Dependency에 추가된 외부라이브러리 중 프로젝트 실행시, IOC컨테이너에 자동으로 Bean을 등록할 필요가 있는 라이브러리가 있다.

 

예를 들어보면

 

@Slf4j
@SpringBootTest
class DemoApplicationTests {

		@Autowired
		DataSource dataSource;
		@Autowired
		TransactionManager transactionManager;
		@Autowired
		JdbcTemplate jdbcTemplate;

		@Test
		void checkBean() {
			log.info("DataSource = {}", dataSource);
			log.info("transactionManager = {}", transactionManager);
			log.info("jdbcTemplate = {}", jdbcTemplate);

			assertThat(dataSource).isNotNull();
			assertThat(transactionManager).isNotNull();
			assertThat(jdbcTemplate).isNotNull();
		}

}

 

 

위와 같이, 테스트 코드를 만들어 보았다.

DataSource, TransactionManager, JdbcTemplate는 @AutoWired 되어 있다. 개발자는 3가지 객체를 Bean으로 등록하기 위해 설정파일을 만들 필요가 없다. 자동으로 IOC컨테이너에 등록된다

 

 

위 그림처럼 Bean이 생성되어 있다. 

개발자는 설정파일을 만들지 않았고  그저 Dependency만 추가했을뿐인데,

어떻게 Bean이 자동으로 IOC컨테이너에 등록된 것일까?

 

 


 

 

외부라이브러리 목록을 보면

'autoconfigure(자동구성)' 이름이 들어간 라이브러리가 있다.

 

 

 

해당 라이브러리의 약속된 위치에 자동구성설정클래스 목록이 정리되어 있다.

META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

 

 

 

 

해당 파일을 열어보면

위 그림과 같이, 수많은 자동구성설정파일 목록이 들어가 있다. 

 

Spring Boot은 약속된 위치의 파일을 읽어

자동구성설정파일 목록대로 Bean을 생성하여 IOC컨테이너에 등록한다.

 

그렇다면 Spring Boot는 어떤 방식으로 해당 파일을 읽을까?

 

@SpringBootApplication
public class AutoConfigApplication {
    public static void main(String[] args) {
        SpringApplication.run(AutoConfigApplication.class, args);
    }
}

 

스프링부트의 시작은 @SpringBootApplication 어노테이션이다.

 

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration 
@EnableAutoConfiguration // 중요!
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
	// 중략...
}

 

@SpringBootApplication 어노테이션을 열어보면,

@EnableAutoConfiguration을 확인할 수 있다.

 

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class) //중요!
public @interface EnableAutoConfiguration {
	//중략...
}

 

@EnableAutoConfiguration 어노테이션을 열오보면,

@Import 어노테이션이 AutoConfigurationImportSelector 클래스를 Import하고 있음을 확인할 수 있다.

 

@Import 어노테이션은 설정파일을 두개 이상 사용할 경우 사용한다.

 

테스트 코드를 하나 만들어 보았다.

public class ImportSelectorTest {
    @Test
    void staticConfig(){
        AnnotationConfigApplicationContext appContext =
                new AnnotationConfigApplicationContext(StaticConfig.class); // 설정파일1 + 설정파일2
        HelloBean bean = appContext.getBean(HelloBean.class);
        assertThat(bean).isNotNull();
    }

    @Configuration
    @Import(HelloConfig.class) //설정파일2
    public static class StaticConfig {  //설정파일1
    	//중략...
    }
}

 

StaticConfig 클래스는 설정파일이다.

 

여기에 @Import 어노테이션을 사용하여, HelloConfig 설정클래스를 추가할 수 있다. AnnotaionConfigApplicationContext, 즉 IOC컨테이너에 설정파일을 2개 등록할 수 있다.

 

하지만 이는 정적이다.

 

@Import 어노테이션에 명시된 설정클래스만 등록가능하므로 정적인 코드이다. 

만약 수가 많고 변경이 잦다면 동적인 코드로 작성해야한다.

 

그래서 ImportSelector가 필요하다.

 

public class HelloImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"hello.selector.HelloConfig"};
    }
}

 

ImportSelect 인터페이스를 구현한 구현체이다.

 

selectImports 메소드를 재정의하였는데, return을 문자열 배열로 한다.

문자열 배열 안에 등록하고 싶은 설정클래스 경로를 적어주면 된다.

 

 

public class ImportSelectorTest {
    @Test
    void selectorConfig(){
        AnnotationConfigApplicationContext appContext 
        = new AnnotationConfigApplicationContext(SelectorConfig.class); // 설정파일 추가

        HelloBean helloBean = appContext.getBean(HelloBean.class);
        assertThat(helloBean).isNotNull();
    }


    @Configuration
    @Import(HelloImportSelector.class) // SelectImportor의 구현체 클래스
    public static class SelectorConfig{
		//중략...
    }
}

 

AnnotationConfigApplicationContext, IOC컨테이너에 설정파일을 등록한다.

SelectorConfig는 SelectImportor 구현체를 Import한 클래스로 동적으로 설정파일을 등록한다.

 

 


 

 

SpringBoot는 AutoConfigurationImportSelector.class를 SelectorImportor의 구현체로 사용한다. 

 

 

AutoConfigurationImportSelector.java

	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		List<String> configurations = ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader())
				.getCandidates();
		Assert.notEmpty(configurations,
				"No auto configuration classes found in "
						+ "META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "
						+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}

 

 

 

getCandidateConfigurations 메소드를 보면

ImportCandidates.load에서 설정파일 목록을 로드하는 것으로 확인된다.

 

그럼 이를 디버깅 해보자.

 

ImportCandidates.java

 

location 변수에 등록된 url 경로를 보며 약속된 위치임을 확인할 수 있다.

META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 

 

위 경로에 저장된 자동구성 클래스 목록을 읽어, 자동구성 설정클래스 위치를 확인하는 것이다.

자동구성설정 클래스에서 Bean을 등록하는 은 아래 포스팅을 참고하기 바란다.

 

 

 

[SpringBoot] 자동구성 라이브러리 만들기

한 SpringBoot 프로젝트에서 추가기능이 필요하여 라이브러리를 하나 만들었다고 가정하자. 라이브러리는 클래스파일 모음으로 POJO이다. IOC컨테이너에 POJO를 Bean으로 등록하려면 설정파일을 읽어

lordofkangs.tistory.com

 

 


 

참고자료

 

스프링 부트 - 핵심 원리와 활용 - 인프런 | 강의

실무에 필요한 스프링 부트는 이 강의 하나로 모두 정리해드립니다., - 강의 소개 | 인프런

www.inflearn.com

 

 

 

반응형