안녕하세요, 코린이의 코딩 학습기 채니 입니다.
개인 포스팅용으로 내용에 오류 및 잘못된 정보가 있을 수 있습니다.
지금까지 해왔던 MVC패턴을 전략패턴 이용하여 진행해보려고 합니다.
요청 별로 늘 servlet을 생성해왔지만, 하나의 Servlet을 이용하여 제어해보겠습니다.
url-command.properties (Command 패턴)
##########################
# url-command.properties #
##########################
/student/studentEnroll.do = com.ce.app.student.controller.StudentEnrollController
/student/selectList.do = com.ce.app.student.controller.SelectListController
AbstractController (Strategy)
public abstract class AbstractController {
public String doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// GET방식만 구현하였는데 POST를 요청한다면 예외던짐!
throw new MethodNotAllowException("GET");
}
public String doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// POST방식만 구현하였는데 GET를 요청한다면 예외던짐!
throw new MethodNotAllowException("POST");
}
}
만일 GET방식만 구현하였는데, POST로 요청을 한다거나 POST방식만 구현하였는데 GET으로 요청을 했을 때 MethodNotAllowException()이 던져지도록 하였습니다. (커스텀 예외클래스)
StudentEnrollController (Concrete Strategy)
public class StudentEnrollController extends AbstractController {
private StudentService studentService;
public SelectListController (StudentService studentService) {
this.studentService = studentService;
}
@Override
public String doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// super 호출하면 예외던져지므로 호출 X!
return "redirect:/";
}
}
자식 클래스들은 필요에 따른 doGet/doPost 메소드를 오버라이딩하여 사용하며, return 값으로 redirect 처리/forward처리를 해주어 DispatcherServlet에서 이를 처리하도록 해줍니다.
StudentListController (Concrete Strategy)
public class SelectListController extends AbstractController {
private StudentService studentService;
public SelectListController (StudentService studentService) {
this.studentService = studentService;
}
@Override
public String doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
return "student/selectList";
}
}
StudentService interface
public interface StudentService {
}
StudentServiceImpl
public class StudentServiceImpl implements StudentService {
private StudentDao studentDao;
public StudentServiceImpl(StudentDao studentDao) {
this.studentDao = studentDao;
}
}
StudentDao interface
public interface StudentDao {
}
StudentDaoImpl
public class StudentDaoImpl implements StudentDao {
}
DispatcherServlet
public class DispatcherServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private Map<String, AbstractController> urlCommandMap = new HashMap<>();
public DispatcherServlet() throws FileNotFoundException, IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
// 1. url-command.properties -> Properties 객체
String filename = DispatcherServlet.class.getResource("/url-command.properties").getPath(); // / -> target/class 의미
Properties prop = new Properties();
prop.load(new FileReader(filename));
// 2. Properties 객체 -> urlCommandMap에 요소추가 (String=AbstractController객체)
Set<String> urls = prop.stringPropertyNames(); // 모든 키 셋을 리턴
StudentDao studentDao = new StudentDaoImpl();
StudentService studentService = new StudentServiceImpl(studentDao);
for(String url : urls) {
String className = prop.getProperty(url);
Class<?> clz = Class.forName(className);
Constructor<?> constructor = clz.getDeclaredConstructor(StudentService.class);
AbstractController controller = (AbstractController) constructor.newInstance(studentService);
urlCommandMap.put(url, controller);
}
System.out.println("urlCommandMap = " + urlCommandMap);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().append("Served at: ").append(request.getContextPath());
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
@콘솔출력값
urlCommandMap = {/student/studentEnroll.do=com.ce.app.student.controller.StudentEnrollController@4e6b5f56,
/student/selectList.do=com.ce.app.student.controller.SelectListController@422fdd77}
url-command.properties에 작성한 url = 객체를 Map의 String, AbstractController타입으로 받아주려고 합니다.
다만 Properties는 String = String으로 AbstractController 객체로 변환하여 저장해줘야 합니다.
따라서 stringPropertyNames()를 이용해 모든 key 값을 Set으로 리턴 받아 reflection을 통해 해당 객체를 생성해주었습니다.
각 Controller는 StudentServiceImpl 를 의존하고 있으며, StudentServiceImpl는 StudentDaoImpl를 의존하고 있습니다.
출력값을 확인하면 {url = 객체}로 잘 출력 된 것을 확인할 수 있습니다.
doGet, doPost 메소드 완성
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 요청 주소
String uri = request.getRequestURI(); // /hello-maven2/student/selectList.do
uri = uri.replace(request.getContextPath(), ""); // student/selectList.do
AbstractController controller = urlCommandMap.get(uri);
// 2. 해당 controller 호출
String method = request.getMethod(); // GET 요청 시 GET, POST 요청 시 POST 리턴
String viewName = null;
// 등록 controller가 없을 때 예외 던짐!
if(controller == null) {
throw new RuntimeException("해당 요청을 처리할 Controller가 존재하지 않습니다.");
}
switch(method) {
case "GET" : viewName = controller.doGet(request, response); break;
case "POST" : viewName = controller.doPost(request, response); break;
default : throw new MethodNotAllowException();
}
// 3. viewName에 따라 forward/redirect처리
if(viewName != null) {
if(viewName.startsWith("redirect:")) {
// redirect 처리
String location = request.getContextPath() + viewName.replace("redirect:", "");
response.sendRedirect(location);
} else {
// forward 처리
String prefix = "/WEB-INF/views/";
String suffix = ".jsp";
viewName = prefix + viewName + suffix;
request.getRequestDispatcher(viewName).forward(request, response);
}
}
// viewName이 null인 경우 컨트롤러에서 응답메세지를 직접 작성한 경우 (비동기) 아무것도 하지 않는다.
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
요청 주소를 getRequsetURI()로 가져온 후 contextPath()를 제외한 uri를 이용해 controller 객체를 uriCommandMap에서 가져와줍니다.
GET/POST 방식에 따른 redirect/forward 처리 분기를 위하여 getMethod() 를 이용해 사용자의 요청방식을 가져온 후 switch문으로 분기 처리 해줍니다.
이 때, controller의 return 값이 redirect 처리 혹은 forward 처리(uri) 이므로, 해당 값을 viewName에 담아주어 redirect 혹은 forward 처리 해줍니다.
(만일 비동기로 요청을 했다면, return 값은 null)
Post 요청 시 doGet 메소드를 선언하여 doGet메소드 내에서 모두 처리!!
SqlSessionUtils
public class SqlSessionUtils {
static SqlSessionFactory factory;
static {
String resource = "/mybatis-config.xml";
// 1. FactoryBuilder
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
// 2. Factory
InputStream is = null;
try {
is = Resources.getResourceAsStream(resource); // resource를 읽어서 stream으로 반환
} catch (IOException e) {
e.printStackTrace();
}
factory = builder.build(is);
}
public static SqlSession getSqlSession() {
// 3. SqlSession
return factory.openSession(false); // autoCommit 여부
// try {
// sqlSession =
// new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream(resource)).openSession(false);
// } catch (IOException e) {
// e.printStackTrace();
// }
}
}
'Java > Spring' 카테고리의 다른 글
Spring) 스프링 개요, 특징, 주요 모듈 (0) | 2022.08.11 |
---|---|
Spring) Emmet / Lombok 설치 및 환경설정 (0) | 2022.08.11 |
Spring) STS 다운로드 및 환경 설정 (0) | 2022.08.11 |
전략패턴) Strategy Pattern - Context, Strategy, Concrete Strategy (0) | 2022.08.04 |
프레임워크) Framework란?, 프레임워크와 라이브러리의 차이점 (0) | 2022.08.02 |