출처 : http://blog.empas.com/ahnyounghoe/12334324
시리즈물(?)입니다.
public abstract class AbstractController extends WebContentGenerator implements Controller {
private boolean synchronizeOnSession = false;
public final void setSynchronizeOnSession(boolean synchronizeOnSession) {
this.synchronizeOnSession = synchronizeOnSession;
}
public final boolean isSynchronizeOnSession() {
return synchronizeOnSession;
}
public final ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
throws Exception {
// delegate to WebContentGenerator for checking and preparing
checkAndPrepare(request, response, this instanceof LastModified);
checkAndPrepare(request, response, this instanceof LastModified);
// execute in synchronized block if required
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
synchronized (session) {
return handleRequestInternal(request, response);
}
}
}
return handleRequestInternal(request, response);
}
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
synchronized (session) {
return handleRequestInternal(request, response);
}
}
}
return handleRequestInternal(request, response);
}
/**
* Template method. Subclasses must implement this.
*/
protected abstract ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception;
* Template method. Subclasses must implement this.
*/
protected abstract ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception;
}
우선 코드 내용을 보기에 앞서 클래스 다이어그램으로 다른 클래스와의 관계를 먼저 살펴보죠.
우선 Controller의 구현 클래스가 되지만 스스로 abstract 입니다.
public abstract class AbstractController extends WebContentGenerator implements Controller {
private boolean synchronizeOnSession = false;
/**
* Template method. Subclasses must implement this.
*/
protected abstract ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception;
* Template method. Subclasses must implement this.
*/
protected abstract ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception;
}
코드에서 보는 바와 같이 abstract로 선언한 이유는 Template Method 제공하기 위함이죠.
Template Method란 프레임워크를 만드는 기반 패턴입니다. (GoF 설계 패턴 참조하세요.)
간단히 말하면 EJB 나 Servlet 등의 API를 보면 그 내용을 채우면 처리가 된다하는 메소드가 있죠.
이것들이 결국 Template Method 패턴을 구현한 것입니다.
이렇게 만든 이유는 무엇일까요? 아래 코드를 보면 답을 알 수 있죠.
public final ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
throws Exception {
throws Exception {
// delegate to WebContentGenerator for checking and preparing
checkAndPrepare(request, response, this instanceof LastModified);
checkAndPrepare(request, response, this instanceof LastModified);
// execute in synchronized block if required
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
synchronized (session) {
return handleRequestInternal(request, response);
}
}
}
return handleRequestInternal(request, response);if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
synchronized (session) {
return handleRequestInternal(request, response);
}
}
}
}
Controller의 클라이언트 코드는 AbstractController를 Controller 인터페이스를 통해 접근할 것입니다. 그렇게 되면 공통적으로 모두 checkAndPrepare() 를 수행하게 하고, handlerRequestInternal()을 수행하도록 작업흐름(Workflow)을 규정한 것이죠.
그러나, checkAndPrepare()를 직접 구현하지는 않았습니다. 부모 클래스인 WebContentGenerator의 것을 활용하도록 했죠. 거기에다가 handlerRequestInternal() 마저 내용이 없는 abstract 죠. 어찌 보면 얌채갔지만..^^;
분화점 혹은 중계자로써 역할을 갖는 것이죠. 물건 팔아 장사하는 분들하고 역할이 비슷한거죠.
Run-time의 인스턴스 관점이 아니라 Design-time에서 시퀀스를 그려보면, 즉 실제 프로그램이 돌아가는 흐름이 아니라 설계자 관점에서 클래스의 역할을 나누기 위해 작업흐름을 묘사해보겠습니다.

client가 인터페이스를 통해 요청이 오면 AbstractController는 Spring 프레임워크가 자체적으로 할 수 있는 일은 checkAndPrepare()를 통해 수행하고, 업무의 고유한 로직은 handleRequestInternal()을 통해서 수행되도록 설계해놓은 것이죠.
이러한 분화점 역할 이외에 부가적으로 AbstractController가 갖고 있는 프로퍼티는 synchronizeOnSession입니다. boolean 값인데 프로퍼티에 따라 차이가 나는 것은 무얼까요?
public final ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
throws Exception {
throws Exception {
// delegate to WebContentGenerator for checking and preparing
checkAndPrepare(request, response, this instanceof LastModified);
checkAndPrepare(request, response, this instanceof LastModified);
// execute in synchronized block if required
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
synchronized (session) {
return handleRequestInternal(request, response);
}
}
}
return handleRequestInternal(request, response);
}
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
synchronized (session) {
return handleRequestInternal(request, response);
}
}
}
return handleRequestInternal(request, response);
}
synchronizeOnSession이 true로 설정되어 있을 때, session이 존재하면
handleRequestInternal()이 해당 세션에 대해서 동기화 되어 수행되게 합니다.
[이하 새로 추가]
checkAndPrepare 메소드의 세번째 인자는 이 객체가 LastModified인지 여부 즉, LastModified 인터페이스 구현 여부를 전달합니다. LastModified는 HttpServlet의 getLastModified() 즉, 서블릿이 받은 요청 객체의 최종 수정 시점을 반환하는 메소드를 구현했음을 나타내는 Marker 인터페이스입니다. AbstractController 가 직접 LastModified를 구현하지는 않았기 때문에 AbstractController의 자식 클래스이면서 LastModified 구현한 경우 true가 되겠죠.
위에 작성한 클래스 다이어그램에 이를 반영하면 조금 수정이 가해지겠네요.

