스프링부트

Spring Boot AutoConfiguration 간단 모듈 만들어보기

알쓸개잡 2023. 9. 24.

Spring Boot AutoConfiguration은 여러 디펜던시 들의 Configuration을 자동으로 수행하여 복잡한 Configuration 구성 없이 필요한 빈을 자동으로 주입 받을 수 있도록 도와주는 기능이다. 이번 포스팅에서는 Spring Boot AutoConfiguration이 동작하는 간단한 예제 코드를 작성해 보겠다.

 

AutoConfiguration class

AutoConfiguration을 구현하는 클래스는 @AutoConfiguration 어노테이션이 지정 된다. 또한 여러 @Conditional 어노테이션을 통해서 AutoConfiguration이 적용되어야 하는 필터를 설정할 수 있다. 일반적으로 AutoConfiguration 클래스는 @ConditionalOnClass 및 @ConditionalOnMissingBean 어노테이션이 많이 사용된다.  AutoConfiguration에 관한 Conditional 어노테이션이 많이 있는데 spring boot autoconfigure condition 페이지를 참고하면 좋을 것 같다.

 

샘플 코드

샘플 코드에 대해서 간단히 설명하면 AutoConfiguration을 통해서 자동으로 주입되는 빈은 두 개의 숫자와 BiFunctional 람다 함수를 입력 받아 두 개의 숫자를 람다 함수에 전달하여 람다 함수가 수행한 결과를 리턴하는 메소드를 가진 클래스이다. 이름은 calculator 이다.

AutoConfiguration이 동작하는 조건은 다음과 같다.

  • calculator.use-auto-configure=true
  • Calculator.class 클래스가 클래스 패스에 있어야 함

필요한 모듈은 아래와 같다.

  • calculator: 빈으로 생성될 대상 클래스를 가지고 있는 모듈
  • calculator-autoconfigure: AutoConfiguration을 수행할 모듈
  • calculator-starter: calculator-autoconfigure, calculator를 디펜던시로 갖는 starter 모듈
  • calculatorapp: calculator-starter를 디펜더시로 갖는 테스트 애플리케이션

편의상 멀티 모듈 프로젝트로 작성하였다.

 

root pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>simple-starter</artifactId>
    <version>0.0.1</version>
    <packaging>pom</packaging>
    <name>simple-starter</name>
    <description>simple-starter</description>

    <modules>
        <module>calculator</module>
        <module>calculator-autoconfigure</module>
        <module>calculator-starter</module>
        <module>calculatorApp</module>
    </modules>
</project>

 

Calculator 모듈

calculator는 실제 동작을 수행하는 클래스를 갖는 모듈이다.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

    <groupId>com.example</groupId>
    <artifactId>calculator</artifactId>
    <version>0.0.1</version>
</project>
package com.example.calculator;

import java.util.function.BiFunction;

public class Calculator {

	private final String resultPrefix;
	public Calculator(String resultPrefix) {
		this.resultPrefix = resultPrefix;
	}

	public String calculate(int x, int y, BiFunction<Integer, Integer, Integer> what) {
		return what.andThen((result) -> resultPrefix + " " + result).apply(x, y);
	}
}

calculate 메소드에서 두 숫자의 연산 결과 앞에 resultPrefix를 붙여 String 타입의 결과를 리턴하도록 하였다.

Calculator 생성자의 resultPrefix 파라미터는 calculator-autoconfigure에서 Calculator를 빈으로 주입시 calculator.result-prefix 속성에 지정된 값을 넘겨준다.

 

calculator-autoconfigure 모듈

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.1.1</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>calculator-autoconfigure</artifactId>
	<version>0.0.1</version>
	<name>calculator-autoconfigure</name>
	<description>calculator-autoconfigure</description>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
		<!-- properties 설정을 자동으로 주입받기 위함 
		META-INF/spring-configuration-metadata.json 에 기록됨 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</artifactId>
			<optional>true</optional>
		</dependency>
		<!-- auto configuration의 conditional 필터 정보를 
		META-INF/spring-autoconfigure-metadata.properties 에 작성하기 위함 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-autoconfigure-processor</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!-- auto configuration을 생성하기 위함 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-autoconfigure</artifactId>
		</dependency>
		<!-- Calculator 클래스를 빈으로 생성하기 위함 -->
		<!-- starter를 만들어서 배포하는 경우에는 starter에서 종속성을 갖도록
		optional을 true로 지정한다. -->
		<dependency>
			<groupId>com.example</groupId>
			<artifactId>calculator</artifactId>
			<version>${project.version}</version>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
	</dependencies>
</project>

 

CalculatorProperties.java

package com.example.calculator.autoconfigure.property;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "calculator")
@Getter
@Setter
public class CalculatorProperties {
	private boolean useAutoConfigure;
	private String resultPrefix;
}

