5. Spring Boot 코드 살펴보기
- Spring Boot 동작 원리 알기 - 자동 생성 코드 살펴보기
6. 자동 구성과 조건
- maven에 라이브러리 추가하기
- Auto Configuration(자동 구성) 동작 원리
- 라이브러리 추가시 자동 구성
- Auto Configuration 에서 제외
- @Conditional
- @ConditionalOnXXX
7. 외부 구성
- Externalized Configuration 사용 (바인딩)
- Spring Profile
5. Spring Boot 코드 살펴보기
Spring Boot 동작 원리 알기
(1) 자동 생성 코드 살펴보기 - pom.xml
- spring-boot-starter-parent
- spring-boot 버전별로 지원하는 라이브러리 의존성 목록.
- spring-boot 버전을 업그레이드하면 라이브러리 의존성도 모두 자동 업그레이드 된다. (자동으로 버전 관리 해줌)
- spring-boot-starter
- core, context, logging 등 기본 스프링 기능을 담당하는 라이브러리.
- 자주 사용하는 라이브러리를 자동으로 추가해준다.
- spring-boot-maven-plugin
- spring boot의 실행을 편하게 하기 위해서, build 할 때 간섭하는 플러그인.
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.5</version>
<relativePath/>
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>3.2.5</version>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
(2) 자동 생성 코드 살펴보기 - Main.java
@SpringBootApplication
- main() 메소드를 포함하는 클래스에 달리며, spring boot에서 매우 중요한 역할.
- @SpringBootConfiguration
- Spring Boot의 특수한 설정 클래스임을 나타내는 annotation.
- @ComponentScan
- Spring에게 지정된 패키지와 그 하위 패키지들을 스캔하도록 지시하는 annotation.
- Spring이 @Component를 자동으로 찾아서 Spring ApplicationContext에 Bean으로 등록할 위치를 지정.
- @EnableAutoConfiguration
- Spring Bean의 자동 구성 기능을 활성화.
/*
Spring boot 프로젝트가 실행되면, SpringApplication.run()이 실행되고
실행을 따라가보면 applicationContext 생성 및 초기화 / 코드 해석 / 빈 초기화 등 여러 동작 수행.
*/
@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 { ... }
- SpringApplication.java
- Spring boot 프로젝트가 실행되면, SpringApplication.run()이 실행되고, 실행을 따라가보면 applicationContext 생성 및 초기화, 코드 해석, 빈 초기화 등 여러 동작을 수행한다.
SpringApplication.java
1. SpringApplication.run() 호출할 때, 자기 자신을 인자(argument)로 넘기고 sources에 추가한다.
SpringApplication.run(Main.class, args)
2. applicationContext 생성.
SpringApplication.run()
SpringApplication.createApplicationContext()
DefaultApplicationContextFactory.create()
DefaultApplicationContextFactory.createDefaultApplicationContext()
3. DefaultApplicationContextFactory.java
private ConfigurableApplicationContext createDefaultApplicationContext() {
if (!AotDetector.useGeneratedArtifacts()) {
return new AnnotationConfigApplicationContext();
}
return new GenericApplicationContext();
}
4. Bean Factory 생성.
SpringApplication.run()
SpringApplication.refreshContext()
SpringApplication.refresh()
AbstractApplicationContext.refresh()
AbstractApplicationContext.obtainFreshBeanFactory()
AbstractRefreshableApplicationContext.refreshBeanFactory()
---------------------------------------------------------------------------------------------------------------------
// AbstractRefreshableApplicationContext.java
{
@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
}
5. 코드(annotation) 해석
SpringApplication.run()
SpringApplication.refreshContext()
SpringApplication.refresh()
AbstractApplicationContext.refresh()
AbstractApplicationContext.invokeBeanFactoryPostProcessors()
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()
ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry()
ConfigurationClassPostProcessor.processConfigBeanDefinitions()
ConfigurationClassParser.parse()
ConfigurationClassParser.processConfigurationClass()
ConfigurationClassParser.doProcessConfigurationClass()
---------------------------------------------------------------------------------------------------------------------
// ConfigurationClassParser.java
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass, filter);
}
...
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScan.class, ComponentScans.class,
MergedAnnotation::isDirectlyPresent);
}
6. Bean 생성 (초기화)
SpringApplication.run()
SpringApplication.refreshContext()
SpringApplication.refresh()
AbstractApplicationContext.refresh()
AbstractApplicationContext.finishBeanFactoryInitialization()
DefaultListableBeanFactory.preInstantiateSingletons()
AbstractBeanFactory.getBean()
AbstractBeanFactory.doGetBean()
AbstractAutowireCapableBeanFactory.createBean()
AbstractAutowireCapableBeanFactory.doCreateBean()
---------------------------------------------------------------------------------------------------------------------
// AbstractAutowireCapableBeanFactory.java
{
...
try {
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
}
...
@ComponentScan
- Spring Framework에서 자동으로 StereoType annotation이 붙은 클래스들을 검색하고 Bean으로 등록하는데 사용.
(StereoType annotation: @Component, @Service, @Repository, @Controller ...) - 기본적으로, 선언된 클래스가 속한 패키지와 그 하위 패키지들을 검색 범위로 한다.
- 이걸 통해서 Spring Boot 프로젝트가 코드를 어디까지 해석할지 제어할 수 있다.
- 주요 옵션
- basePackages: 특정 패키지를 스캔의 시작점으로 지정. 여러 패키지를 지정할 수 있다.
- basePackageClasses: 스캔 시작점으로 사용할 클래스를 지정한다. 여러 클래스 지정 가능
- includeFilters와 excludeFilters: 특정 조건에 맞는 컴포넌트를 포함시키거나 제외시킬 때 사용.
- useDefaultFilters: 기본적으로 활성화되어 있는 스테레오타입 어노테이션을 기반으로 한 필터 사용 여부를 설정. 기본값은 true (@Component, @Service, @Repository, @Controller 등)
@Autowired
- Spring Framework에서 DI를 위해 사용되는 annotation. (Bean 주입에 사용)
- 이걸 사용하면 Spring 컨테이너가 자동으로 지정된 타입의 Bean을 해당 필드, 메소드, 생성자에 주입.
- 주입할 Bean이 없거나 모호할 때 오류 발생.
- 옵션 - required: 빈의 null 여부에 대한 설정, 기본값은 true다.
- Spring Boot는 @Autowired를 보고, Spring Bean이 적절한 위치에 주입될 수 있도록 설정한다.
요약
Spring Boot의 기본 설정에 대한 동작.
- Main 클래스를 인자로 넘기기 때문에 해당 클래스를 해석한다.
- Spring Boot는 @ComponentScan을 통해서 코드를 해석한다.
- Main 클래스는 기본적으로 @SpringBootApplication를 가지고 있고, 얘는 @ComponentScan을 가지고 있다.
- @ComponentScan의 옵션에 따라서 Spring Boot는 전체 패키지에서 클래스들의 코드를 해석한다.
- 해석하는 도중에 @Component, @Autowired 등 annotation이 달린 클래스를 만나면 다르게 동작하도록 내부적으로 설정.
버전 관리
- Spring BOM(Bill of Materials): 여러 라이브러리와 의존성 간의 버전 충돌 문제를 해결.
SpringApplication
- Spring은 SpringApplication.run()을 통해서 실행되고, 이 코드를 보면 applicationContext, Bean 생성 등 많은 작업을 함
@SpringBootApplication
- 이 annotation 내부의 또 다른 annotation들을 통해 Spring Boot에서 많은 일을 하는걸 알 수 있다.
@ComponentScan
- Spring에게 지정된 패키지와 그 하위 패키지들을 스캔하도록 지시하는 어노테이션. 스캔을 통해 Bean을 생성한다.
@Component
- @ComponentScan으로 인해서 찾아지는 클래스로, Spring에서 Bean으로 등록한다.
@Autowired
- 생성한 Bean을 적절한 위치에 넣기 위해 사용되는 annotation.
6. 자동 구성과 조건
maven에 라이브러리 추가하기
- Spring Boot를 이용해서 Web Application을 동작하게 하고 싶다.
- pom.xml에 web 의존성 추가하면, 자동으로 Web Application으로 동작함. -> 자동 구성(Auto Configuration)
<!-- pom.xml에 Web 의존성 추가 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Auto Configuration(자동 구성) 동작 원리
@EnableAutoConfiguration
- 해당 annotation을 추가하면 Application은 자동으로 계속 특정 경로의 클래스를 찾으려고 한다.
- Spring Boot에서 자동 구성(Auto Configuration)을 활성화하기 위해 사용되는 annotation.
@EnableAutoConfiguration
- @Import가 있을 경우, 지정된 클래스를 Application의 설정 컨텍스트에 명시적으로 포함시킨다.
- AutoConfigurationImportSelector는 "META-INF/spring/%s.imports" 경로의 파일을 해석해서 해당하는 클래스를 로드하려고 시도함.
@Import(AutoConfigurationImportSelector.class)
ConfigurationClassParser.doProcessConfigurationClass()
ConfigurationClassParser.processImports()
AutoConfigurationImportSelector.selectImports()
AutoConfigurationImportSelector.selectImports()
AutoConfigurationImportSelector.getCandidateConfigurations()
ImportCandidates.load()
ImportCandidates.LOCATION
ImportCandidates.java
private static final String LOCATION = "META-INF/spring/%s.imports";
{
protected List<String> getExcludeAutoConfigurationsProperty() {
Environment environment = getEnvironment();
if (environment == null) {
return Collections.emptyList();
}
if (environment instanceof ConfigurableEnvironment) {
Binder binder = Binder.get(environment);
return binder.bind(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class)
.map(Arrays::asList)
.orElse(Collections.emptyList());
}
String[] excludes = environment.getProperty(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class);
return (excludes != null) ? Arrays.asList(excludes) : Collections.emptyList();
}
}
org.springframework.boot.autoconfigure.AutoConfiguration.imports
...
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration
...
WebMvcAutoConfiguration.java
@AutoConfiguration(after = { DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@ImportRuntimeHints(WebResourcesRuntimeHints.class)
public class WebMvcAutoConfiguration {
...
}
위 파일들의 상위에 @ConditionalXXX annotation이 붙어있는데, 이는 특정 조건에서 동작한다는 뜻이다.
ex) @ConditionalOnClass({Servlet.class, DispatcherServlet.class. ... })는 이 클래스들이 존재할 때만 동작한다는 뜻이다.
라이브러리 추가시 자동 구성
- pom.xml에 dependency를 추가하면, maven은 해당 라이브러리 파일을 프로젝트에 추가한다.
- 라이브러리가 추가되면서 특정 조건이 만족되어 작동하지 않던 빈들이 동작한다.
Auto Configuration 에서 제외
- @EnableAutoConfiguration의 exclude를 설정한다.
(@SpringBootApplication을 사용한 경우도 동일한 방법으로 제외 가능)
@SpringBootApplication(exclude = RedisAutoConfiguration.class)
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}
참고: 2024.11.09 - [NHN Java 백엔드 8기/Spring Boot] - [Spring Boot] 서비스 추상화 (Portable Service Abstraction)
[Spring Boot] 서비스 추상화 (Portable Service Abstraction)
1. 서비스 추상화 (PSA, Poratble Service Abstraction)개발자가 특정 환경이나 기술에 종속되지 않고, 일관된 방식으로 서비스를 사용할 수 있도록 추상화를 제공.Interface로 사용하고, Interface의 구현부(impl
lightningtech.tistory.com
@Conditional
- 설정된 모든 Condition 인터페이스의 조건이 TRUE인 경우 동작.
- 특정 조건에서만 Bean이 동작하길 원할 때 사용.
- Auto Configuraton(자동 구성)도 이 기능을 이용해서 동작.
@ConditionalOnXXX
- Spring-boot가 제공하는 @Conditional의 확장.
구분 | 내용 | 비고 |
@ConditionalOnWebApplication | 프로젝트가 웹 애플리케이션이면 설정 동작 | - |
@ConditionalOnBean | 해당 Bean 이 Spring Context 에 존재하면 동작 | Auto configuration only |
@ConditionalOnMissingBean | 해당 Bean 이 Spring Context 에 존재하지 않으면 동작 | Auto configuration only |
@ConditionalOnClass | 해당 클래스가 존재하면 자동 설정 등록 | - |
@ConditionalOnMissingClass | 해당 클래스가 존재하지 않으면 자동 설정 등록 | - |
@ConditionalOnResource | 자원(file 등)이 존재하면 동작 | - |
@ConditionalOnProperty | 특정 프로퍼티가 존재하면 동작 | - |
@ConditionalOnJava | JVM 버전에 따라 동작 여부 결정 | - |
@ConditionalOnWarDeployment | 전통적인 WAR 배포 방식에서만 동작 | - |
@ConditionalOnExpression | SpEL (Spring Expression Language)의 결과에 따라 동작 여부 결정 | - |
* 주의
Spring은 Bean을 등록 할 때, 패키지를 위에서부터 읽으면서 등록하므로, 빈 등록 시 우리가 원하는 대로 동작하지 않을 수 있다. 따라서 주의해서 사용이 필요하다
참고
[Spring Boot] @ConfigurationProperties 활성화시 @Profile 무시 에러
1. 에러 발생의도문제문제 원인 파악문제가 발생한 코드2. @ConfigurationProperties란?Externalized Configuration (외부 구성) Externalized Configuration 사용 (바인딩)@ConfigurationProperties3. 해결Bean 등록 방법 2가지@Co
lightningtech.tistory.com
7. 외부 구성 (Externalized Configuration)
- Spring-boot는 같은 소스 코드로 여러 환경에서 동작할 수 있도록 외부화 설정을 제공한다.
- java properties, YAML, 환경변수, 실행 인자로 설정 가능하다.
- 전체 프로젝트의 설정은 .properties, .yaml 중 하나만 사용하는 것을 권장.
- 같은 곳에 application.propreties, application.yaml 이 동시에 존재하면 application.propreties 가 우선한다.
- 외부 구성만 수정했지만, application의 동작이 바뀌는 것도 자동 구성(Auto Configuration)의 일부이다.
Externalized Configuration 사용 (바인딩)
- Spring Boot는 설정값을 바인딩 하기 위한 2가지 방법을 제공.
(1) @Value바인딩
- 속성값(properties) @Value annotation으로 바인딩하여 사용.
- Spel 표현식을 지원.
@Component
public class AppStartupRunner implements ApplicationRunner {
@Value("${greeting.english}")
private String english;
@Value("${greeting.korean}")
private String korean;
@Override
public void run(ApplicationArguments args) {
System.out.println(english);
System.out.println(korean);
}
}
(2) @ConfigurationProperties 바인딩
- properties를 이 annotation으로 바인딩하여 사용.
- 이 annotation으로 설정된 클래스는 Dependency Injection(의존성 주입)로 참조하여 사용
(DI 3가지 방법: 생성자/필드/Setter 주입)
- 사용법
- @ConfigurationProperties 클래스 선언
- @ConfigurationProperties 활성화(2가지 방법)
- 활성화가 되면서 Bean 등록됨.
- @EnableConfigurationProperties("GreetingProperties.class")
(활성화가 필요한 클래스를 직접 등록) - @ConfigurationPropertiesScan
(@ComponentScan과 비슷하게 패키지 단위로 스캔 -> 1번 방법이 너무 길어지는 경우 사용)
- @EnableConfigurationProperties("GreetingProperties.class")
/* @ConfigurationProperties 클래스 선언 */
@AllArgsConstructor
@Getter
@ConfigurationProperties("greeting")
public class GreetingProperties {
private String english;
private String korean;
}
/* @ConfigurationProperties 활성화 */
// 방법 1
@EnableConfigurationProperties(GreetingProperties.class)
// 방법 2
@ConfigurationPropertiesScan
Spring Profile
- 프로그램을 띄우는 환경마다 동작을 다르게 하고 싶을 때 사용.
- 환경마다 다른 설정파일을 참조할 수 있고, 이를 Profile이라 부른다.
- spring.profiles.active 실행 인자로, 프로필 지정 설정 파일의 로딩 여부가 결정된다.
- ex) spring.profiles.active=prod 는 application.properties 와 application-prod.properties 를 모두 로딩한다.
- 프로필은 여러 개 지정 가능하다. (중복될 경우, 덮어쓰기(override)한다)
Profile에 따라서 다른 Bean이 동작하게 하기
(1) @Configuration + @Bean
@Configuration
public class GreetingConfig {
//TODO-1 eng profile 인 경우 빈이 활성화 된다.
@Profile("eng")
@Bean
Greeting englishGreeting() {
return new EnglishGreeting();
}
//TODO-2 eng profile 이 아닌 경우 빈이 활성화 된다.
@Profile("!eng")
@Bean
Greeting koreanGreeting() {
return new KoreanGreeting();
}
}
(2) @Component
//TODO-3 eng profile 인 경우 빈이 활성화 된다.
@Profile("eng")
@Component
public class EnglishFarewell implements Farewell {
@Override
public void sayGoodBye() {
System.out.println("good bye");
}
}
요약
외부 구성
- Spring Boot 코드 중간에 외부 속성 값을 바인딩 할 수 있다.
(내부에 있는 값들을 환경변수라는 application.properties 값을 변경함으로써 바꿀 수 있다) - 바인딩 2가지 방법: @Value, @ConfigurationProperties
Profile
- Spring 에서는 profile을 이용해 상황에 따라서 다른 환경 변수를 보거나, 다른 방식으로 동작하게 할 수 있다.
Spel
- 런타임에서 객체에 대한 쿼리와 조작(querying and manipulating)을 지원하는 강력한 표현 언어.
- @Value에서 지원함.
yaml
- 데이터를 표현하기 위한 형식의 한 종류. (json과 같은 레이어)
출처: https://nhnacademy.dooray.com/share/pages/ABFgz8KgRv62A85x9KClUw
Spring Boot Core
nhnacademy.dooray.com
'Spring' 카테고리의 다른 글
[Spring Boot] 01. Spring Boot Core (3) - AOP(관점 지향 프로그래밍) * (0) | 2024.11.08 |
---|---|
[Spring Boot] @ConfigurationProperties 활성화시 @Profile 무시 에러 (0) | 2024.11.08 |
[Spring Boot] Lombok (0) | 2024.11.06 |
[Spring Boot] 01. Spring Boot Core (1) - Spring Boot 소개, IoC, DI (3) | 2024.11.05 |
[Spring Boot] 디자인 패턴 (0) | 2024.11.04 |