스프링에서는 클라이언트로부터 요청이 들어오면
DispatcherServlet이 요청을 처리하고, 모델을 생성하여
Controller에게 전달한다.
DispatcherServlet이 하는 일
1. 요청 처리
예를 들어 GET으로 요청한 데이터 타입은 String인데 메서드에선 int 로 받을 수 있다.
이건 중간에 DispatcherServlet이 String을 int로 변환해주는 작업을 하기 때문이다.
2. Model 생성
메서드의 매개변수에서 (Model model) 을 쓸 수 있는 이유는 DispatcherServlet이 Model을 생성하고 넘겨주기 때문이다.
다음 코드를 통해 DispatcherServlet이 어떻게 동작하는지 알아볼 수 있었다.
package com.hyocoding.ch1;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Iterator;
import java.util.Map;
import java.util.Scanner;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.ui.Model;
import org.springframework.validation.support.BindingAwareModelMap;
@WebServlet("/myDS")//localhost:8080/ch1/myDS?year=2022&month=7&day=1
public class MyDispatchServlet extends HttpServlet{
@Override
public void service(HttpServletRequest request, HttpServletResponse response) throws IOException{
//요청으로 들어온 데이터를 map으로 생성한다.
Map<String, String[]> map = request.getParameterMap();
Model model = null;
String viewName = "";
try {
//YoilTeller의 main메서드를 가져온다.
Class clazz = Class.forName("com.hyocoding.ch1.YoilTeller");
Object obj = clazz.newInstance();
Method main = clazz.getDeclaredMethod("main", int.class, int.class, int.class, Model.class);
Parameter[] paramArr = main.getParameters();
//전달 인자를 담을 배열을 생성.
Object[] argArr = new Object[main.getParameterCount()];
for(int i=0;i<paramArr.length;i++) {
String paramName = paramArr[i].getName();
Class<?> paramType = paramArr[i].getType();
//map에서 매개변수에 담을 값들을 얻는다.
Object value = map.get(paramName);
//매개변수 타입이 Model이라면 model 객체 생성.
if(paramType==Model.class) {
argArr[i] = model = new BindingAwareModelMap();
}
else if(paramType==HttpServletRequest.class) {
argArr[i]=request;
}
else if(paramType==HttpServletResponse.class) {
argArr[i]=response;
}
else if(value!=null) {
String strvalue = ((String[])value)[0];
//map에는 value가 String으로 들어있는데 이를 int로 변환해준다.
argArr[i] = convertTo(strvalue, paramType);
}
}
viewName = (String) main.invoke(obj, argArr);
}catch(Exception e) {
e.printStackTrace();
}
render(viewName,model,response);
}
private void render(String viewName, Model model, HttpServletResponse response) throws IOException {
String result="";
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
PrintWriter out = response.getWriter();
Scanner sc = new Scanner(new File(getResolveViewName(viewName)),"utf-8");
while(sc.hasNextLine())
result += sc.nextLine()+ System.lineSeparator();
//model을 map으로 변환
Map map = model.asMap();
Iterator it = map.keySet().iterator();
while(it.hasNext()) {
String key = (String)it.next();
// 4. replace()로 key를 value로 바꾼다.
result = result.replace("${"+key+"}", map.get(key)+"");
}
//렌더링 결과를 화면에 출력.
out.println(result);
}
//ViewResolver 역할. jsp파일 진짜 경로 찾기
private String getResolveViewName(String viewName) {
return getServletContext().getRealPath("/WEB-INF/views/"+viewName+".jsp");
}
private Object convertTo(Object value, Class type) {
if(type==null || value==null || type.isInstance(value)) // 타입이 같으면 그대로 반환
return value;
// 타입이 다르면, 변환해서 반환
if(String.class.isInstance(value) && type==int.class) { // String -> int
return Integer.valueOf((String)value);
} else if(String.class.isInstance(value) && type==double.class) { // String -> double
return Double.valueOf((String)value);
}
return value;
}
}
YoilTeller
package com.hyocoding.ch1;
import java.util.Calendar;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class YoilTeller {
@RequestMapping("/getyoil")
public String main(int year, int month, int day, Model m) {
char yoil=getYoil(year,month,day);
m.addAttribute("year",year);
m.addAttribute("month",month);
m.addAttribute("day", day);
m.addAttribute("yoil", yoil);
return "getyoil";
}
private char getYoil(int year, int month, int day) {
Calendar cal = Calendar.getInstance();
cal.set(year, month-1, day);
int weekNum = cal.get(Calendar.DAY_OF_WEEK);//일:1, 월:2 ...
char yoil = " 일월화수목금토일".charAt(weekNum);
return yoil;
}
}
DispatcherServlet에서 RequestMapping을 어떻게 처리하는지 구현해보고 싶었는데 아직 잘 모르겠다..ㅎㅎ
좀 더 공부해본 다음 해보는 걸로.
위의 코드를 intellij에서 작성해봤는데 메소드의 매개변수 이름을 arg0, arg1, arg2 이렇게 가져와서 안됐다..
진짜 매개변수 이름을 가져오려면 컴파일할때 -parameters 옵션을 넣어줘야하는데 어떻게 하는 건지 모르겠다.
이클립스에서는 window> preferencs> compiler 에서 store information about method parameters 옵션을 체크해주면 된다.
이 기능은 java 1.8 이상부터 지원하니 업데이트 필수~
'Spring' 카테고리의 다른 글
[Spring] @RequestParam과 @ModelAttribute (0) | 2022.08.04 |
---|---|
[Spring] 요청 URL 경로 출력하기(feat. Filter) (0) | 2022.07.14 |
[Spring] HttpServletResquest, HttpServletResponse (0) | 2022.07.03 |
[Spring] WebDataBinder로 데이터 타입 변환, 검증하기 (0) | 2022.06.22 |
[Spring] 예외 처리( @ExceptionHander, @ResponseStatus) (0) | 2022.06.20 |
댓글