WAS에는 3가지의 컨테이너로 구성되어있다
Servlet - 서블릿 관리
JSP - JSP 관리
EJB (Enterprise JavaBeans)
https://doozi316.github.io/web/2020/09/13/WEB26/
WAS를 왜 사용하는지 컨테이너는 무엇인지 예쁘게 잘 정리된 블로그
서블릿 컨테이너의 역할
- 웹서버와의 통신 지원
- 서블릿 생명주기(Life Cycle) 관리
- 멀티쓰레드 지원 및 관리
- 선언적인 보안 관리
서블릿은 웹 컨테이너에 의해서 관리되며, 다양한 클라이언트 요청에 의해서 동적인 콘텐츠
(content)로 응답 가능한 자바 기반의 웹 컴포넌트이다.
클라이언트의 요청에 의해서 동적으로 실행된다.
서블릿은 반드시 웹 컨테이너에 의해서 관리되며, 자바 스레드로 동작되기 때문에 효율
적으로 사용이 가능하다서블릿의 응답결과는 일반적으로 HTML 형식으로 서비스된다.
MVC 패턴을 적용하여 웹 어플리케이션을 개발한다면, 서블릿이 아닌 JSP에서 HTML 코드를 작성하게
된다
실행 순서/동작 구조
-Java EE 기반에서는 프로그램을 컨테이너가 제어한다.
-개발자가 아닌 제 3자가 실행흐름을 제어하는 것을 IoC(Inversion of Control) 제어의 역전이라 한다.
1.클라이언트로부터 요청을 받음
-요청받은 페이지가 서블릿이면 서블릿 컨테이너로 처리를 넘김, WEB-INF에서 서블릿을 찾아서 실행준비2.최초의 요청 여부 판단
-서블릿 컨테이너에서 해당 요청이 최초의 요청인지 아닌지 판단한다.3.서블릿 객체 생성
-서블릿 컨테이너에서 최초의 요청에 해당해서 서블릿을 메모리에 로딩하고 객체를 생성4.init() 메소드 실행
-객체가 생성된 다음 호출되는 메소드 Servlet 인터페이스에 선언되어잇고 고 아래에 구현되어있다.
-서블릿 객체의 초기화 작업이 구현되어있다.5.service() 메소드 실행
-서블릿의 요청 순서에 상관없이 요청이 있을때 마다 실행
-실제 서블릿에서 처리해야 되는 내용이 구현되어있다.
API
-서블릿을 구현할 때 해당 API만 상속하면 웹에서 동작하는 기본조건 갖추는게 가능
-모든 서블릿은 HttpServlet을 상속 받아야 한다.(아래의 상속 구조를 보자)

GenericServlet클래스와 HttpServlet 클래스의 차이점은 둘 다 추상클래스이지만
service() 메소드가 HttpServlet에서는 구현되어있다
-매핑(URI별칭) = URI 경로를 설정 하고(웹에서 보았을 때 서블릿이 보이면 안됌.)
-service방식으로 생성(어떤 방식이든 비즈니스 로직을 처리하고 응답문서를 생성 전송)
service는 service()를 오버라이드 한다.
클라이언트로부터의 요청이 올 때마다 다른 thread가 뜨면서 이 메소드를 실행.



