이전에는 웹 애플리케이션 서버에 WAR 파일을 배포하는 방식이었는데, WAR 배포 방식의 단점을 해결하기 위해 내장 톰캣 기능을 제공한다.
public class EmbedTomcatServletMain {
public static void main(String[] args) throws LifecycleException {
System.out.println("EmbedTomcatServletMain.main");
// 톰캣 설정
Tomcat tomcat = new Tomcat();
Connector connector = new Connector();
connector.setPort(8080);
tomcat.setConnector(connector);
//서블릿 등록
Context context = tomcat.addContext("", "/");
tomcat.addServlet("", "helloServlet", new HelloServlet());
context.addServletMappingDecoded("/hello-servlet", "helloServlet");
tomcat.start();
}
// 톰캣 설정
Tomcat tomcat = new Tomcat();
Connector connector = new Connector();
connector.setPort(8080);
tomcat.setConnector(connector);
// 스프링 컨테이너 생성
AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext();
appContext.register(HelloConfig.class);
// 스프링 MVC 디스패처 서블릿 생성, 스프링 컨테이너 연결
DispatcherServlet dispatcher = new DispatcherServlet(appContext);
// 디스패처 서블릿 등록
Context context = tomcat.addContext("", "/");
tomcat.addServlet("", "dispatcher", dispatcher);
tomcat.start();
서블릿 컨테이너 초기화와 거의 유사한데, 메인 메서드를 직접 실행하는지 서블릿 컨테이너가 제공하는 초기화 메서드를 통해서 실행하는지의 차이점만 있다.
자바의 메인 메서드를 실행해서 웹 애플리케이현을 실행하려면 jar 형식으로 빌드해야 하고, jar 안에는 'META-INF/MANIFEST.MF'파일에 실행할 메인 메서드의 클래스를 지정해줘야 한다.
build.gradle에 아래와 같이 설정을 해두면 gradle이 빌드 시 MANIFEST 파일을 자동으로 생성해준다.
//일반 Jar 생성
task buildJar(type: Jar) {
manifest {
attributes 'Main-Class': 'hello.embed.EmbedTomcatSpringMain'
}
with jar
}
그런데 jar 파일을 압축을 풀어서 보면, 라이브러리 역할을 하는 jar 파일이 없는 것을 알 수 있다. war 파일의 경우 lib 폴더에 라이브러리 역할을 하는 jar 파일이 포함되어 있었다. war는 was 위에서만 실행할 수 있으므로 war를 사용할수도 없기 때문에, 대안으로 FatJar를 사용한다.
jar 안에는 jar를 포함할 수 없지만, 클래스를 포함할 수는 있기 때문에 라이브러리에 사용되는 jar의 압축을 풀어서 나오는 class를 jar 파일에 포함시키는 것이다.
//Fat Jar 생성
task buildFatJar(type: Jar) {
manifest {
attributes 'Main-Class': 'hello.embed.EmbedTomcatSpringMain'
}
duplicatesStrategy = DuplicatesStrategy.WARN
from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } // 라이브러리 jar 파일에서 class 추출
with jar
}
장점
단점
FetJar의 단점을 해결하기 위해, 스프링 부트에서는 jar 내부에 jar를 포함할 수 있는 특별한 구조의 jar를 생성한다. 그리고 동시에 만든 jar를 내부 jar를 포함해서 실행할 수 있게 했다.
jar 내부에 또 jar를 포함하고 있기 때문에, 어떤 라이브러리가 포함되어 있는지 쉽게 확인할 수 있다.
a.jar, b.jar 와 같이 내부에 jar를 포함하므로 같은 경로의 파일이 있어도 둘 다 인식할 수 있다.
JarLuncher
실행 가능 Jar 내부에서는 메인메서드가 있는 BootAplication 클래스가 아니라 JarLuncher라는 전혀 다른 클래스를 실행한다. jar 내부에 jar를 읽어들이고, 특별한 구조에 맞게 클래스 정보를 읽어들이는 작업을 처리해준다. 처리 후에 Start-Class에 지정된 메인 메서드를 호출한다.
실행 과정 정리