[스프링 부트 개념과 활용] 자동 설정 (Auto Configuration)
인프런에서 백기선님의 스프링 부트 개념과 활용을 수강하고 개인적으로 공부한 내용을 정리한 글입니다.
자동 설정의 개요
스프링 프로젝트가 실행되는 지점인 메인 어플리케이션의 기본적인 형태는 다음과 같다.
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
여기서 @SpringBootApplication
는 세개의 어노테이션을 하나로 합쳐놓은 형태다.
@SpringBootConfiguration
@ComponentScan
@EnableAutoConfiguration
스프링 부트 어플리케이션은 Bean을 두 단계에 걸쳐 등록한다.
1단계 : ComponentScan으로 등록하고
2단계 : EnableAutoConfiguration으로 추가적으로 읽어온 Bean들을 읽어서 등록한다.
@ComponentScan의 역할
@ComponentScan은 자기 자신부터 시작해서, 하위 패키지를 싹 훑어서 @Component라는 어노테이션을 붙인 클래스들을 찾아서 Bean으로 등록한다. 구체적인 검색 대상은 아래와 같다.
- @Configuration @Repository @Service @Controller @RestController
@EnableAutoConfiguration의 역할
Configuration는 Bean을 읽어들이기 위한 조건 등이 정의된 설정 파일이다. org.springframework.boot.autoconfigure
라는 패키지에서, spring.factories
라는 파일을 찾아보자.
이 파일에서 EnableAutoConfiguration라는 key 하단에 정의된 수많은 XXXConfiguration들이 모두 자동 설정 대상에 해당된다.
예시로 WebMvcAutoConfiguration이라는 설정 파일을 열어보자.
@Configuration
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {
...
}
@ConditionalXXX
형식의 어노테이션들은 어떤 경우에 객체를 Bean으로 등록할 건지 조건을 등록할 때 사용한다. 프로그램이 실행된 순간 수많은 자동 설정이 조건에 따라 적용돼서, 많은 Bean들이 자동으로 생성되고 어플리케이션이 구동되게 된다.
정리
- @SpringBootApplication이 붙은 어플리케이션을 실행
- @Component 어노테이션이 있는 클래스들을 스캔해서 Bean으로 등록 (Bean 등록 1단계)
- @EnableAutoConfiguration에 의해
spring.factories
안에 들어있는 수많은 자동 설정이 조건에 따라 적용 (Bean 등록 2단계) - 많은 Bean들이 자동으로 생성되고 어플리케이션이 구동되게 된다.
Starter & 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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>spring.example</groupId> <artifactId>spring-boot</artifactId> <version>1.0-SNAPSHOT</version> <!--프로젝트에 필요한 의존성--> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure-processor</artifactId> <optional>true</optional> </dependency> </dependencies> <!--의존성 관리에 필요한 영역--> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.0.3.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> </project>
-
Configutation 대상이 되는 클래스를 작성한다.
package home; public class Person { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
-
Person 클래스의 설정 파일을 작성한다.
package home; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class PersonConfiguration { @Bean public Person person() { Person person = new Person(); person.setAge(10); person.setName("코알라일락"); return person; } }
-
resources
폴더 하단에META-INF
라는 이름의 폴더를 하나 만들고, 그 안에spring.factories
라는 이름의 빈 파일을 하나 만들어주자.
- key에 해당되는 값을 읽어올 수 있도록
spring.factories
에 방금 작성한 자동 설정 파일을 추가한다.
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
home.PersonConfiguration
mvn install
명령어를 실행하자. 빌드가 끝나면 jar 파일이 생성되고, 이 파일은 다른 maven 프로젝트에서도 사용할 수있도록 로컬 maven 저장소에 설치된다.
그런데... mvn install을 실행했을 때 이런 에러가 떴다. 원인은 아마 내가 brew로 maven을 설치했기 때문인듯.
[ERROR] Source option 5 is no longer supported. Use 7 or later.
[ERROR] Target option 5 is no longer supported. Use 7 or later.
pom.xml에 아래의 태그를 작성하면 해결된다.
<properties>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
자동 설정이 적용되고 있는지 로그로 확인해보자. ApplicationRunner를 활용해서 Bean을 출력하는 코드를 작성했다.
package home;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
@Component
public class PersonRunner implements ApplicationRunner {
@Autowired
Person person;
public void run(ApplicationArguments args) throws Exception {
System.out.println(person);
}
}
person 객체가 잘 주입되어 로그가 찍히는 것을 확인할 수 있다.
이 경우 발생할 수 있는 문제
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public Person person() {
Person person = new Person();
person.setAge(20);
person.setName("개발자");
return person;
}
}
만약 이런식으로 Application에서 동일한 Person 객체를 Bean으로 등록했다면, @AutoConfiguration
이 우선순위가 더 높아서 개발자가 직접 등록한 Bean이 적용되지 않는다. 다시 프로젝트를 실행해 보면 로그는 여전히 Person{name='코알라일락', age=10}
로 출력된다.
@ConfigurationProperties로 자동 설정 구현하기
위에서 발생한 문제를 해결하려면 컴포넌트 스캔으로 읽은 Bean이 항상 우선순위가 더 높아야 한다. 이를 위해 @ConditionalOnMissingBean
어노테이션을 사용한다.
컴포넌트 스캔 단계에서 이미 등록된 Bean이라면, AutoConfiguration
단계에선 Bean으로 등록하지 않는다.
@Configuration
public class PersonConfiguration {
@Bean
@ConditionalOnMissingBean
public Person person() {
Person person = new Person();
person.setAge(10);
person.setName("코알라일락");
return person;
}
}
다시 한 번 실행해보면 Application에서 직접 등록한 Bean이 출력된다. 이걸 활용하면 스프링에서 제공하는 각종 기능을 내 입맛대로 바꿀 수 있게된다.
나에게 필요한 Bean을 하나하나 모두 통째로 생성해서 등록하는 작업은 번거롭다. Bean을 직접 정의하지 않고 properties만 정의해서 간편하게 갖다 쓰는 방법이 있다.
resources
폴더 하단에application.properties
라는 파일을 하나 만든다.
-
application.properties
안에 Bean에 설정할 값을 key-value 형식으로 작성한다.person.name = koalailac person.age = 30
-
@ConfigurationProperties("prefix 이름")
어노테이션을 붙인 Properties 파일을 만든다.@ConfigurationProperties("person") public class PersonProperties { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
-
Configuration metadata file를 생성할 수 있도록 pom.xml의
<dependencies></dependencies>
태그 내부에 새로운 의존성을 추가한다. (참고)<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
-
properties 파일을 읽어와 값을 사용할 수 있도록 Configuration 파일에
@EnableConfigurationProperties
어노테이션을 붙인다.@Configuration @EnableConfigurationProperties(PersonProperties.class) public class PersonConfiguration { @Bean @ConditionalOnMissingBean public Person person(PersonProperties properties) { Person person = new Person(); person.setAge(properties.getAge()); // properties value를 가져옴 person.setName(properties.getName()); // properties value를 가져옴 return person; } }
로컬 저장소의 jar 파일에 변경사항이 반영되도록 다시 한 번 mvn install
을 실행해야 한다.
다시 빌드해보면 properties에 작성한 값이 로그에 찍히는 것을 확인할 수 있다.
'if (study) > Spring' 카테고리의 다른 글
[스프링 부트 개념과 활용] 내장 웹 서버 (Embedded web server) (0) | 2020.07.24 |
---|---|
[스프링 부트 개념과 활용] 의존성 관리 (dependency management) (0) | 2020.07.20 |
[스프링 부트 개념과 활용] 스프링 부트 시작하기 (0) | 2020.07.20 |