Profile
Profile 정의(클래스)
package me.jinmin;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
@Configuration
@Profile("test") // **이 부분**
public class TestConfiguration {
@Bean
public BookRepository bookRepository(){
return new TestBookRepository();
}
}
위 클래스는 profile이 test일 경우에만 사용이 되는 빈 설정 파일이 된다.
package me.jinmin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
ApplicationContext context;
@Autowired
BookRepository bookRepository;
@Override
public void run(ApplicationArguments args) throws Exception {
Environment environment = context.getEnvironment();
System.out.println(Arrays.toString(environment.getActiveProfiles()));
System.out.println(Arrays.toString(environment.getDefaultProfiles()));
}
}
💥오류가 난다. Why? ⇒ profile이 test로 설정되지 않았기 때문에. 그렇다면 Profile을 설정해보자.
Profile 설정(선언)
IntelliJ 버전 별로 상이하게 설정!
공통(Ultimate, Community) : Edit Configurations... Click

아래의 이미지에서 !
Ultimate ver. ⇒ Active profiles:칸에 test입력
Community ver. ⇒ VM options:칸에 -Dspring.profiles.active="test"입력

AppRunner.class 구동 결과
print :
[test]
[default]
Profile 정의
@Configuration @Profile("test")@Component @Prfile("test")@Bean @Profile("test")Profile 표현식
! (not)
& (and)
| (or)
예제) (Profile 설정(-Dspring.profiles.action="test")을 지우고 App. 실행)
package me.jinmin;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Repository;
@Repository
@Profile("!test")
public class TestBookRepository implements BookRepository {
}
결과) Profile을 test로 설정하지 않았지만 @Profile("!test")으로 정의했기때문에 오류없이 실행됨을 볼 수 있다.
print:
[]
[default]
Profile의 쓰임새?
Property
우선 순위 (StandardServletEnvironment의 우선 순위)
기본적인 Property 설정과 접근
설정
VM options:에 -Dapp.name=spring5로 설정접근
(1)
package me.jinmin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
ApplicationContext context;
@Autowired
BookRepository bookRepository;
@Override
public void run(ApplicationArguments args) throws Exception {
Environment environment = context.getEnvironment();
System.out.println(environment.getProperty("app.name"));
}
}
print : spring5
(2)
\main\resources에 app.properties생성(일반 file) 후 "app.about=spring"입력
app.about=spring
@SpringBootApplication이 표시된 클래스에 @PropertySource("classpath:/app.properties") 추가
package me.jinmin;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.PropertySource;
@SpringBootApplication
@PropertySource("classpath:/app.properties")
public class demoIocApplication {
public static void main(String[] args) {
SpringApplication.run(demoIocApplication.class, args);
}
}
AppRunner.class 실행 및 결과
package me.jinmin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
ApplicationContext context;
@Autowired
BookRepository bookRepository;
@Override
public void run(ApplicationArguments args) throws Exception {
Environment environment = context.getEnvironment();
System.out.println(environment.getProperty("app.name"));
System.out.println(environment.getProperty("app.about"));
}
}
print:
spring5
spring
우선 순위?
app.properties의 내용을 "app.about=spring"에서 "app.name=spring"으로 변경한 후 실행하여 결과를 확인하자!
결과 (JVM 시스템 프로퍼티가 더 우선 순위가 높다.)
package me.jinmin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
ApplicationContext context;
@Autowired
BookRepository bookRepository;
@Override
public void run(ApplicationArguments args) throws Exception {
Environment environment = context.getEnvironment();
System.out.println(environment.getProperty("app.name"));
}
}
print:
spring5
또 다른 방법
package me.jinmin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
ApplicationContext context;
@Autowired
BookRepository bookRepository;
@Value("${app.name}")
String appName;
@Override
public void run(ApplicationArguments args) throws Exception {
Environment environment = context.getEnvironment();
System.out.println(appName);
}
}
print :
spring5
국제화 기능을 제공하는 인터페이스
사용법
main\resources에 세 가지를 추가하자
messages.properties (default)
"greeting=Hello {0}"
messages_ko_KR.properties (KOREA)
"greeting=안녕, {0}"
messages_en.properties (ENGLISH)
"greeting=Hello {0}"
package me.jinmin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Component;
import java.util.Locale;
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
MessageSource messageSource;
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println(messageSource.getMessage("greeting", new String[]{"jinmin"}, Locale.ENGLISH));
System.out.println(messageSource.getMessage("greeting", new String[]{"jinmin"}, Locale.KOREA));
System.out.println(messageSource.getMessage("greeting", new String[]{"jinmin"}, Locale.getDefault()));
}
}
결과(default의 값이 한글로 나오는 경우는 os의 디폴트)

✨(참고) 릴로딩 기능이 있는 MessageSource 활용
package me.jinmin;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
@SpringBootApplication
public class demoIocApplication {
public static void main(String[] args) {
SpringApplication.run(demoIocApplication.class, args);
}
@Bean
public MessageSource messageSource(){
var messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:/messages");
messageSource.setDefaultEncoding("UTF-8");
messageSource.setCacheSeconds(3);
return messageSource;
}
}
package me.jinmin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Component;
import java.util.Locale;
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
MessageSource messageSource;
@Override
public void run(ApplicationArguments args) throws Exception {
while(true){
System.out.println(messageSource.getMessage("greeting", new String[]{"jinmin"}, Locale.ENGLISH));
System.out.println(messageSource.getMessage("greeting", new String[]{"jinmin"}, Locale.KOREA));
System.out.println(messageSource.getMessage("greeting", new String[]{"jinmin"}, Locale.getDefault()));
Thread.sleep(1000L);
}
}
}
1초마다 매번 (1)의 결과가 나오며, messages.properties의 변경 내용을 Real-time으로 변경하고 Build할 경우, (2)의 내용이 나온다.
(1)

