최근 cli로 된 어플리케이션을 개발하였다.
어플리케이션 실행 후 사용할 DB 정보를 입력을 통하여 받고 싶었으나 Spring의 경우 DataSource가 우선 생성되어야 어플리케이션이 실행될 수 있었다.
따라서 Spring 구동 이전의 main 메소드에서 DB 정보를 받아오도록 아래와 같이 진행하였다.
public static void main(String[] args) {
try {
br = new BufferedReader(new InputStreamReader(System.in));
String dbUrl = validDBURL(); // IP PORT 연결 테스트
String schema = validInputString("DB 스키마");
jdbcUrl = "jdbc:mysql://" + dbUrl + "/" + schema;
dbUser = validInputString("DB 계정");
dbPassword = validInputString("DB 패스워드");
new SpringApplicationBuilder(MainDoubleApplication.class)
.sources(DataSourceConfig.class)
.run(args);
} catch (Exception e) {
System.err.println("Error: " + e.getMessage() + e);
}
}
허나 해당 어플리케이션을 실행해보니 Main 메소드의 내용이 2번 실행 후 Spring이 실행되었다.
검색을 해보니 매번 아무생각없이 추가한 spring-boot-devtools로 인해 main이 두번 실행되고 있던 것이었다.
해당 의존성을 제거하니 정상적으로 Main 메소드 한번 실행 후 Spring이 실행되었다.
왜?
그렇다면 spring-boot-devtools는 왜 main 메소드를 두번 실행할까?
이는 devtools의 핵심 기능인 Automatic Restart 기능을 위해서이다.
※ Automatic Restart - 서비스 기동 중 코드 변경사항 반영
devtools 공식 문서를 보면 이렇게 나와있다.
정리하자면 아래와 같다.
devtools 사용 시 두 개의 클래스 로더를 사용하여 어플리케이션이 기동된다.
base 클래스로더 - 변경이 발생하지 않는 클래스 관리 (ex. 외부 jar파일)
restart 클래스로더 - 개발을 진행하면서 변경되는 클래스 관리
이후 클래스 변경이 일어나면 기존 restart 클래스로더가 변경된 클래스를 반영한 후 소멸된다. 그 후 새 restart 클래스로더가 생성된다.
이렇게 하면 base 클래스로더에서 관리하는 변경사항이 발생하지 않는 클래스들은 다시 로딩하지 않으므로 restart 시간이 줄어든다.
따라서 빠른 Automatic Restart 진행을 위해 어플리케이션 기동 시 두 개의 클래스 로더를 생성하고 이로 인해 main 메소드가 두번 실행되는 것이다.
테스트
main 메소드가 실행되는 것을 보기 위해 실행 시 "Main Method Run"을 출력하도록 해두었고
@SpringBootApplication
public class MainDoubleApplication {
public static void main(String[] args) {
System.out.println("================== Main Method Run ==================");
new SpringApplicationBuilder(MainDoubleApplication.class)
.run(args);
}
}
Controller에서는 "/test" path호출 시 before change를 반환하도록 해두었다.
@RestController
public class TestController {
@GetMapping("/test")
public String test() {
return "before change";
}
}
초기 실행 후 "/test" 호출 시는 결과는 아래와 같았다.
이후 Controller에서 반환하는 내용을 변경하고 기다려 보니 console 창에는 아래와 같은 로그가 발생하였다.
Main Method Run이 출력되는 걸 보면 새 restart 클래스로더를 생성하기 위해 main 메소드가 다시 실행되었다는 것을 알 수 있었고
이후 "/test"를 호출해보니 Controller의 변경된 내용이 반영되어있었다.
결론
spring-boot-devtools에서는 빠른 Automatic Restart 진행을 위해 어플리케이션 기동 시 두 개의 클래스 로더를 생성한다.
클래스 로더 생성 시 main 메소드를 실행해야하고 이로 인해 main 메소드가 두 번 실행되는 것이다.
'BackEnd > Spring' 카테고리의 다른 글
어노테이션은 사실 깡통이다. (0) | 2024.09.22 |
---|---|
[Spring MVC] Filter에서 파라미터 처리하는 방법 (0) | 2024.08.01 |
gradle 프로젝트가 build 안될 때 - java.lang.IllegalStateException: Module entity with name (3) | 2023.05.16 |
Spring AntPathMatcher URL 체크 시 OR 조건 매핑 (0) | 2023.03.02 |