You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// 스프링 부트 내부의 스프링 컨테이너를 생성하는 코드 부분
class ServletWebServerApplicationContextFactory implements ApplicationContextFactory {
private ConfigurableApplicationContext createContext() {
if (!AotDetector.useGeneratedArtifacts()) {
return new AnnotationConfigServletWebServerApplicationContext();
}
return new ServletWebServerApplicationContext();
}
}
이후 TomcatServletWebServerFactory에서 Tomcat을 생성하고 컨테이너를 연결함
public class TomcatServletWebServerFactory extends AbstractServletWebServerFactory implements ConfigurableTomcatWebServerFactory, ResourceLoaderAware { @Override
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
for (LifecycleListener listener : getDefaultServerLifecycleListeners()) {
tomcat.getServer().addLifecycleListener(listener);
}
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
registerConnectorExecutor(tomcat, connector);
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
registerConnectorExecutor(tomcat, additionalConnector);
}
prepareContext(tomcat.getHost(), initializers);
return getTomcatWebServer(tomcat);
}
}
// prepareContext 내부의 TomcatStarter
@Override
public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
try {
for (ServletContextInitializer initializer : this.initializers) {
initializer.onStartup(servletContext);
}
}
내부적으로(ie. TomcatStarter) 내장 톰캣에 디스패처 서블릿을 등록하고 스프링 컨테이너와 연결해서 동작할 수 있도록 함
스프링 부트와 웹 서버 - 빌드와 배포
gradle clean build 명령을 통해서 프로젝트를 빌드해서 jar파일을 생성할 수 있음
이렇게 생성된 Jar파일의 내부는 다음과 같음
boot-0.0.1-SNAPSHOT.jar
META-INF
MANIFEST.MF
org/springframework/boot/loader
JarLauncer.class → 스프링 부트 main() 실행 클래스
BOOT-INF
classes
hello/boot/BootApplication.class
hello/boot/controller/HelloController.class
lib
spring-webmvc-6.0.4.jar
tomcat-embed-core-10.1.5.jar
…
classpath.idx → 외부 라이브러리 경로
layers.idx → 스프링 부트 구조 경로
Jar내부에 Jar를 담아서 인식하는 것이 불가능한데, Jar가 포함되어있고 인식까지 됨.
스프링 부트 실행 가능 Jar
FatJar는 하나의 Jar파일에 라이브러리의 모든 클래스와 리소스를 포함했음. 그래서 실행에 필요한 모든 내용을 하나의 JAR로 만들어서 배포하는 것이 가능했음. 하지만 FatJar는 다음의 문제를 지녔었음
어떤 라이브러리가 포함되어 있는지 확인이 어려움
파일명 중복을 해결할 수 없음
스프링부트는 이러한 문제를 해결하기 위해 Executable Jar를 통해 jar 내부에 jar를 포함할 수 있는 특별한 구조로 만들고, 동시에 만든 jar 내부에 jar를 포함해서 실행할 수 있도록 함.
Jar 내부에 Jar를 포함하기에 어떤 라이브러리가 포함되어 있는지 확인 가능
Jar 내부에 Jar를 포함하기때문에 a.jar, b.jar 내부에 같은 경로의 파일이 있더라도 모두 인식 가능함
Executable Jar 실행 과정
java -jar xxx.jar 실행
META-INF/MANIFEST.MF파일 로드
JarLaucner.main() 실행
BOOT-INF/classes/* 인식
BOOT-INF/lib/* 인식
Main-Class를 읽어서 main() 메소드 실행
Main-Class
기존 코드에 있는 main()메소드가 존재하는 클래스가 아니라 JarLauncer 클래스가 main()을 실행함
JarLauncer는 스프링 부트가 빌드 시에 주입함. org/springframework/boot/loader/JarLauncer에 포함됨
스프링 부트는 jar 내부에 jar를 읽어들이는 기능이 필요한데, 특별한 구조에 맞게 클래스 정보를 읽어들여야 함 → JarLauncer의 역할
Start-Class
코드로 작성했던 SpringBootApplication어노테이션이 붙은 main()메소드의 클래스
이외 정보
Spring-Boot-Version
스프링 부트 버전
Spring-Boot-Classes
개발한 클래스 경로
Spring-Boot-Lib
라이브러리 경로
Spring-Boot-Classpath-Index
외부 라이브러리 모음
Spring-Boot-Layers-Index
스프링 부트 구조 정보
스프링 부트 로더
org/springframework/boot/loader 하위에 JarLauncher를 포함한 스프링 부트가 제공하는 ExecuatbleJar를 실제 구동시키는 클래스들이 포함됨.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
스프링 부트와 내장 톰캣
WAR 배포 방식의 단점
톰캣 같은 WAS를 별도로 설치해야함
개발 환경 설정이 복잡함
배포과정이 복잡해짐
톰캣 버전 변경 시 톰캣 재설치 필요
내장 톰캣의 사용
embeded tomcat을 활용하여 직접 토캣을 생성하고 설정, 실행하는 코드
내장 톰캣 빌드와 배포
자바의 main() 메소드를 실행하기 위해서 jar 형식으로 빌드해야함
그리고 jar 안에는 META-INF/MANIFEST.MF파일에 실행할 main()메소드의 클래스를 지정해줘야함
gradle을 통하면 다음과 같이 설정할 수 있음
위의 gradle설정을 통해서 buildJar을 실행할 경우, springframework를 찾을 수 없다는 에러가 발생함
Unable to initilize main class hello.embed.EmbedTocatSpringMain Caused by ...jar파일을 풀어서 내부를 확인해보면, 코드로 생성한 클래스파일은 존재하지만, 의존성 라이브러리과 내장톰캣 라이브러리들은 포함되어있지않음.
WAR를 풀어보았을땐
💡/classes하위에 코드의 클래스 파일이 존재하고,/lib하위에 의존 라이브러리 jar이 존재함Jar 파일은 Jar파일을 포함할 수 없음
WAR와 다르게 JAR 파일은 내부 라이브러리 역할을 하는 JAR파일을 포함할 수 없음. 포함한다고 해도 인식이 안됨. 이것이 JAR파일 스펙의 한계임. 그렇다고 WAR를 사용할 수도 없으며, WAR는 웹 어플리케이션 서버(WAS) 위에서만 실행할 수 있음
대안으로 라이브러리 JAR파일을 모두 구해서 MANIFEST 파일에 해당 경로를 적어주면 인식이 되지만 번거롭다. JAR 파일 안에 JAR 파일을 포함할 수 없기떄문에 라이브러리 역할을 하는 JAR파일도 함께 가지고 다녀야함
FatJar
위의 문제상황의 대안으로 fat jar 또는 uber jar 방법이 존재함
Jar 안에는 Jar를 퐇마할 수 없음. 하지만 클래스는 포함될 수 있음
라이브러리에 사용되는 Jar파일을 풀면 class들이 나오며, class를 뽑아서 새로 만드는 jar에 포함하는 것.
이렇게 수 많은 의존 라이브러리에서 나오는 class때문에 fat jar가 탄생함
어떤 라이브러리가 포함되어있는지 확인이 어려움
파일명 중복을 해결할 수 없음
클래스나 리소스 명이 같은 경우, 하나를 포기해야함.
jakarta.servlet.ServletContainerInitializer이 파일이 여러 라이브러리에 있을 수 있으며, 이런 경우 나머지 하나는 포함되지 않으므로 정상 동작이 안됨
부트 클래스
configClass: 스프링 설정을 파라미터로 전달받음
args: main(args)를 전달 받아서 사용함
@componentscan을 통해서 해당 패키지 하위의 모든 경로의 빈을 등록함
스프링 부트와 웹 서버
spring-boot-starter-web 의존성을 추가하게 되면 spring-boot-start-tomcat 등 관련된 라이브러리가 함께 추가됨
스프링 부트의 실행 과정
스프링 부트를 실행할 때는 자바 main()메소드에서 SpringApplication.run()을 호출함
여기에 메인 정보를 넘겨주는데, 보통 @SpringBootApplication어노테이션이 있는 현재 클래스로 설정
@SpringBootApplication 어노테이션의 ComponentScan을 통해서 현재 패키지와 하위 모든 패키지의 컴포넌트를 스캔함
run()메소드를 호출함으로써 실행되는 작업은 다음과 같음
이후 TomcatServletWebServerFactory에서 Tomcat을 생성하고 컨테이너를 연결함
내부적으로(ie. TomcatStarter) 내장 톰캣에 디스패처 서블릿을 등록하고 스프링 컨테이너와 연결해서 동작할 수 있도록 함
스프링 부트와 웹 서버 - 빌드와 배포
gradle clean build 명령을 통해서 프로젝트를 빌드해서 jar파일을 생성할 수 있음 이렇게 생성된 Jar파일의 내부는 다음과 같음
Jar내부에 Jar를 담아서 인식하는 것이 불가능한데, Jar가 포함되어있고 인식까지 됨.
스프링 부트 실행 가능 Jar
FatJar는 하나의 Jar파일에 라이브러리의 모든 클래스와 리소스를 포함했음. 그래서 실행에 필요한 모든 내용을 하나의 JAR로 만들어서 배포하는 것이 가능했음. 하지만 FatJar는 다음의 문제를 지녔었음
스프링부트는 이러한 문제를 해결하기 위해
Executable Jar를 통해 jar 내부에 jar를 포함할 수 있는 특별한 구조로 만들고, 동시에 만든 jar 내부에 jar를 포함해서 실행할 수 있도록 함.Executable Jar실행 과정Main-Class
기존 코드에 있는 main()메소드가 존재하는 클래스가 아니라 JarLauncer 클래스가 main()을 실행함 JarLauncer는 스프링 부트가 빌드 시에 주입함. org/springframework/boot/loader/JarLauncer에 포함됨 스프링 부트는 jar 내부에 jar를 읽어들이는 기능이 필요한데, 특별한 구조에 맞게 클래스 정보를 읽어들여야 함 → JarLauncer의 역할
Start-Class
코드로 작성했던 SpringBootApplication어노테이션이 붙은 main()메소드의 클래스
이외 정보
스프링 부트 로더
org/springframework/boot/loader 하위에 JarLauncher를 포함한 스프링 부트가 제공하는 ExecuatbleJar를 실제 구동시키는 클래스들이 포함됨.
BOOT-INF
classes: 개발한 class 파일과 리소스 파일
lib: 외부 라이브러리
classpath.idx: 외부 라이브러리 모음
layers.idx: 스프링 부트 구조 정보
Beta Was this translation helpful? Give feedback.
All reactions