headfisrt servlet&jsp chapter 5
TRANSCRIPT
Servlets & JSP5장
속성과 리스너
• 5장 스터디 범위
초기화 파라미터(1)• 하나의 서블릿 내에서 계속해서 참조하고자 하는 정보가
있음 (책에서는 admin email을 예시로 제시함)
• DD(=배포서술자) web.xml에서
<servlet> .... 코드 생략 .... <init-param> <param-name>adminEmail</param-name> <param-value>[email protected]</param-value> </init-param> </servlet>
초기화 파라미터(2)• DD(=배포서술자) web.xml에서
• 서블릿 코드에서는
out.println(getServletConfig().getInitParameter("adminEmail"));
<servlet> .... 코드 생략 .... <init-param> <param-name>adminEmail</param-name> <param-value>[email protected]</param-value> </init-param> </servlet>
초기화 파라미터(3)• 아래 코드가 의미하는 바는
• 이미 servletConfig 객체가 가 있다는 뜻이겠죠?
• 이 일은 서블릿이 초기화 될때 -> init(servletConfig) 때 생성됩니다
out.println(getServletConfig().getInitParameter("adminEmail"));
이를 절차적으로 표현하면• 1. 컨테이너가 배포서술자를 읽었는데 <init-param> 이 있으면 -> 2.
ServletConfig 인스턴스를 만들어 줍니다(서블릿당 한개씩)서블릿당 한개씩 == <servlet></servlet> 개수 만큼 인스턴스 생성
• 3. 컨테이너는 <init-param> 내에 있는 이름과 값을 확인한 후 ServletConfig 객체에 저장해 주고
• 4. 이후 컨테이너가 서블릿 클래스 인스턴스를 생성하고
• 5. 생성한 후에 init의 인자로 ServletConfig(=> init(ServletConfig) )를 진행함으로써 서블릿 초기화 파라미터를 공유할 수 있게 됨
컨텍스트 초기화 파라미터(1)• 이전 초기화 파라미터를 활용하면 이런것이 가능하겠죠?
<servlet> —- 위 생략 —- // 메일 관련 servlet <context-param> <param-name>MailHelp</param-name> <param-value>[email protected]</param-value> </context-param> </servlet>
<servlet> // 카페 관련 servlet <context-param> <param-name>CafeHelp</param-name> <param-value>[email protected]</param-value> </context-param> </servlet> —- 아래 생략 —-
컨텍스트 초기화 파라미터(2)• 근데 만약 한개의 통합된 help 이메일을 제공하고 싶다면?
<servlet> —- 위 생략 —- // 메일 관련 servlet <context-param> <param-name>help_intergrated</param-name> <param-value>[email protected]</param-value> </context-param> </servlet>
<servlet> // 카페 관련 servlet <context-param> <param-name>help_intergrated</param-name> <param-value>[email protected]</param-value> </context-param> </servlet> —- 아래 생략 —-
?????????
컨텍스트 초기화 파라미터(3)• 지금 우리가 생각한것?
어 코드중복이다
한번의 수고로 프로그램을 제어하고 싶다
해결책?? ->> 다음장
• 지금까지 4번 등장한
컨텍스트 초기화 파라미터(4)
컨텍스트 초기화 파라미터 가 답입니다
How To Use 컨텍스트 초기화 파라미터?(5)
• 왼쪽을 오른쪽과 같이 만들어 주면 됩니다
<servlet> —- 위 생략 —- // 메일 관련 servlet <context-param> <param-name>help_intergrated</param-name> <param-value>[email protected]</param-value> </context-param> </servlet>
<servlet> // 카페 관련 servlet <context-param> <param-name>help_intergrated</param-name> <param-value>[email protected]</param-value> </context-param> </servlet> —- 아래 생략 —-
<context-param> —- 위 생략 —- <param-name>help_intergrated</param-name> <param-value>[email protected]</param-value> </context-param>
<servlet> // 메일 관련 servlet </servlet>
<servlet> // 카페 관련 servlet </servlet> —- 아래 생략 —-
그러면 서블릿에서 컨텍스트 초기화 파라미터에
어떻게 접근하나요? (6)• 이렇게요
out.println(getServletContext().getInitParameter("adminEmail"));
읭? 뭐가 달라요?
out.println(getServletConfig().getInitParameter("adminEmail"));
out.println(getServletContext().getInitParameter("adminEmail"));
좋은 방법은 아닌것 같지만 전역변수와 지역변수 개념을 활용해
이해해 보면 . . .
어 근데..? 지난학기에 객체지향적으로 프로그래밍 작성하자고
교수님이 말씀하셨는데??
왜 지금까지 String 타입의 이메일 주소만 공유해?
나 객체 공유할래
객체를 공유해 볼까?• 음 우선 DD에는 문자열 밖에 못들어 간다고 하네?
• 그러면 문자열 받아와서 객체를 생성하게 해주는어떤 친구를 만들면 되겠다
• 음 우리가 배운 MVC 패턴을 고려할 때 C가 이 역할을 담당하면 좋을것 같네??
• 좋아 control 역할을 해주는 서블릿을 만들고 그 서블릿이 객체를 만든 후 객체가 공유될 수 있도록 속성에 묶어두게 하면 되겠네?
우왕 너무 좋은 방법이다
나만 믿고 개발하면 됨 따라와
뭔가 잘못된것 같은데 고쳐볼사람?
저요!서블릿은 자신이 호출 되어야지만
동작할 수 있어요
control 역할을 해줄 서블릿즉 객체를 만든 후 객체가 공유될 수있도록 속성에 묶어줄 서블릿이먼저 호출되지 않으면 문제가 생길것 같아요
그래서 필요한 것이 리스너!• JSP도 아니고 서블릿도 아닌 어떤 자바객체
• 초기화 목적으로 실행될 어떤 객체
•그것이 바로 리스너
ServletContextListener는 뭐해요?
• 컨텍스트가 초기화 되는 것을 알아 차립니다(초기화 = 애플리케이션이 배포됨)
• Servlet Context로 부터 컨텍스트 초기화 파라미터를 읽음
• 데이터 베이스 연결을 위한 초기화 파라미터를 사용함
• 컨텍스트가 종료되는것을 알아요
리스너는 어떻게 만들까요?• 우선 Web.xml에 listener 항목을 추가해 줍니다
<listener> <listener-class> com.example.MyServletContextListener </listener-class> </listener>
앗! 단 해당 클래스는 반드시 ServletContextListener 인터페이스를 상속받고 있어야 해요
이렇게 말이죠
public class MyServletContextListener implements ServletContextListener { public void contextInitialized(ServletContextEvent event){ servletContext sc = event.getServletContext(); String dogBreed = sc.getInitParameter("breed"); Dog d = new Dog(dogBreed); sc.setAttribute("dog", d); } }
이후 그냥 속성에 객체를 속성에 저장해 주면 되요
public class MyServletContextListener implements ServletContextListener { public void contextInitialized(ServletContextEvent event){ servletContext sc = event.getServletContext(); String dogBreed = sc.getInitParameter("breed"); Dog d = new Dog(dogBreed);
sc.setAttribute("dog", d); } }
그러면 서블릿에서는 어떻게 가져오나요?
public class ListenerTester extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, servletException { response.setContentType("text/html"); PrintWriter out = response.getWriter();
Dog dog = (Dog) getServletContext().getAttribute("dog");
out.println("Dog's breed is : "+ dog.getBreed()); } }
getAttribute의 return값은 Object!!!
리스너는 8개가 있다고 해요• 216페이지를 참조하세요
• 우리는 다시 context 이야기로 돌아가도록 합시다
Context 생존범위는 스레드에 안전하지 못하다고 해요
• 그 이유는 서블릿이 멀티 스레드 기반으로 실행되는데요청이 동일한 서블릿으로 부터 온 것인지 다른 서블릿으로 부터 온 것인지 알지 못하기 때문이라고 합니다
책에서 보인 예시• 서블릿1이 A를 10 B를 5로 설정하였다
• 그런데 서블릿2가 와서 A를 7로 바꾸어 놓았다
• 서블릿 1은 A를 불러오면 당연히 10이 return될 것이라 생각했다
• 하지만 7이 왔다
그림으로
10 5
7context
그림으로
10 7context
그림으로
10 5
7context
보호 중
그림으로
10 5
7context
보호 중
컨텍스트에 락이 필요
HowTo컨텍스트 락걸기?
public void doGet(어쩌구 저쩌구) throws 어쩌구, 저쩌구 { ... 코드 ... synchronized(getServletContext()){ getServletContext().setAttribute("foo", "22"); getServletContext().setAttribute("bar", "42");
out.println(getServletContext().getAttribute("foo")); out.println(getServletContext().getAttribute("bar")); } ... 코드 ... }
보호되는 영역
세션도 동기화 문제가 있다고 해요
세션? 하나의 클라이언트와의 대화 상태를 유지하기 위해서 사용
한 시점에 세션에서 작업하는 스레드는 오직 하나
그런데 왜 동기화 문제가 발생?
동일한 클라이언트 하나 이상의 요청
같은 세션에 요청이 들어감 정말 안전한가?
이 문제를 해결하기 위해
public void doGet(어쩌구 저쩌구) throws 어쩌구, 저쩌구 { ... 코드 ... synchronized(session){ getServletContext().setAttribute("foo", "22"); getServletContext().setAttribute("bar", "42");
out.println(getServletContext().getAttribute("foo")); out.println(getServletContext().getAttribute("bar")); } ... 코드 ... }
아 도데체 어떤 것들이 스레드로 부터 안전한가요?
• Request 속성 및 지역변수
3장 스터디에서 보았던 코드
public void doGet(어쩌구 저쩌구) throws 어쩌구, 저쩌구 { JSP단으로 보내줄 데이터를 열심히 가공합니다 JSP단으로 보내줄 데이터를 정말 열심히 열심히 가공합니다 ... 코드 ... JSP단으로 보내줄 데이터를 열심히 가공했어요 RequestDispatcher view = request.getRequestDispatcher("result.jsp"); view.forward(request, response); }
여기에 flush가 들어가면?
public void doGet(어쩌구 저쩌구) throws 어쩌구, 저쩌구 { JSP단으로 보내줄 데이터를 열심히 가공합니다 JSP단으로 보내줄 데이터를 정말 열심히 열심히 가공합니다 ... 코드 ... JSP단으로 보내줄 데이터를 열심히 가공했어요
os.flush(); RequestDispatcher view = request.getRequestDispatcher("result.jsp"); view.forward(request, response); }
IllegalSt
ateExc
eption
flush() 라는 것은
• 클라이언트에게 응답을 보냈다.
• response를 내려 보내라
• 끝났다 라는 뜻이므로
• 이미 request를 쓸 수 없는 상태가 되어 버렸다
IllegalStateException 발생 원인
Q & A