org.apache.tomcat.embed:tomcat-embed-core
dependencies {
//스프링 MVC 추가
implementation 'org.springframework:spring-webmvc:6.0.9'
//내장 톰켓 추가
implementation 'org.apache.tomcat.embed:tomcat-embed-core:10.1.6'
}
//일반 Jar 생성
tasks.register('buildJar', Jar) {
manifest {
attributes 'Main-Class': 'hello.embed.EmbedTomcatSpringMain'
}
with jar
}
//Fat Jar 생성
tasks.register('buildFatJar', Jar) {
manifest {
attributes 'Main-Class': 'hello.embed.EmbedTomcatSpringMain'
}
duplicatesStrategy = DuplicatesStrategy.WARN
from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
public class EmbedTomcatServletMain {
public static void main(String[] args) throws LifecycleException {
log.info("EmbedTomcatServletMain.main");
// Tomcat 설정
Tomcat tomcat = new Tomcat();
Connector connector = new Connector();
connector.setPort(8080);
tomcat.setConnector(connector);
// Servlet 등록
Context context = tomcat.addContext("", "/");
tomcat.addServlet("", "helloServlet", new HelloServlet());
context.addServletMappingDecoded("/hello-servlet", "helloServlet");
tomcat.start();
}
}
public class EmbedTomcatSpringMain {
public static void main(String[] args) throws LifecycleException {
log.info("EmbedTomcatSpringMain.main");
// Tomcat 설정
Tomcat tomcat = new Tomcat();
Connector connector = new Connector();
connector.setPort(8080);
tomcat.setConnector(connector);
// Spring Container 생성
AnnotationConfigWebApplicationContext applicationContext
= new AnnotationConfigWebApplicationContext();
applicationContext.register(HelloConfig.class);
// DispatcherServlet 생성, Spring Container 연결
DispatcherServlet dispatcherServlet = new DispatcherServlet(applicationContext);
// Servlet 등록
Context context = tomcat.addContext("", "/");
tomcat.addServlet("", "dispatcher", dispatcherServlet);
context.addServletMappingDecoded("/", "dispatcher");
tomcat.start();
}
}
WebApplicationContext
public interface WebApplicationContext extends ApplicationContext {
String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
String SCOPE_REQUEST = "request";
String SCOPE_SESSION = "session";
String SCOPE_APPLICATION = "application";
String SERVLET_CONTEXT_BEAN_NAME = "servletContext";
String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters";
String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes";
@Nullable
ServletContext getServletContext();
}
./gradlew buildJar
로 빌드할 경우class
로 풀려있다.위 모든 과정을 편리하게 처리해주는 부트 클래스를 만들어 보자.
SpringApplication
public class MySpringApplication {
public static void run(Class<?> configClass, String[] args) {
// Tomcat 설정
Tomcat tomcat = new Tomcat();
Connector connector = new Connector();
connector.setPort(8080);
tomcat.setConnector(connector);
// Spring Container 생성
AnnotationConfigWebApplicationContext applicationContext
= new AnnotationConfigWebApplicationContext();
applicationContext.register(configClass);
// DispatcherServlet 생성, Spring Container 연결
DispatcherServlet dispatcherServlet = new DispatcherServlet(applicationContext);
// DispatcherServlet 등록
Context context = tomcat.addContext("", "/");
tomcat.addServlet("", "dispatcherServlet", dispatcherServlet);
context.addServletMappingDecoded("/", "dispatcherServlet");
try {
tomcat.start();
} catch (LifecycleException e) {
throw new RuntimeException(e);
}
}
}
@SpringBootApplication
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ComponentScan
public @interface MySpringBootApplication {
}
@MySpringBootApplication
public class MySpringBootAppMain {
public static void main(String[] args) {
MySpringApplication.run(MySpringBootAppMain.class, args);
}
}
@SpringBootApplication
public class BootApplication {
public static void main(String[] args) {
SpringApplication.run(BootApplication.class, args);
}
}
SpringBoot 에서 스프링 컨테이너를 생성하는 코드
class ServletWebServerApplicationContextFactory implements ApplicationContextFactory {
// ...
private ConfigurableApplicationContext createContext() {
if (!AotDetector.useGeneratedArtifacts()) {
return new AnnotationConfigServletWebServerApplicationContext();
}
return new ServletWebServerApplicationContext();
}
}
WebServerApplicationContext
public interface WebServerApplicationContext extends ApplicationContext {
WebServer getWebServer();
String getServerNamespace();
static boolean hasServerNamespace(ApplicationContext context, String serverNamespace) {
return (context instanceof WebServerApplicationContext) && ObjectUtils
.nullSafeEquals(((WebServerApplicationContext) context).getServerNamespace(), serverNamespace);
}
static String getServerNamespace(ApplicationContext context) {
return (context instanceof WebServerApplicationContext)
? ((WebServerApplicationContext) context).getServerNamespace() : null;
}
}
SpringBoot 내부에서 내장 톰캣을 생성하는 코드
public class TomcatServletWebServerFactory extends AbstractServletWebServerFactory
implements ConfigurableTomcatWebServerFactory, ResourceLoaderAware {
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
Tomcat tomcat = new Tomcat();
// ...
Connector connector = new Connector(this.protocol);
// ...
return getTomcatWebServer(tomcat);
}
}
META-INF
MANIFEST.MF
BOOT-INF
classes
: 우리가 개발한 class 파일과 resource 파일lib
: 외부 라이브러리classpath.idx
: 외부 라이브러리 경로layers.idx
: SpringBoot 구조 경로org.springframework.boot.loader
JarLauncher.class
: SpringBoot main() 실행 클래스
Main-Class
를 제외한 나머지 필드는 Java 표준이 아니다.
스프링 부트가 임의로 사용하는 정보이다.
Start-Class
Spring-Boot-Version
Spring-Boot-Classes
Spring-Boot-Lib
Spring-Boot-Classpath-Index
Spring-Boot-Layers-Index
Manifest-Version: 1.0
Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: hello.boot.BootApplication
Spring-Boot-Version: 3.0.2
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
Spring-Boot-Layers-Index: BOOT-INF/layers.idx
Build-Jdk-Spec: 17
- "BOOT-INF/lib/lombok-1.18.28.jar"
- "BOOT-INF/lib/spring-webmvc-6.0.4.jar"
- "BOOT-INF/lib/spring-web-6.0.4.jar"
...
- "dependencies":
- "BOOT-INF/lib/"
- "spring-boot-loader":
- "org/"
- "snapshot-dependencies":
- "application":
- "BOOT-INF/classes/"
- "BOOT-INF/classpath.idx"
- "BOOT-INF/layers.idx"
- "META-INF/"