[Spring Boot] 3. 스프링부트 핵심기능

SpringApplication

스프링 부트 어플리케이션 대해서 좀더 자세히 들어가본다.

지금까지 스프링 어플리케이션 예제는 다음과 같이 간단히 실행하고 있었다.

Application1.java
1
2
3
4
5
6
7
@SpringBootApplication
public class SpringbootApplicationtestApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootApplicationtestApplication.class, args);
}
}

이렇게 실행하면 스프링 부트가 제공하는
다양항 커스터마이징 기능을 사용하기 어렵다.

Application1.java
1
2
3
4
5
6
7
8
9
@SpringBootApplication
public class SpringbootApplicationtestApplication {
public static void main(String[] args) {
//SpringApplication.run(SpringbootApplicationtestApplication.class, args);
SpringApplication app = new SpringApplication(SpringbootApplicationtestApplication.class);
app.run(args);
}
}

SpringApplication의 인스턴스를 만들어서 실행하였다.
위와 결과는 동일하지만 인스턴스에 커스터마이징을 할 수 있게 되었다.

기본로그 레벨 = INFO

아무 설정 없을때 기본 로그 레벨은 INFO레벨이다.
만약 디버그 모드로 어플리케이션을 실행하게 되면 로그가 DEBUG레벨로
찍히게 되는데 특이한점은 자동설정 로그를 보면 어떤 것은 적용되고
어떤것은 왜 적용이 안되었는지 로그를 통해 확인할 수 있다.

FailureAnalyzer

한마디로 말해서 에러가 났을때 에러를 좀 더 내가 원하는대로 이쁘게
출력할 수 있는 기능이다.

기본적으로 스프링 부트는 여러 FailureAnalyzer를 제공하고
개발자 스스로 만들 수 있지만 거의 그럴 확율은 없다.

  1. 기본
    처음에 나오는 스프링 로고 대신 다른 문구가 나오게 할 수 있다.
    classpath(보통 resource)에 banner.txt 파일을 만들고
    그안에 문구를 넣으면 스프링 로고 대신 그 문구가 출력 된다.
    txt가 아니라 png, gif, jpg도 가능하다.
    특정 위치라면 application.properties
    spring.banner.location를 사용해서 위치를 지정할 수 있다.

  2. 변수 사용
    ${spring-boot.version}등의 변수를 사용해서
    스프링 부트 버전 출력을 할 수도 있다. 다만 변수중에 MANIFEST.MF가 필요한
    변수들은 패키징후에 출력을 확인할 수 있다.

  3. Banner OFF
    배너 끄는 방법은 배너모드를 OFF 값을 주면 된다.

    SpringbootApplicationtestApplication.java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @SpringBootApplication
    public class SpringbootApplicationtestApplication {

    public static void main(String[] args) {

    SpringApplication app = new SpringApplication(SpringbootApplicationtestApplication.class);
    app.setBannerMode(Banner.Mode.OFF); //간단하다.
    app.run(args);
    }
    }
  4. Banner
    파일이 아닌 Banner클래스를 구현하면 코딩으로 배너 구현이 가능하다.

    SpringbootApplicationtestApplication.java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    @SpringBootApplication
    public class SpringbootApplicationtestApplication {
    public static void main(String[] args) {
    SpringApplication app = new SpringApplication(SpringbootApplicationtestApplication.class);
    app.setBanner(new Banner() {
    @Override
    public void printBanner(Environment environment, Class<?> sourceClass, PrintStream out) {
    System.out.println("============");
    System.out.println("====TEST===");
    System.out.println("============");
    }
    });
    app.run(args);
    }
    }

SpringApplicationBuilder

만약 ApplicationContext 계층을 만들거나
빌더 API등을 이미 사용하고 있다면 SpringApplicationBuilder를 사용하여
빌더 패턴이 사용 가능하다.

SpringbootApplicationtestApplication.java
1
2
3
4
5
6
7
8
@SpringBootApplication
public class SpringbootApplicationtestApplication {
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(SpringbootApplicationtestApplication.class)
.run(args);
}
}

더 자세한방법은 23.4 Fluent Builder API를 참조한다.

커스터마이징하기 위해선 위에서 처럼 new로 인스턴스화 하거나
빌더패턴을 이용해야한다. 그렇지 않고 run으로 바로 실행하면
커스터마이징이 불가능하다.

Application Events and Listener

참조 https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-spring-application.html#boot-features-application-events-and-listeners

스프링과 스프링 부트가 기본적으로 제공하는 이벤트 가 존재한다.
리스너를 사용하면 해당 이벤트가 발생했을때 동작하게 할 수 있다.

SampleListener.java
1
2
3
import org.springframework.context.ApplicationListener;
public class SampleListener implements ApplicationListener<event타입> {
}

예를들어 ApplicationStartingEvent 리스너를 구현한다고 하면

SampleListener.java
1
2
3
4
5
6
7
@Component
public class SampleListener implements ApplicationListener<ApplicationStartedEvent> {
@Override
public void onApplicationEvent(ApplicationStartedEvent applicationStartedEvent) {
System.out.println("started...");
}
}

리스너를 빈으로 등록하면 등록되어있는 빈중에 해당하는 리스너는 알아서 시작해 준다.