프로젝트 만드는 이 부분 안넣으려다가.. 다른 사람도 보는 사람 있을까봐..
doget은 get방식으로 요청응답 할 때 dopost는 post방식으로 요청응답 할 때 사용된다
-알아본 것
(1) 응답 할 때 일일히 찍기어려우니까 JSP를 이용한다
(2)예외에 대해서 웹에서 예외처리가 따로있으니 그 형식에 맞게끔 예외를 형성해서 ServletException 으로 예외처리 될 수 있게 던져라
(3) 자원의 생성과 종료를 느껴봐라
package org.zerock.myapp.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.Cleanup;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;
@Log4j2
@NoArgsConstructor
// 매핑되어있는 부분
@WebServlet("/Hello")
public class HelloServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
log.info("찍어라 제발");
try {
response.setCharacterEncoding("utf8");
@Cleanup("close")
PrintWriter out = response.getWriter();
//하나씩 찍기는 불가능하다.
out.print("<html><head></head><body>");
out.print("<h1>Hello world.</h1>");
out.print("</body></html>");
out.flush();
} catch(Exception e) {
throw new ServletException(e);
} // try-catch
}// service
}// end class
Deployment Assembly에 Maven라이브러리가 빠졌는지 확인하세요
그거 때문에 오류가 났었습니다.
(deploy assembly는 임시 배포 공간을 마련하는 것/ Maven라이브러리가 없으면 여러 라이브러리 관리가 안되서 오류)
-ServletConfig 의 클래스를 찍어본다.
(1)라이프 사이클
-init 생성단계(자원객체를 얻는 부분)
-service 준비단계()
-destory 파괴단계 ()
package org.zerock.myapp.servlet;
import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;
@Log4j2
@NoArgsConstructor
// Servlet 의 Lifecycle과 그 callback 메소드를 이해하자!
//@WebServlet({ "/Lifecycle1", "/Lifecycle2" })// --> web.xml에서 mapping을 해주기위해 주석처리
public class LifecycleServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
public void init(ServletConfig config) throws ServletException { // 사전처리작업
log.trace("init({}) invoked.", config);
// 요청처리에 필요한 각 종 자원객체의 획득(DataSource)
}// init
@Override
public void destroy() { // 사후처리작업
log.trace("destroy({}) invoked.");
// init 단계에서 획득했던 자원객체들을 해제하는 작업
}// destroy
@Override
protected void service(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
log.trace("service({},{}) invoked)",req,res);
} // service
}// end class
(2)web.xml 을통해 servlet mapping
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
id="WebApp_ID"
version="4.0">
<display-name>servlet01</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>index.htm</welcome-file>
</welcome-file-list>
<!-- Lifecycle servlet registration -->
<servlet>
<servlet-name>LifecycleServlet</servlet-name>
<servlet-class>org.zerock.myapp.servlet.LifecycleServlet</servlet-class>
</servlet>
이 부분 매핑부분
<servlet-mapping>
<servlet-name>LifecycleServlet</servlet-name>
<url-pattern>/Lifecycle1</url-pattern>
<url-pattern>/Lifecycle2</url-pattern>
</servlet-mapping>
</web-app>
어노테이션을 통해 매핑이 가능하지만 web.xml을 통해 매핑가능
web.xml을 통해 매핑하는게 좋은 점은 여러개의 서블릿을 한번에
정리되어있는 상태에서 확인 할 수 있기 때문에 유지보수성이 올라간다.
(value = url-pattern / 별칭이다. 어노테이션 속성에는 별칭이 가능)
속성명을 기재하지 않으면 기본속성값에 들어간다.
package org.zerock.myapp.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;
@Log4j2
@NoArgsConstructor
// name 생략가능 url-pattern = { "/xxx", "/yyy" }
@WebServlet(name="MyServlet", value = { "/xxx", "/yyy" }) // 원소가 하나이면 중괄호 생략가능
public class HelloSevelet2 extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
log.info("HelloServlet 요청");
}// doGet
}// end class
package org.zerock.myapp.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.Cleanup;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;
@Log4j2
@NoArgsConstructor
@WebServlet("/Response")
public class ResponseServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
log.trace("service(req, res) invoked");
try {
// MIME타입 설정
response.setContentType("text/html; charset = UTF-8");
// 자바 I/O
//@Cleanup
PrintWriter out = response.getWriter();
try(out){
// html 작성 및 출력 -> JSP에서 해야함
out.print("<html><body>");
out.print("ResponseServlet 요청성공");
out.print("</body></html>");
out.flush();
} // try-with-resources
}catch(Exception e) {
throw new ServletException(e);
}// try-catch
}// service
} //end class
- @PreDestroy과 destroy() 성능과 기능 차이가 없다. 규약에 따른 라이프사이클에 따라 destroy()를 사용하는 게 좋다.
- 위의 문제의 해결 방법을 찾기 위해서 구글링해 보니 Java 9 부터 PostConstruct 가 deprecated
그리고, maven 에 javax.annotation-api 를 추가해 주면 된다
@WebServlet("/PostPre")
public class PostPreServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
log.info("service(req,res) invoked");
}// service
@Override
public void init(ServletConfig config) throws ServletException { // 사전처리작업
log.trace("init({}) invoked.", config);
// 요청처리에 필요한 각 종 자원객체의 획득(DataSource)
}// init
@Override
public void destroy() { // 사후처리작업
log.trace("destroy({}) invoked.");
// init 단계에서 획득했던 자원객체들을 해제하는 작업
}// destroy
@PostConstruct
public void initMethod() {
log.info("@PostConstruct");
}// PostConstruct
//init과 성능과 기능 차이가 없다. 규약에 따른 라이프사이클에 따라 init을 사용하는 게 좋다.
@PreDestroy
public void clean() {
log.info("@PreDestroy");
}// PreDestory
}// end class

