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 에 있다.
'스프링부트' 카테고리의 다른 글
application events and listeners (0) | 2023.10.02 |
---|---|
spring boot JMS activemq connection factory (0) | 2023.09.28 |
spring boot actuator health endpoints delay check (0) | 2023.09.22 |
spring boot websocket (웹소켓) (0) | 2023.09.20 |
spring boot application startup tracking (0) | 2023.09.17 |
댓글