스프링 부트 - 핵심 원리와 활용 김영한님 강의 내용
JAR ( Java Archive )
여러 클래스와 리소스를 묶어서 JAR라는 압축 파일
을 만들 수 있다.WAR ( Web Application Archive )
WAR 구조
WEB-INF 폴더 하위는 클래스, 라이브러리, 설정 정보가 들어가는 곳이다.
제외한 영역은 정적 리소스 (HTML, CSS)가 사용되는 영역
톰켓에 직접 배포 방법
⇒ 해당 부분은 IDE를 통해 관리할 수 있음
스프링 컨테이너
를 만들고, 서블릿과 스프링을 연결하는 Dispatcher Servlet
도 등록해야 한다.서블릿 컨테이너 초기화
ServletContainerInitailizer
라는 초기화 인터페이스를 제공한다.onStartup()
메서드를 실행해 애플리케이션에 필요한 기능들을 초기화 하거나 등록할 수 있다.public interface ServletContainerInitializer {
public void onStartup(Set<Class<?>> c, ServletContext ctx)
throws ServletException;
}
@HandlesTypes
어노테이션과 함께 사용한다.resources/META-INF/services/jakarta.servlet.ServletContainerInitializer 파일에 hello.container.MyContainerInitV1 와 같이 클래스를 지정해주면,
WAS 실행 시 해당 클래스를 초기화 클래스로 인식하고 로딩 시점에 실행한다.
서블릿을 등록하는 방법
@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");
}
}
// HelloServlet 생성
public class HelloServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HelloServlet.service");
resp.getWriter().println("hello servlet!");
}
}
애플리케이션 초기화
public interface AppInit {
void onStartup(ServletContext servletContext);
}
AppInit 구현
public class AppInitV1Servlet implements AppInit{
@Override
public void onStartup(ServletContext servletContext) {
System.out.println("AppInitV1Servlet.onStartup");
// 순수 Servlet Code 등록
// '/hello-servlet'을 호출하면, helloServlet이 실행된다.
servletContext.addServlet("helloServlet", new HelloServlet())
.addMapping("/hello-servlet");
}
}
@WebServlet을 사용하면 어노테이션 하나로 서블릿을 편리하게 등록할 수 있다.
하지만, 유연하게 변경하는 것이 어렵다.
프로그래밍 방식은 특정 조건에 따라 if문으로 분기해서 서블릿을 등록하거나 뺄 수 있다.
또한, 서블릿을 직접 생성하기 때문에 생성자에 필요한 정보를 넘길 수 있다.
AppInit을 초기화 하는 방법
@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);
// class hello.container.AppInitV1Servlet
for (Class<?> appInitClass : c) {
try {
// Reflection 을 통해 객체 생성
AppInit appInit = (AppInit) appInitClass.getDeclaredConstructor()
.newInstance();
appInit.onStartup(ctx);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
@HandlesTypes
어노테이션에 어플리케이션 초기화 인터페이스를 지정한다.ServeltContainerInitializer
는 파라미터로 넘어오는 Set<Class<?>> c 에 인터페이스의 구현체
들을 모두 찾아서 클래스 정보
로 전달한다.실행 로그
1. 서블릿 컨테이너 초기화 실행
MyContainerInitV2.onStartup
MyContainerInitV2 c = [class hello.container.AppInitV1Servlet]
MyContainerInitV2 container =
org.apache.catalina.core.ApplicationContextFacade@38dd0980
2. 애플리케이션 초기화 실행
AppInitV1Servlet.onStartup
애플리케이션 초기화의 목적
의존성 제거
)public class AppInitV2Spring implements AppInit {
@Override
public void onStartup(ServletContext servletContext) {
System.out.println("AppInitV2Spring.onStartup");
// 스프링 컨테이너 생성
AnnotationConfigWebApplicationContext appContext =
new AnnotationConfigWebApplicationContext();
appContext.register(HelloConfig.class);
// Spring MVC Dispatcher Servlet 생성
DispatcherServlet dispatcher = new DispatcherServlet(appContext);
// 스프링 컨테이너에 Dispatcher Servlet 등록.
servletContext
.addServlet("dispatcherV2", dispatcher)
.addMapping("/spring/*");
}
}
컨트롤러 빈들을 호출
한다.WebApplicationInitializer
이다.WebApplicationInitializer
로 바꿔주면 된다.동작방식
spring-web 라이브러리를 열어보면, 위에서 했던 방법과 동일하게 jakarta 파일에 컨테이너를 등록
하는 과정을 거친다.
등록된 클래스인 SpringServeltContainerInitializer
를 보면,
@HandlesTypes
를 통해 어플리케이션을 초기화하는 것을 볼 수 있다.