2021. 10. 15. 21:42ㆍ개인노트
스프링에서 엄청나게 편하게 사용중인 RequestMapping어노테이션의 작동원리를 알아보자!
서블릿과 필터로 만들어보는 dispatcher-servlet을 대신할 필터하나
컨트롤러 부분의 MainController
커스텀 어노테이션 부분의 MyRequestMapping
각각 페이지를 연결시킬 index.jsp join.jsp login.jsp 를 만들어서 준비한다.
최초에는 Web 프로젝트를 생성해준다.spring이 아니라 구 방식의 프로젝트
그리고 reflect란 이름으로 프로젝트 생성 및 톰캣 9.0사용 및 경로지정.
Web.xml에 DisPatcher 서블릿을 대신하여 url를 맵핑하여 연결해줄때 사용할 필터를 등록한다..!
web.xml에 필터 생성 및 등록.
<filter>
<filter-name>dispatcher</filter-name>
<filter-class>com.test.reflect.filter.Dispatcher</filter-class>
</filter>
<filter-mapping>
<filter-name>dispatcher</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
그리고 각각의 클래스나 어노테이션을 생성해주었다.
MyRequestMapping 어노테이션 (연결할 url주소를 받기위해 String value를 하나 추가되어있음)
package com.test.reflect.anno;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestMapping {
String value();
}
MainController (뷰리졸버가 없다는 가정하에 /WEB-INF와 .jsp를 그대로 매칭시켰다.)
아래는 /login /index /join 각각 3개의 연결에 대한 페이지처리를 수행한다.
package com.test.reflect.controller;
import com.test.reflect.anno.MyRequestMapping;
public class MainController {
@MyRequestMapping(value = "/login")
public String login() {
System.out.println("login() 호출");
return "/WEB-INF/login.jsp";
}
@MyRequestMapping("/index")
public String index() {
System.out.println("index() 호출");
return "/WEB-INF/index.jsp";
}
@MyRequestMapping("/join")
public String join() {
System.out.println("join() 호출");
return "/WEB-INF/join.jsp";
}
}
Dispatcher 필터!!
요청받은 url에 따라서 endPoint를 가져와서 어노테이션에 동일한 value값이 존재하는경우 메서드를 수행하면서 매칭되는 파일에다가 연결시켜준다!
ex)http://localhost:8080/reflect/login를 호출한경우
endPoint = /login
MainController에 MyRquestMapping 어노테이션이 존재하는지 확인한 뒤 존재한다면 value값을 확인하고
/login과 동일할 경우 메서드를 수행하여 결과값을 받는다(결과값 = /WEB-INF/login.jsp)
그리고 foward방식으로 필터에서 연결시켜준다!
package com.test.reflect.filter;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.test.reflect.anno.MyRequestMapping;
import com.test.reflect.controller.MainController;
public class Dispatcher implements Filter{
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
//URL주소 매핑을 위해 파싱하기(replace를 수행하면 기존 contextpath인 /reflect 를 제외한걸 가져옴.
String endPoint = request.getRequestURI().replaceAll(request.getContextPath(), "");
System.out.println(endPoint);//실제 호출한 주소 ex) /login
MainController mainController = new MainController();
Method[] methods = mainController.getClass().getDeclaredMethods();//해당 클래스에 존재하는 모든 메서드를 가져온다
for(Method method : methods) {
//어노테이션이 한개인경우
/*
Annotation annotation =method.getDeclaredAnnotation(MyRequestMapping.class);
*/
//어노테이션이 여러개인경우
Annotation[] annotations =method.getDeclaredAnnotations();
Annotation annotation=null;
for(Annotation anno : annotations) {
if(anno.annotationType().equals(MyRequestMapping.class)) {
annotation = anno;
}
break;
}
MyRequestMapping myRequestMapping = (MyRequestMapping) annotation;
String value = myRequestMapping.value(); //어노테이션에 선언된 value값(URL주소 매핑을 위한 값을 받는부분)
if(value.equals(endPoint)) {
try {
String path = (String) method.invoke(mainController);
RequestDispatcher dis = request.getRequestDispatcher(path);
dis.forward(request, response);
} catch (Exception e) {
e.printStackTrace();
}
break;
}
}
}
}
각각의 페이지를 요청하면 해당페이지가 나타난다.
##그리고 !! Controller에 DTO나 VO가 존재하여 거기에 해당되는 값을 받을때 자동으로 주입받는 형태가 스프링에서는 지원이 되는데..!##
테스트용 LoginDTO JoinDTO 생성
package com.test.reflect.dto;
public class LoginDTO {
private String username;
private String password;
@Override
public String toString() {
return "LoginDTO [username=" + username + ", password=" + password + "]";
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
package com.test.reflect.dto;
public class JoinDTO {
private String username;
private String password;
private String email;
@Override
public String toString() {
return "JoinDTO [username=" + username + ", password=" + password + ", email=" + email + "]";
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
그리고 MainController를 각각의 DTO를 사용하도록 변경!
package com.test.reflect.controller;
import com.test.reflect.anno.MyRequestMapping;
import com.test.reflect.dto.JoinDTO;
import com.test.reflect.dto.LoginDTO;
public class MainController {
@MyRequestMapping(value = "/login")
public String login(LoginDTO logindto) {
System.out.println("login() 호출");
System.out.println(logindto);
return "/WEB-INF/login.jsp";
}
@MyRequestMapping("/index")
public String index() {
System.out.println("index() 호출");
return "/WEB-INF/index.jsp";
}
@MyRequestMapping("/join")
public String join(JoinDTO joindto) {
System.out.println("join() 호출");
System.out.println(joindto);
return "/WEB-INF/join.jsp";
}
}
Dispatcher필터를 수정 파라미터를 기준으로 setter를 가져올 수 있도록 하는 메서드, 그에 해당하는 이름으로 변경시켜주는 메서드등이 추가 되었다!
package com.test.reflect.filter;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Enumeration;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.test.reflect.anno.MyRequestMapping;
import com.test.reflect.controller.MainController;
public class Dispatcher implements Filter{
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
//URL주소 매핑을 위해 파싱하기(replace를 수행하면 기존 contextpath인 /reflect 를 제외한걸 가져옴.
String endPoint = request.getRequestURI().replaceAll(request.getContextPath(), "");
System.out.println(endPoint);//실제 호출한 주소 ex) /login
MainController mainController = new MainController();
Method[] methods = mainController.getClass().getDeclaredMethods();//해당 클래스에 존재하는 모든 메서드를 가져온다
for(Method method : methods) {
//어노테이션이 한개인경우
/*
Annotation annotation =method.getDeclaredAnnotation(MyRequestMapping.class);
*/
//어노테이션이 여러개인경우
Annotation[] annotations =method.getDeclaredAnnotations();
Annotation annotation=null;
for(Annotation anno : annotations) {
if(anno.annotationType().equals(MyRequestMapping.class)) {
annotation = anno;
}
break;
}
MyRequestMapping myRequestMapping = (MyRequestMapping) annotation;
String value = myRequestMapping.value(); //어노테이션에 선언된 value값(URL주소 매핑을 위한 값을 받는부분)
if(value.equals(endPoint)) {
try {
Parameter[] params = method.getParameters();
String path = null;
if(params.length!=0) {//파라미터 있을때
Object dtoInstance=null;
for (Parameter param : params) {
dtoInstance = param.getType().newInstance();//해당 클래스로 만들어준다
setData(dtoInstance, request);
}
path = (String) method.invoke(mainController,dtoInstance);
}else {//없을때
path = (String) method.invoke(mainController);
}
RequestDispatcher dis = request.getRequestDispatcher(path);
dis.forward(request, response);
} catch (Exception e) {
e.printStackTrace();
}
break;
}
}
}
private <T> void setData(T instance, HttpServletRequest request) {
//모든 파라미터의 이름들을 가져온다. ex) username,password
Enumeration<String> keys = request.getParameterNames();
//key--> set으로 변형 ex) username --> setUsername
while(keys.hasMoreElements()) {
String key = keys.nextElement();
String methodKey = keyToMethodKey(key);
Method[] methods = instance.getClass().getDeclaredMethods();
for (Method method : methods) {
//만약 username의 파라미터가 존재한다면 setUsername메서드를 찾아서 해당값을 넘겨주고 메서드를 실행시킨다!
if(method.getName().equals(methodKey)) {
try {
method.invoke(instance, request.getParameter(key));
} catch (Exception e) {
e.printStackTrace();
}
}
}//methods
}//while
}
/**
* 파라미터 값을 받으면 setter의 기본형태로 만들어서 string을 return한다
* @param key
* @return String methodKey
*/
private String keyToMethodKey(String key) {
String firstKey = "set";
String upperKey = key.substring(0,1).toUpperCase();
String remainKey = key.substring(1);
return firstKey+upperKey+remainKey;
}
}
##일반적인 요청시 파라미터가 없는경우##
##파라미터가 존재하고 postman을통해 요청했을때##
DTO에 존재하는 setter를 찾아서 제대로 작동한 모습이다!!
자바 리플렉션을 통하여 스프링에서 어노테이션등을 붙여서 자동으로 페이지 연결 시켜주거나, 그 안에있는 DTO나 VO등에 set을 해주는 것 등을 어떠한 방식으로 하는지 알아보았다!
'개인노트' 카테고리의 다른 글
개발자 채용 과제 수정해보기 (0) | 2021.10.28 |
---|---|
(인터셉터+어노테이션)로그인 체크하기(session) (0) | 2021.10.19 |
Spring AOP 테스트 및 커스텀태그 적용해보기 (0) | 2021.10.14 |
개발자 채용 과제 도전 및(+) 후기(....) (0) | 2021.10.12 |
Hyper-V 사용안할때 끄는법 (0) | 2021.09.26 |