calculator.use-auto-configure, calculator.result-prefix 설정 값을 주입받는다.

 

CalculatorAutoConfiguration.java

package com.example.calculator.autoconfigure.configuration;

import com.example.calculator.Calculator;
import com.example.calculator.autoconfigure.property.CalculatorProperties;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;

@AutoConfiguration
@ConditionalOnClass({Calculator.class})
@EnableConfigurationProperties(CalculatorProperties.class)
@ConditionalOnProperty(prefix = "calculator", name = "use-auto-configure", havingValue = "true")
public class CalculatorAutoConfiguration {

	@ConditionalOnMissingBean(Calculator.class)
	@Bean
	public Calculator calculator(CalculatorProperties calculatorProperties) {
		return new Calculator(calculatorProperties.getResultPrefix());
	}
}

ConditionalOnClass 어노테이션에 의해서 Calculator.class 가 클래스 패스에 있어야 한다. 즉, com.example:calculaor 디펜던시가 있어야 한다. ConditionalOnProperty 어노테이션에 의해서 calculator.use-auto-configure=true 인 경우 에만 AutoConfiguration이 동작한다.

 

AutoConfiguration.imports

resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 파일에 AutoConfiguration을 수행하는 클래스 파일 경로를 기록해야 한다. Spring Boot 에서는 이 파일에 있는 경로의 AutoConfiguration 파일을 자동 설정 대상으로 한다.

com.example.calculator.autoconfigure.configuration.CalculatorAutoConfiguration

 

calculator-starter 모듈

pom.xml

starter는 종속성만 가지고 있기 때문에 pom.xml만 정의한다.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>calculator-starter</artifactId>
    <version>0.0.1</version>
    <name>calculator-starter</name>
    <description>calculator-starter</description>

    <dependencies>
        <dependency>
            <groupId>com.example</groupId>
            <artifactId>calculator-autoconfigure</artifactId>
            <version>${project.version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>com.example</groupId>
            <artifactId>calculator</artifactId>
            <version>${project.version}</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>
</project>

 

calculatorApp 애플리케이션

AutoConfiguration 동작을 테스트 해보기 위한 애플리케이션이다.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>calculatorApp</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>calculatorApp</name>
    <description>calculatorApp</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.example</groupId>
            <artifactId>calculator-starter</artifactId>
            <version>0.0.1</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

 

application.yml

calculator:
  use-auto-configure: true
  result-prefix: 'result is'

calculator.use-auto-configure: false 를 셋팅하면 AutoConfiguration이 동작하지 않으므로 아래 CalculatorAppApplication.java 코드에서 Calculator calculator 는 자동 주입되지 않는다.

 

CalculatorAppApplication.java

package com.example.calculatorapp;

import com.example.calculator.Calculator;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@RequiredArgsConstructor
public class CalculatorAppApplication implements CommandLineRunner {

	//calculator-starter를 통해 배포된 calculator-autoconfigure에 의해서 자동 주입된다.
	private final Calculator calculator;

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

	@Override
	public void run(String... args) throws Exception {
		//calculator.result-prefix + 더하기 연산 수행한 결과
		String result = calculator.calculate(1, 2, Integer::sum);
		System.out.println(result);

		//곱하기 연산 수행 결과
		result = calculator.calculate(2, 3, (x, y) -> x * y);
		System.out.println(result);

		//곱하기 연산 수행 결과
		result = calculator.calculate(4, 5, (x, y) -> x * y);
		System.out.println(result);
	}
}

 

빌드 및 실행

$>./mvnw clean compile
...
[INFO] Reactor Summary:
[INFO] 
[INFO] calculator 0.0.1 ................................... SUCCESS [  0.322 s]
[INFO] calculator-autoconfigure 0.0.1 ..................... SUCCESS [  0.411 s]
[INFO] calculator-starter 0.0.1 ........................... SUCCESS [  0.011 s]
[INFO] calculatorApp 0.0.1-SNAPSHOT ....................... SUCCESS [  0.184 s]
[INFO] simple-starter 0.0.1 ............................... SUCCESS [  0.001 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS

$>./mvnw install
...

$>./mvnw spring-boot:run -pl calculatorApp
....
result is 3
result is 6
result is 20

정상적으로 Calculator 인스턴스가 주입 되어 수행되는 것을 확인할 수 있다. 또한 calculator.result-prefix 설정도 적용된 것을 알 수 있다. 간단하게나마 spring boot auto configuration 샘플 코드를 작성해 보았다.

관련 코드는

https://gitlab.com/blog4031530/spring-simple-starter 에 있다.

 

blog / spring-simple-starter · GitLab

GitLab.com

gitlab.com

 

댓글

💲 추천 글