ApplicationContext를 만들기 전의 이벤트 리스너는 @Bean으로 등록할 수 없다.

위의 ApplicationStartedEvent 이벤트는 서버 스타트 작업이 다 끝나고 발생하는
이벤트여서 정상 작동하지만 서버 스타트 시작시 발생하는
ApplicationStartingEvent 이벤트로 리스너를 바꿔보자
바꾼 ApplicationStartingEvent 리스너는 빈으로 등록되어도
아무것도 실행이 되지 않음을 알 수 있다.

어플리케이션 컨텍스트가 만들어진 것을 기준으로
만들어진 이후의 이벤트의 리스너는 그이벤트의 리스너가 빈인 경우
자동으로 호출이 되는 반면에 어플리케이션 컨텍스트 시작 전의
이벤트의 리스너인 경우에는 실행이 안된다.

즉 이와 같은 경우에는 리스너를 따로 등록을 해주어야 한다.

SpringbootApplicationtestApplication.java
1
2
3
4
5
6
7
8
@SpringBootApplication
public class SpringbootApplicationtestApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(SpringbootApplicationtestApplication.class);
app.addListeners(new SampleListener()); //등록하는 부분
app.run(args);
}
}

이 경우 빈 등록은 하지 않아도 정상 작동한다.

WebApplicationType 설정

참조 : https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-spring-application.html#boot-features-web-environment

SpringApplication은 개발자 대신 알맞는
ApplicationContext타입을 생성하려 한다..

WebApplicationType을 결정하는 알고리즘은 매우 간단하다.

  • Spring MVC 존재시
    • -> WebApplicationType.SERVLET
    • AnnotationConfigServletWebServerApplicationContext 사용
  • Spring MVC가 존재하지 않고 Spring WebFlux가 존재할 때
    • -> WebApplicationType.REACTIVE
    • AnnotationConfigReactiveWebServerApplicationContext 사용
  • 그외
    • WebApplicationType.NONE
    • AnnotationConfigApplicationContext 사용

이는 만약 개발자가 같은 어플리케이션에서 Spring MVC와 Spring WebFlux를
같이 사용하는 경우 Spring MVC가 기본적으로 사용된다는 뜻이다.

만약 둘다 사용중에 WebFlux를 사용을 하고 싶다면
setWebApplicationType(WebApplicationType)를 사용함으로써 이 기본값을
overriding 할 수 있다.
setApplicationContextClass(…​)를 호출 해서 ApplicationContext 타입을
완전하게 제어하는 것도 가능하다.

JUnit 테스트에서 SpringApplication을 사용할 때
setWebApplicationType (WebApplicationType.NONE)로 하는 것이
종종 바람직(desirable)하다.

어플리케이션 인자(argument) 사용하기

여기서 argument는 --로 들어오는 Program argument를 뜻한다.
-D로 들어오는것은 JVM 옵션이다. JVM 옵션언 어플리케이션 argument가 아니다.

-Dtest1 --test2를 주고 실행해서 테스트를 해보자

SpringbootApplicationtestApplication.java
1
2
3
4
5
6
7
@Component
public class AppArgumentTestComponent {
public AppArgumentTestComponent(ApplicationArguments arguments){
System.out.println("test1: " + arguments.containsOption("test1"));
System.out.println("test2: " + arguments.containsOption("test2"));
}
}

어떤 빈의 생성자가 1개이고 그 생성자의 파라미터가 빈일 경우 스프링이 알아서 주입한다.

이경우 test1:false, test2: true로 나온다.

어플리케이션 실행후 추가 작업 하는 방법

참조: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-spring-application.html#boot-features-command-line-runner

어플리케이션을 실행한 후 추가적으로 무언가 실행하고 싶을때
다음 인터페이스를 구현해서 가능하다.

  • ApplicationRunner(추천)
  • CommandLineRunner
    두 인터페이스 모두 동일한 방식으로 작동하며
    SpringApplication.run(…​)이 완료되기 직전에 호출되는
    단일 실행 메소드(single run method)를 지원한다.

둘의 차이는 인자 접근이다.
CommandLineRunner인터페이스는 어플리케이션 인자에 대한 접근을 간단한 문자열 배열
로 제공하는 반면, ApplicationRunner인터페이스는 위에서 봤던
ApplicationArguments인터페이스를 사용한다. ApplicationArguments인터페이스에
유용한 메소드가 많아서 low-level이 아닌 추상화 된 api사용을 할 수 있다.

CommandLineRunner인터페이스는 다음 예제처럼 구현된다.

CommandLineRunner 구현 테스트
1
2
3
4
5
6
7
@Component
public class AppArgumentTestComponent implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
Arrays.stream(args).forEach(System.out::println);
}
}

두 인터페이스 구현이 여러 개가 있으며 이를 특정한 순서에 따라서 실행하려면
org.springframework.core.Ordered인터페이스를 추가로 구현하거나
org.springframework.core.annotation.Order어노테이션을 사용한다.

@Order
1
2
3
4
5
@Component
@Order(1) // 숫자가 낮은게 높은 우선순위로 먼저 수행된다.
public class Append implements CommandLineRunner {
...
}

Related POST

공유하기