참고
웹 서버와 스프링 부트 소개
NOTE
스프링 부트는 어떤 원리로 내장 톰캣을 사용해서 실행할 수 있는것인가?
최근버전인 JAR 대신, WAR을 어떻게 하는지 알아보자
WAR vs JAR
NOTE
WAR(Web Application Archive)
기본 구조
•
서버에 배포할 떄 사용하는 파일이다.
•
jar파일이 JVM위에서 실행되면 WAR는 웹 애플리케이션 서버 위에서 실행된다.
•
서버위에서 실행되고, HTML과 같은 정적리소스 클래스 파일을 모두 포함하기에 구조가 JAR에 비해 복잡하다.
JAR(Java Archive)
•
여러 클래스와 리소스를 압축파일로 만드는 방식이다.
•
이 파일은 JVM위에서 직접 실행되거나 다른 곳에서 사용하는 라이브러리로 제공된다.
•
직접 실행하는 경우 main()이 필요하고, MANIFEST.MF 파일에 실행할 메인 메서드가 있는 클래스를 지정한다.
•
실행 예) java -jar [.jar 파일이름]
톰캣 설치하고 사용하기
NOTE
# 톰캣 다운로드 이후 /bin경로로 이동
chmode 755 *
./startup.bat # 톰캣실행
./shutdown.bat # 톰캣종료
netstat -ano | findstr :포트번호 # 8080포트 찾기
taskkill /f /pid 프로세스번호 # 8080 포트 프로세스 죽이기
Bash
복사
톰캣 관련 명령어
@WebServlet(urlPatterns = "/test")
public class TestServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("TestServlet.service");
resp.getWriter().println("test");
}
}
Java
복사
서버에 배포할 Servlet 코드
# war 파일생성
./gradlew build
cd ./build/libs
ls
# 압축해제
jar -xvf server-0.0.1-SNAPSHOT.war
Bash
복사
build 파일 생성하고 압축해제
war 파일생성
압축해제 (우리가 생성한 Servlet과 index.html이 들어감)
톰캣 사용(war 파일 배포)
war파일을 /webapp 경로에 넣은이후 톰캣실행
실행하면 자동으로 압축해제 함
살행결과
인텔리제이에 톰캣 연결(톰캣을 인텔리제이에서 실행함)
ServletContainerInitializer, @HandlesTypes()
NOTE
WAS는 실행하는 시점에 필요한 초기화 작업이 존재한다!
대표적으로 서블릿 컨테이너 초기화, 애플리케이션 초기화가 존재한다.
서블릿 컨테이너 초기화
public class MyContainerInitV1 implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
System.out.println("MyContainerInitV1.onStartup");
System.out.println("c = " + c);
System.out.println("ctx = " + ctx);
}
}
Java
복사
ServletContainerInitializer를 통해서 가능
hello.container.MyContainerInitV1
hello.container.MyContainerInitV2
Java
복사
/resources/META-INF/services \
/jakarta.servlet.ServletContainerInitializer (경로)
애플리케이션 초기화
@HandlesTypes(AppInit.class)
public class MyContainerInitV2 implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
System.out.println("MyContainerInitV2.onStartup");
System.out.println("c = " + c);
System.out.println("ctx = " + ctx);
for (Class<?> appInitClass : c) {
try {
// new AppInitV1Servlet()과 동일한 코드
AppInit appInit = (AppInit) appInitClass.getDeclaredConstructor().newInstance();
appInit.onStartUp(ctx);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
Java
복사
@HandlesTypes 추가
public class AppInitV1Servlet implements AppInit {
@Override
public void onStartUp(ServletContext servletContext) {
// 순수 서블릿 코드 등록
ServletRegistration.Dynamic helloServlet = servletContext.addServlet("helloServlet", new HelloServlet());
helloServlet.addMapping("/heelo-servlet");
}
}
Java
복사
순수 서블릿 코드 등록 (@WebServlet 방법이외에도 이렇게 프로그래밍으로 등록가능)
1.
@HandlersTypes 애노테이션에 애플리케이션 초기화 인터페이스 지정
2.
서블릿 컨테이너 초기화는 파라미터로 넘어오는 c에 초기화 인터페이스 구현체들을 모두 찾아서 전달한다.
3.
appInitClass.getDeclaredConstructor().newInstance()
•
리플렉션을 통해 객체를 생성한다.
4.
appInit.onStartUp(ctx)
•
애플리케이션 초기화 코드를 직접 실행하면서 서블릿 컨테이너 정보가 담긴 ctx도 전달
스프링 컨테이너 등록
NOTE
앞서 사용한 서블릿 컨테이너/애플리케이션 초기화를 응용해 WAS와 스프링을 통합한다!
// Spring MVC 추가
implementation 'org.springframework:spring-webmvc:6.0.4'
Java
복사
다음 과정이 필요하다
1. 스프링 컨테이너 만들기
2. 스프링 MVC컨트롤러 빈 등록
3. 디스패쳐 서블릿 서블릿컨테이너에 등록
public class AppInitV2Spring implements AppInit {
@Override
public void onStartUp(ServletContext servletContext) {
System.out.println("AppInitV2Spring.onStartUp");
// 스프링 컨테이너 생성
AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext();
appContext.register(HelloConfig.class);
// 스프링 MVC 디스패쳐 서블릿 생성, 스프링 컨테이너 연결
DispatcherServlet dispatcher = new DispatcherServlet(appContext);
// 디스패쳐 서블릿을 서블릿 컨테이너에 등록
ServletRegistration.Dynamic servlet = servletContext.addServlet("dispatcherV2", dispatcher);
// /spring/* 요청이 디스패쳐 서블릿을 통함
servlet.addMapping("/spring/*");
}
}
Java
복사
애플리케이션 초기화 방식 응용
최종 그림
디스패쳐 서블릿이 정상적으로 동작함!
WebApplicationInitializer
NOTE
기존의 초기화 방식을 직접 구현하지 않고 간단한 인터페이스 구현만으로 스프링이 지원한다!
public class AppInitV3Spring implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
System.out.println("AppInitV3Spring.onStartUp");
// 스프링 컨테이너 생성
AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext();
appContext.register(HelloConfig.class);
// 스프링 MVC 디스패쳐 서블릿 생성, 스프링 컨테이너 연결
DispatcherServlet dispatcher = new DispatcherServlet(appContext);
// 디스패쳐 서블릿을 서블릿 컨테이너에 등록
ServletRegistration.Dynamic servlet = servletContext.addServlet("dispatcherV3", dispatcher);
// 모든 요청이 디스패쳐 서블릿을 통함
servlet.addMapping("/");
}
}
Java
복사
ServletContainerInitializer, @HandlerTypes 을 구현하지 않고
WebApplicationInitializer만 구현하면 된다.
초록색 영역은 이미 스프링이 만들어서 제공하는 영역
경로의 우선순위는 구체적인 것이 먼저 실행된다.
•
일반적으로는 스프링 컨테이너 하나, 디스패쳐 서블릿도 하나만 만든다.
•
디스패쳐 서블릿의 경로 매핑도 /로 해서 하나로 진행한다.