1. methodcall2
package com.fastcampus.ch2;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.Map;
import java.util.Scanner;
import org.springframework.ui.Model;
import org.springframework.validation.support.BindingAwareModelMap;
public class MethodCall2 {
public static void main(String[] args) throws Exception{
// 1. YoilTellerMVC의 객체를 생성
// (1) 클래스 객체 생성 => YoilTellerMVC의 설계도 베낌
Class clazz = Class.forName("com.fastcampus.ch2.YoilTellerMVC");
// (2) 베낀 설계도를 이용해 YoiltellerMVC 객체 생성
Object obj = clazz.newInstance();
// 2. 요일텔러의 main메서드의 정보를 가져온다. 매소드 이름, 매소드의 매개변수 4개 // 이름이 main인 것이 다수 있을 수 있으므로 꼭 뒤에 매개 변수까지 적어서 내가 쓰고 싶은 녀석을 특정해야함.
Method main = clazz.getDeclaredMethod("main", int.class, int.class, int.class, Model.class);
// 3. 모델을 생성
Model model = new BindingAwareModelMap();
System.out.println("[before] model="+model);
// 4. main 매소드를 호출
// 우리가 호출할 매소드의 매개 변수가 다 다르기 때문에 밑줄처럼 호출 못하고 Reflection API로 호출하는 게 훨씬 유연함.
// String viewName = obj.main(2021, 10, 1, model); // 아래 줄과 동일
//Reflection API를 이용한 호출 (하드 코딩)
// 매소드.invoke()는 해당 매소드를 실행하는 매소드
// 형태: 매소드A.invoke(A를 실행할 객체, A의 파라미터 값)
// 파라미터 값은 차례대로 배열로 들어감.
// 객체가 타입이 object이므로 형 변환 필요
String viewName = (String)main.invoke(obj, new Object[] { 2021, 10, 1, model });
System.out.println("viewName="+viewName);
// Model의 내용을 출력
System.out.println("[after] model="+model);
// 텍스트 파일을 이용한 rendering
render(model, viewName);
} // main
static void render(Model model, String viewName) throws IOException {
String result = "";
// 1. 뷰의 내용을 한줄씩 읽어서 하나의 문자열로 만든다.
Scanner sc = new Scanner(new File("src/main/webapp/WEB-INF/views/"+viewName+".jsp"), "utf-8");
while(sc.hasNextLine())
result += sc.nextLine()+ System.lineSeparator();
// 2. model을 map으로 변환
Map map = model.asMap();
// 3.key를 하나씩 읽어서 template의 ${key}를 value바꾼다.
Iterator it = map.keySet().iterator();
while(it.hasNext()) {
String key = (String)it.next();
// 4. replace()로 key를 value 치환한다.
result = result.replace("${"+key+"}", ""+map.get(key));
}
// 5.렌더링 결과를 출력한다.
System.out.println(result);
}
}
2.Methodcall3
위에서는 메인 메소드에 매개변수를 하드코딩을 통해 넣었다면, 여기서는 동적으로 (원본에서 매개 변수 값이 변할 때마다, class로 만든 사본에서도 매개변수의 값이가 바뀔 수 있게) 만든다.
package com.fastcampus.ch2;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Scanner;
import org.springframework.ui.Model;
import org.springframework.validation.support.BindingAwareModelMap;
public class MethodCall3 {
public static void main(String[] args) throws Exception{
// 1. 요청할 때 제공된 값 - request.getParameterMap(); -> 스트링 쿼리의 Name과 Value를 Map 형태로 가져올 수 있다.
Map map = new HashMap();
map.put("year", "2021");
map.put("month", "10");
map.put("day", "1");
Model model = null;
Class clazz = Class.forName("com.fastcampus.ch2.YoilTellerMVC");
Object obj = clazz.newInstance();
// YoilTellerMVC.main(int year, int month, int day, Model model)
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();
Object value = map.get(paramName); // map에서 못찾으면 value는 null
// paramType중에 Model이 있으면, 생성 & 저장
if(paramType==Model.class) {
// 해당 배열에는 모델을 생성
argArr[i] = model = new BindingAwareModelMap();
} else if(value != null) { // map에 paramName이 있으면,
// value와 parameter의 타입을 비교해서, 다르면 변환해서 저장
argArr[i] = convertTo(value, paramType);
}
}
System.out.println("paramArr="+Arrays.toString(paramArr));
System.out.println("argArr="+Arrays.toString(argArr));
// Controller의 main()을 호출 - YoilTellerMVC.main(int year, int month, int day, Model model)
String viewName = (String)main.invoke(obj, argArr);
System.out.println("viewName="+viewName);
// Model의 내용을 출력
System.out.println("[after] model="+model);
// 텍스트 파일을 이용한 rendering
render(model, viewName);
} // main
private static Object convertTo(Object value, Class type) { // Class type은 매개변수의 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;
}
private static void render(Model model, String viewName) throws IOException {
String result = "";
// 1. 뷰의 내용을 한줄씩 읽어서 하나의 문자열로 만든다.
Scanner sc = new Scanner(new File("src/main/webapp/WEB-INF/views/"+viewName+".jsp"), "utf-8");
while(sc.hasNextLine())
result += sc.nextLine()+ System.lineSeparator();
// 2. model을 map으로 변환
Map map = model.asMap();
// 3.key를 하나씩 읽어서 template의 ${key}를 value바꾼다.
Iterator it = map.keySet().iterator();
while(it.hasNext()) {
String key = (String)it.next();
// 4. replace()로 key를 value 치환한다.
result = result.replace("${"+key+"}", ""+map.get(key));
}
// 5.렌더링 결과를 출력한다.
System.out.println(result);
}
}
3.DispatcherServlet
package com.fastcampus.ch2;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
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;
//Spring의 @Controller + @RequestMapping 합친 것
// 서블릿은 매소드 단위로 매핑이 안되고 클래스 단위로 매핑이 됨.
// 해당 어노테이션을 가진 라이브러리가 없어서 에러가 남. buildpath -> configure buildpath -> runtime 추가하면 톰캣에서 해당 어노테이션 가진 라이브러리가 추가됨.
//@WebServlet은 HttpServlet 상속, 매소드 이름이 service인 것, 매개변수로 request와 reponse 받는 것이 항상 고정된 형식
@WebServlet("/myDispatcherServlet") // http://localhost/ch2/myDispatcherServlet?year=2021&month=10&day=1
public class MyDispatcherServlet extends HttpServlet {
@Override
public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
Map map = request.getParameterMap();
Model model = null;
String viewName = "";
try {
Class clazz = Class.forName("com.fastcampus.ch2.YoilTellerMVC");
Object obj = clazz.newInstance();
// 1. main메서드의 정보를 얻는다.
Method main = clazz.getDeclaredMethod("main", int.class, int.class, int.class, Model.class);
// 2. main메서드의 매개변수 목록(paramArr)을 읽어서 메서드 호출에 사용할 인자 목록(argArr)을 만든다.
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();
Object value = map.get(paramName);
// paramType중에 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) { // map에 paramName이 있으면,
// value와 parameter의 타입을 비교해서, 다르면 변환해서 저장
String strValue = ((String[])value)[0]; // getParameterMap()에서 꺼낸 value는 String배열이므로 변환 필요
argArr[i] = convertTo(strValue, paramType);
}
}
// 3. Controller의 main()을 호출 - YoilTellerMVC.main(int year, int month, int day, Model model)
viewName = (String)main.invoke(obj, argArr);
} catch(Exception e) {
e.printStackTrace();
}
// 4. 텍스트 파일을 이용한 rendering
render(model, viewName, response);
} // main
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;
}
private String getResolvedViewName(String viewName) {
return getServletContext().getRealPath("/WEB-INF/views") +"/"+viewName+".jsp";
}
private void render(Model model, String viewName, HttpServletResponse response) throws IOException {
String result = "";
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
PrintWriter out = response.getWriter();
// 1. 뷰의 내용을 한줄씩 읽어서 하나의 문자열로 만든다.
Scanner sc = new Scanner(new File(getResolvedViewName(viewName)), "utf-8");
while(sc.hasNextLine())
result += sc.nextLine()+ System.lineSeparator();
// 2. model을 map으로 변환
Map map = model.asMap();
// 3.key를 하나씩 읽어서 template의 ${key}를 value바꾼다.
Iterator it = map.keySet().iterator();
while(it.hasNext()) {
String key = (String)it.next();
// 4. replace()로 key를 value 치환한다.
result = result.replace("${"+key+"}", map.get(key)+"");
}
// 5.렌더링 결과를 출력한다.
out.println(result);
}
}
4. 스스로 해보기
public class MethodCall2 {
public static void main(String[] args) throws Exception{
Class clazz = Class.forName("com.fastcampus.ch2.YoilTellerMVC");
Object obj = clazz.newInstance();
Method main = clazz.getDeclaredMethod("main", int.class, int.class, int.class, Model.class);
Model model = new BindingAwareModelMap();
System.out.println("[before] model="+model);
String viewName = (String)main.invoke(obj, new Object[] {2021, 10,1,model});
System.out.println("viewName="+viewName);
System.out.println("[after]Model=" + model);
render(model,viewName);
} // main
static void render(Model model, String viewName) throws IOException {
String result = "";
Scanner sc = new Scanner(new File("src/main/webapp/WEB-INF/views/"+viewName+".jsp"), "utf-8");
while(sc.hasNextLine())
result += sc.nextLine() + System.lineSeparator();
// 모델의 내용을 key와 value로 나눠서 Map으로 보여주는 함수 .asMap();
Map map = model.asMap();
Iterator it = map.keySet().iterator();
while(it.hasNext()) {
String key = (String)it.next();
result = result.replace("${"+key+"}", ""+map.get(key));
System.out.println(result);
}
}
}
public class MethodCall3 {
public static void main(String[] args) throws Exception{
// 1. 요청할 때 제공된 값 - request.getParameterMap(); -> 스트링 쿼리의 Name과 Value를 Map 형태로 가져올 수 있다.
Map map = new HashMap();
map.put("year", "2021");
map.put("month", "10");
map.put("day", "1");
Model model = null;
Class clazz = Class.forName("com.fastcampus.ch2.YoilTellerMVC");
Object obj = clazz.newInstance();
// YoilTellerMVC.main(int year, int month, int day, Model model)
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();
Object value = map.get(paramName);
if(paramType == Model.class) {
argArr[i] = model = new BindingAwareModelMap();
}else if(value != null) {
argArr[i] = convertTo(value, paramType);
}
}
System.out.println("paramArr=" + Arrays.toString(paramArr));
System.out.println("argArr=" + Arrays.toString(argArr));
// Controller의 main()을 호출 - YoilTellerMVC.main(int year, int month, int day, Model model)
String viewName = (String)main.invoke(obj, argArr);
System.out.println("viewName="+viewName);
// Model의 내용을 출력
System.out.println("[after] model="+model);
// 텍스트 파일을 이용한 rendering
render(model, viewName);
} // main
private static Object convertTo(Object value, Class type) { // Class type은 매개변수의 type을 의미
// type.isInstance(value) type이란 객체의 클래스가 value와 같다면, 둘 다 자료형일 경우, 자료형이 같다면의 뜻
if(type==null || value == null || type.isInstance(value))
return value;
if(String.class.isInstance(value)&& type==int.class) { //param type이 int 인데 value의 type은 String일 경우 paramType을 String에서 int로 바꿔라
return Integer.valueOf((String)value);
} else if(String.class.isInstance(value)&&type==double.class) {
return Double.valueOf((String)value);
}
return value;
}
private static void render(Model model, String viewName) throws IOException {
String result = "";
// 1. 뷰의 내용을 한줄씩 읽어서 하나의 문자열로 만든다.
Scanner sc = new Scanner(new File("src/main/webapp/WEB-INF/views/"+viewName+".jsp"), "utf-8");
while(sc.hasNextLine())
result += sc.nextLine()+ System.lineSeparator();
// 2. model을 map으로 변환
Map map = model.asMap();
// 3.key를 하나씩 읽어서 template의 ${key}를 value바꾼다.
Iterator it = map.keySet().iterator();
while(it.hasNext()) {
String key = (String)it.next();
// 4. replace()로 key를 value 치환한다.
result = result.replace("${"+key+"}", ""+map.get(key));
}
// 5.렌더링 결과를 출력한다.
System.out.println(result);
}
}
'백엔드 개발 > SpringMVC' 카테고리의 다른 글
서블릿과 JSP(2) - 서버 안에 저장소에 대하여 (0) | 2023.03.01 |
---|---|
서블릿과 JSP (1) (0) | 2023.03.01 |
관심사 분리와 MVC 패턴의 원리(1) (0) | 2023.03.01 |
관심사 분리와 MVC 패턴 (실습) (0) | 2023.03.01 |
관심사 분리와 MVC 패턴에 대해 (0) | 2023.03.01 |