반환타입이 String이면, View이름을 반환해야한다. 반환타입이 void이면, 맵핑주소가 View이름이 된다. 반환타입이 ModelAndView이면, 컨트롤 내부에서 ① ModelAndView 생성 ② 작업 후 결과 ModelAndView 에 저장 ③ setViewName() 으로 View 이름지정 ④ ModelAndView 참조변수를 반환
MVC패턴 원리
매개변수이름을 얻는방법
① Reflection API : -parameters옵션을 넣고 컴파일해야함. ( Java8, Jdk1.8 부터 가능 )
② Class파일 얻어오기
예제 1 ( Reflection API 사용 )
※ StringJoiner( 구분자, 접두사, 접미사)
예제 2
리플랙션이란? : 자바의 클래스, 인터페이스 내에 어떤 멤버들이 있는지 조사하는 것
Class 객체를 얻는 방법 3가지
메서드
용도
1
Class clazz = 클래스이름 .class;
[ 클래스로부터 얻는방법 ]
2
Class clazz = Class.forName("패키지... 클래스이름");
[ 클래스로부터 얻는방법 ]
3
Class clazz = 객체참조변수.getClass();
[ 객체로부터 얻는방법 ]
패키지와 타입정보 얻기
메서드
용도
예시
1
Package getPackage()
패키지 정보 읽기
패키지 : ch12.com.exam01
2
String getSimpleName()
패키지를 제외한 타입이름
클래스 간단 : Car
3
String getName()
패키지를 포함한 전체 타입 이름
클래스 전체이름 : ch12.com.exam01.Car
멤버 정보얻기
메서드
용도
1
Constructorof[] getDeclaredConstructors()
생성자 정보읽기
2
Field[] getDeclaredFields()
필드(멤버변수) 정보 읽기
3
Method[] getDeclaredMthods()
메서드 정보읽기
리소스 경로 얻기
메서드
용도
1
URL getResouce(String name)
리소스 파일의 URL 리턴 (단순경로)
2
InputStream getResourceAsStream(String name)
리소스 파일의 InputStream 리턴 ( 경로의 파일을 열어 그안에 데이터를 읽음)
Invoke 메서드 : Method를 동적으로 실행시켜 주는 메서드
예제 3 ( Refalction API 이용 )
public class MethodCall2 {
public static void main(String[] args) throws Exception{
// 1. YoilTellerMVC 의 객체를 생성
Class clazz = Class.forName("com.fastcampus.ch2.YoilTellerMVC");
Object obj = clazz.newInstance();
// 2. main메서드의 정보를 가져온다.
Method main = clazz.getDeclaredMethod("main", int.class, int.class, int.class, Model.class);
// 매서드 , 매개변수 3개, 모델 (일치필요)
// 3. Model을 생성
Model model = new BindingAwareModelMap(); // BindingAwareModelMap는 Model(인터페이스)를 구현한 것
System.out.println("[before] model="+model);
// 4. main메서드를 호출
// String viewName = obj.main(2021, 10, 1, model); //아랫줄과 동일
String viewName = (String)main.invoke(obj, new Object[] { 2021, 10, 1, model }); //Reflection API를 이용한 호출
// 1번에서 만든 객체, 배열로 받게 되있음( main 메서드의 매개변수 String args~ )
// 여기에 맞는 메서드를 찾아서 가져온다.
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);
}
}
예제 3
public class MethodCall3 {
public static void main(String[] args) throws Exception{
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(); // main메서드의 매개변수 목록을 가져온다.
Object[] argArr = new Object[main.getParameterCount()]; // 매개변수 갯수와 같은 길이의 Object배열을 생성
// 동적구성
// 요청할 때의 넘어온 값으로 객체배열을 생성
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) {
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을 원격프로그램으로
@WebServlet이란? 스프링의 @Controller(클래스앞) + @ReqeustMapping(메서드앞) 이 합쳐진 것
서블릿은... ① HttpServlet을 꼭 상속받아야한다. ② 메서드이름은 Service라는 이름으로 꼭 해야한다. ③ 메서드 매개변수는 (HttpServletReqeust request, HttpServlet reponse) 를 꼭 적어줘야한다. ④ 서블릿에서는 메서드단위 맵핑 x, 클래스단위로만 가능 ⑤ 그래서, 서블릿은 스프링에 비해서 서블릿을 많이 만들어야하는 단점이 있음.