(2)

이벤트 기반 프로그래밍에 필요한 인터페이스 제공. 옵저버 패턴 구현체
(1) 4.2 이전🎆
Event 형성
main\java\me\..\MyEvent
package me.jinmin;
import org.springframework.context.ApplicationEvent;
public class MyEvent extends ApplicationEvent {
private int data;
public MyEvent(Object source) {
super(source);
}
public MyEvent(Object source, int data) {
super(source);
this.data = data;
}
public int getData() {
return data;
}
}
Event 처리
\main\java\me\..\MyEventHandler
package me.jinmin;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Component
public class MyEventHandler implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent event) {
System.out.println("이벤트를 받았다. 데이터는 : " + event.getData());
}
}
Event 출판(publish, 또는 발생) & 실행 결과
main\java\me\AppRunner
package me.jinmin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
ApplicationEventPublisher applicationEventPublisher; // **이 부분**
@Override
public void run(ApplicationArguments args) throws Exception {
applicationEventPublisher.publishEvent(new MyEvent(this, 100));
}
}
print:
이벤트를 받았다. 데이터는 : 100
순서
MyEventHandler)이 받아서 데이터 출력(2) 4.2 이후🎇 (ApplicationEvent 상속 x)
Event 형성
\main\java\me\..\MyEvent
package me.jinmin;
public class MyEvent {
private int data;
private Object source;
public MyEvent(Object source, int data) {
this.source = source;
this.data = data;
}
public Object getSource() {
return source;
}
public int getData() {
return data;
}
}
Event 처리
\main\java\me\..\MyEventHandler
package me.jinmin;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class MyEventHandler {
@EventListener //중요
public void handle(MyEvent event) {
System.out.println("이벤트를 받았다. 데이터는 : " + event.getData());
}
}
AppRunner 실행 결과는 4.2 이전과 같다.
복수 개의 Event(Handler) 처리
MyEvnetHandler
package me.jinmin;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class MyEventHandler {
@EventListener
public void handle(MyEvent event) {
System.out.println(Thread.currentThread().toString());
System.out.println("이벤트를 받았다. 데이터는 : " + event.getData());
}
}
AnotherHandler
package me.jinmin;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class AnotherHandler {
@EventListener
public void handle(MyEvent myEvent){
System.out.println(Thread.currentThread().toString());
System.out.println("AnotherHandler " + myEvent.getData());
}
}
AppRunner.class 구동 결과
print:
Thread[main,5,main]
AnotherHandler 100
Thread[main,5,main]
이벤트를 받았다. 데이터는 : 100
⇒ 같은 쓰레드에서의 진행과 같은 값.
(1) 순서는 랜덤인지 정해진 순서가 있는지 모른다. ⚾But, 정해줄 수는 있다.
⇒ @Order
- 각 Handler에 @EventListener 아래 @Order(Ordered.HIGHEST_PRECEDENCE) 와 @Order(Ordered.HIGHEST_PRECEDENCE + 2) 를 추가하면 +2를 한 EventHandler가 나중에 발생한다.
(2) 비동기로 실행하고 싶다?
구동 Class의 @SpringBootApplication아래 @EnableAsync를 추가하고 + 각 Handler에 @EventListener 아래 @Async를 추가. (순서 보장 x)
‼비동기로 이벤트를 발생기키면 Thread 별로 발생하기 때문에 Thread 스케줄링에 따라 순서가 정해지고 @Order는 소용없게 된다.
결과
print:
Thread[task-2,5,main]
이벤트를 받았다. 데이터는 : 100
Thread[task-1,5,main]
AnotherHandler 100
Spring이 제공하는 기본 이벤트
ContextRefreshedEvent: ApplicationContext를 초기화 했거나 리프래시 했을 때 발생.
ContextStartedEvent: ApplicationContext를 start()하여 라이프사이클 빈들이 시작
신호를 받은 시점에 발생.
ContextStoppedEvent: ApplicationContext를 stop()하여 라이프사이클 빈들이 정지
신호를 받은 시점에 발생.
ContextClosedEvent: ApplicationContext를 close()하여 싱글톤 빈 소멸되는 시점에
발생.
RequestHandledEvent: HTTP 요청을 처리했을 때 발생.
사용법
package me.jinmin;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
public class MyEventHandler {
@EventListener
@Async
public void handle(MyEvent event) {
System.out.println(Thread.currentThread().toString());
System.out.println("이벤트를 받았다. 데이터는 : " + event.getData());
}
@EventListener
@Async
public void handle(ContextRefreshedEvent event) {
System.out.println(Thread.currentThread().toString());
System.out.println("ContextRefreshedEvent");
var applicationContext = event.getApplicationContext();
}
@EventListener
@Async
public void handle(ContextClosedEvent event) {
System.out.println(Thread.currentThread().toString());
System.out.println("ContextRefreshedEvent");
var applicationContext = event.getApplicationContext();
}
}
리소스를 읽어오는 기능을 제공하는 인터페이스
\main\java\me...\AppRunner
package me.jinmin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;
import java.nio.file.Files;
import java.nio.file.Path;
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
ResourceLoader resourceLoader;
@Override
public void run(ApplicationArguments args) throws Exception {
Resource resource = resourceLoader.getResource("classpath:test.txt");
System.out.println(resource.exists());
System.out.println(resource.getDescription());
System.out.println(Files.readString(Path.of(resource.getURI())));
}
}
\resources\test.txt ⇒ "Hello Spring"
결과
