💙 들어가며
서블릿은 어떻게 생성되서 어떻게 소멸되는 것일까?
실행 단계 및 생명주기에 대해서 알아보자.
그리고 전역에서 사용할 수 있는 공간인
ServletContext에 대해서 알아보자.
✏️ 학습내용 정리
#WAS 실행 (3단계)
WAS가 실행될 때마다 사용되는 3가지 단계가 있다.
💡 참고로 톰캣은 WS와 WAS를 모두 가지고 있다.
1. Listener
💡 Listener(리스너)란?
1. 리스너는 웹 애플리케이션의 생명주기 이벤트를 감지하고 처리하는 데 사용된다.
예를 들어, 웹 애플리케이션이 시작되거나 종료될 때 이벤트를 처리하거나,
세션 생성 또는 파괴 시점에서 작업을 수행할 수 있다.
2. 리스너는 Java의 javax.servlet.ServletContextListener 인터페이스를 구현하여 작성된다.
출처: 챗 지피티(https://chat.openai.com/)
(이벤트와 같은 특별한 상황이 있을 때)
톰캣이나 WAS와 관련된 이벤트를 처리할 때 사용한다.
WAS가 실행될 때 가장 먼저 실행되는 코드이다.
WAS가 실행된다는 것을 application이 실행된다고 볼 수 있다.
application이 가장 처음 실행될 때
동시에 실행되는 코드가 필요하거나
혹은 application이 끝날 때 무언가 실행되기를 원한다고 하면
그때 Listener를 쓴다.
Session 새로운 사용자가 온 것
Session end 사용자가 더 이상 없는 것
(현재 사용자수를 집계할 때 session start와 end를 사용했었다.)
이처럼 Session과 관련된 처리도 Listener를 사용한다.
💡 Listener를 이용해서 application 초기세팅을 해보자.
package kr.co.rland.web.config;
import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;
import jakarta.servlet.annotation.WebListener;
@WebListener
public class AppContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent event) {
System.out.println("서버 시작 이벤트 발생");
String resource = "mybatis-config.xml";
InputStream inputStream;
try {
inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
event.getServletContext().setAttribute("sqlSessionFactory", sqlSessionFactory);
event.getServletContext().setAttribute("a", "hello");
} catch (Exception e) {
e.printStackTrace();
}//try~catch ends
}//contextInit ends
@Override
public void contextDestroyed(ServletContextEvent event) {
System.out.println("서버 종료 이벤트 발생");
}
}// class ends
2. Servlet
💡 Servlet(서블릿)이란?
1. 서블릿은 웹 애플리케이션에서 클라이언트의 요청을 처리하고 동적인 콘텐츠를 생성하는 데 사용된다.
2. 서블릿은 Java 클래스로 작성되며, javax.servlet.Servlet 인터페이스를 구현하거나
javax.servlet.http.HttpServlet 클래스를 상속받아 작성된다.
3. 사용자 요청을 처리하고 응답을 생성하는 역할을 한다.
예를 들어, 사용자가 웹 페이지를 요청하면 서블릿이 그에 해당하는 HTML을 생성하여 반환할 수 있다.
출처: 챗 지피티(https://chat.openai.com/)
사용자가 요구하는 것(url로 요청)과 맥을 일치하는 것이다.
servlet에 loadOnStartup=1이라는 옵션을 주면
application이 시작하자마자
특정 servlet을 시작되게 할 수도 있다.
(=Listener처럼 사용할 수 있는 것!)
프로그램이 시작되자마자 메모리에 올라가야 하는 것들을
여기에 사용할 수 있겠다.
💡 Servlet에 loadOnStartup 옵션주기
package kr.co.rland.web.config;
import java.io.IOException;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
//urlPatterns는 진짜 '*'와 같은 pattern들이 올 수 있다.
//loadOnStartup=1을 하면 application 시작과 동시에 실행하게 한다.(=Listner처럼 사용)
@WebServlet(urlPatterns = "", loadOnStartup = 1)
public class MybatisConfigServlet extends HttpServlet{
@Override
public void init() throws ServletException {
System.out.println("초기화");
}
@Override
protected void service(HttpServletRequest arg0, HttpServletResponse arg1) throws ServletException, IOException {
System.out.println("서비스");
}
@Override
public void destroy() {
System.out.println("삭제");
}
}
서블릿버전으로 loadOnStartup을 하게되면
얘도 시작되자마자 할 수 있게 처리할 수 있다.
WebServlet으로 init과 destroy를 해보자.
서블릿으로도 할 수 있지만 Listener로 시작하는 것이 더 바람직하다!
Listener로 init과 destory를 해보자.
💡 Listener와 Servlet이 동시에 실행된다면?
Listener와 Servlet이 동시에 놓여있다고 한다면
Listener가 먼저 실행되고 Servlet이 차례로 실행되고 난 뒤에
닫힐 때에도 Servlet이 먼저 닫히고 Listener가 마지막으로 닫히면서 끝난다.
(약간 자원 close할 때처럼 가장 먼저 실행된 녀석이 가장 마지막에 닫히는 느낌)
3. Filter (스프링할 때 보게 될 예정)
💡 Filter(필터)란?
1. 필터는 클라이언트 요청 및 응답을 가로채어 중간에서 변형하거나 검사하는 데 사용되며,
주로 입력 데이터 유효성 검사, 요청 및 응답 압축, 인증 및 권한 부여, 로깅 등과 같은 작업에 활용된다.
2. 필터는 javax.servlet.Filter 인터페이스를 구현하여 작성된다.
3. 필터 체인을 통해 여러 개의 필터를 연결하여 요청 및 응답을 처리할 수 있다.
출처: 챗 지피티(https://chat.openai.com/)
#서블릿의 생명주기
프레임워크를 사용하다보면 어떤 것들을 오버라이드 할 수 있는지
어떠한 순서로 호출되고 있는지를 알아야 한다.
(우리는 여태까지 Servlet 프레임워크의 service라는 함수만 오버라이드 해봤음)
처음 servlet 객체가 init이 되고 난 뒤에 destroy가 될 때까지
여러 개의 service(요청,응답)가 호출이 되는데,
그렇다면 service 하나에 따라서 객체가 여러개 생성되었다가 소멸되는 것일까?
정답은 아니다.
하나의 객체 안에서 스레드를 통해서
여러개의 서비스 함수를 개별적으로 실행할 수 있다. (효율적!!)
#ServletContext (=JSP 내장객체 application)
SqlSessionFactory와 같이
모든 Servlet이 공유하는 자원을
따로 담는 곳이 있다면 얼마나 좋을까?
이전에는 App이라는 main함수를 가진 곳에서
static 변수로 처리했었는데,
이제는 web개발을 하기 때문에
우리에게 main함수가 없다.
다행히 Servlet은
이런 공유할 자원을 담을 수 있는
공간을 별도로 마련해두었다.
이름은 ServletContext
얻는 방법은 다음과 같다.
우리의 메인함수는 톰캣이 가지고 있다.
필요할 때 서블릿을 하나씩 하나씩 꺼내서 실행시켜준다.
(각각의 서블릿은 service 함수를 가진다.)
그렇다면 지난번에 App의 main함수에서 만들었던
SqlSessinoFactory를 어디서 만들어야 할까?
프로그램이 실행되자마자 실행될 곳에
SqlSessionFactory를 만들어주면 된다.
#JSP Listener에서 객체 불러오기
먼저 Listener에 필요한 객체들을 생성해서 setAttribute해준다.
setAttribute("키값", 전달할 값)을 해서
키값이 JSP에서 활용되는 원리이다.
💡 자바에서 Listener 설정하기
package kr.co.rland.web.config;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;
import jakarta.servlet.annotation.WebListener;
@WebListener
public class AppContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent event) {
System.out.println("서버 시작 이벤트 발생");
String resource = "mybatis-config.xml";
InputStream inputStream;
try {
inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//sqlSessionFactory라는 이름의 키값으로 sqlSessionFactory라는 객체를 전달한다.
event.getServletContext().setAttribute("sqlSessionFactory", sqlSessionFactory);
//a라는 이름의 키값으로 "hello"라는 문자열을 전달한다.
event.getServletContext().setAttribute("a", "hello");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}//try~catch ends
}//contextInit ends
@Override
public void contextDestroyed(ServletContextEvent event) {
System.out.println("서버 종료 이벤트 발생");
}
}// class ends
그 뒤에 JSP의 코드블록에서 전달받은 key값을 이용해서
출력용 코드블록(<%=%>)을 활용하여 출력한다.
이때 SqlSession을 그냥 session이라는 이름으로 만들게 되면
jsp 내장객체 session과 이름이 충돌된다.
때문에 sqlSession과 같은 식으로 충돌나지 않게 이름을 정한다.
💡 내장객체와 이름충돌이 나지 않게 하자
<%@page import="kr.co.rland.web.entity.Menu"%>
<%@page import="org.apache.ibatis.session.SqlSessionFactory"%>
<%@page import="org.apache.ibatis.session.SqlSession"%>
<%@page import="kr.co.rland.web.repository.MenuRepository"%>
<%@page import="java.util.List"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
//SqlSessionFactory 객체는 Listner에서 get으로 불러온다.
//session이라는 이름은 내장객체와 충돌이 나기 때문에 오류가 난다.
//SqlSession session이 아니라 SqlSession sqlSession으로 이름을 바꿔준다.
SqlSessionFactory sqlSessionFactory = (SqlSessionFactory) application.getAttribute("sqlSessionFactory");
SqlSession sqlSession = sqlSessionFactory.openSession();
MenuRepository repository = sqlSession.getMapper(MenuRepository.class);
List<Menu> list = repository.findAll();
%>
//테스트 출력용! 출력하면 id 해시값이 나온다.
<%= sqlSessionFactory %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>메뉴목록<%=application.getAttribute("a")%></h1>
<ul>
<li>아메리카노(Americano)</li>
<li>아메리카노(Americano)</li>
<li>아메리카노(Americano)</li>
</ul>
</body>
</html>
💙 마치며
1.
톰캣 혹은 WAS가 실행될 때
바로 메모리에 올리고 싶은 내용이 있다면
Listener와 Servlet를 사용해서 2개의 옵션 중에 하나를 쓰면 된다.
1. Application start Listener 만들기
2. 특정 Servlet이 바로 실행될 수 있도록
@WebServlet 어노테이션에 loadOnStartup=1 옵션 주기
'SERVER│SERVLET' 카테고리의 다른 글
[뉴렉처 6기] Server│인증과 권한│세션(Session)│쿠키(Cookie)│응답헤더와 요청헤더 (0) | 2023.10.17 |
---|---|
[뉴렉처 6기] SERVLET│JSP의 단점(스파게티 코드)│MVC 모델(1, 2)│EL│태그라이브러리(JSTL) (0) | 2023.09.10 |
[뉴렉처 6기] SERVLET│쿼리 스트링│JSP입문│재스퍼│코드블록 (0) | 2023.09.10 |
[뉴렉처 6기] SERVLET│서버설정│Annotation│매핑과 배포(IDE버전) (0) | 2023.09.10 |
[뉴렉처 6기] SERVLET│서블릿이란?│WS와 WAS│매핑과 배포(CMD버전) (0) | 2023.09.01 |