해당 3개에 대해 예제를 통해 확인했다.
@WebServlet("/Login")
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
log.info("서비스시작");
try {
// Step.1 전송파라미터 획득
String userid = request.getParameter("userid");
String passwd = request.getParameter("passwd");
log.info("\t userid: {} , passwd: {}" , userid, passwd);
// Step.2 에코(echo)
response.setContentType("text/html; charset=UTF-8");
@Cleanup
PrintWriter out = response.getWriter();
out.print("<html><body>");
out.print("아이디값: " +userid+"<br>");
out.print("비밀번호값: " + passwd + "<br>" );
out.print("</body></html>");
out.flush();
} catch (Exception e) {
throw new ServletException(e);
}// try-catch
}// service
}// end class
postman을 사용한 결과 ▼

@WebServlet("/Sport")
public class SportServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
log.info("서비스시작");
try {
// Step.1 전송파라미터 획득
request.setCharacterEncoding("utf8");
String[] sports = request.getParameterValues("sports");
String gender = request.getParameter("gender");
log.info("\t sports: {} , gender: {}" , sports, gender);
// Step.2 에코(echo)
response.setContentType("text/html; charset=UTF-8");
@Cleanup
PrintWriter out = response.getWriter();
out.print("<html><body>");
out.print("<ul>");
for(String sport: sports) {
out.print("<li>좋아하는 운동:" + sport +"</li>");
}//for
out.print("<li> 성별 : " +gender+ "</li>");
out.print("</ul>");
out.print("</body></html>");
out.flush();
} catch (Exception e) {
throw new ServletException(e);
}// try-catch
}// service
}// end class
postman을 사용한 결과 ▼

1번은 해당 속성 자체가 없을 때 (아래의 예제에서는 속성이 없어도 빈문자열로 나옴)(생각을 가두지마라)
2번은 속성에 값이 없을 때 (아래의 예제에서는 속성이 없어도 빈문자열로 나옴)(생각을 가두지마라)
3번은 화면 나온대로
이부분이 중요하다. *실전에서는 한HTML 파일에 전송파라메터가 수십개다// 제네릭 인터페이스다. 제네릭 요소를 포함한다. Enumeration<String> enu = request.getParameterNames();
@WebServlet("/Member")
public class MemberServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
log.trace("서비스 시작");
try {
// Step.1 전송파라미터 획득
// (주의) getParameter 메소드 수행전에,
// 반드시 request.setCharacterEncoding("utf8") 로 인코딩 형식을 지정해줘라
request.setCharacterEncoding("utf8");
// 제네릭 인터페이스다. 제네릭 요소를 포함한다.
Enumeration<String> enu = request.getParameterNames();
// Step.2 응답화면 생성 및 전송
response.setContentType("text/html; charset=utf-8");
@Cleanup
PrintWriter out = response.getWriter();
out.print("<html><body><ul>");
while(enu.hasMoreElements()) {
String paramName = enu.nextElement();
String paramValue = request.getParameter(paramName);
System.out.println( String.format("<li> %s : %s </li>" , paramName, paramValue));
out.println( String.format("<li> %s : %s </li>" , paramName, paramValue));
}// while
out.print("</ul></body></html>");
out.flush();
} catch (Exception e) {
throw new ServletException(e);
}// try- catch
}// service
끝!