이전에는 "@TestAnnRequestMapping"어노테인션을 "TestController"의 기능인 "addRequestFunction"메소드를 요청(Request) URL과 메소드(Method) 처리하였습니다.
이번에는 컨트롤러(Controller)에서 "TestController"를 계승(extneds)하지 않고 처리하겠습니다.
1. "TestServletContextListener.java"의 "contextInitialized"에서 메소드(Method)와 컨트롤러(Controller) 클래스 인스턴스를 매핑하하기 위해 메소드매핑맵(methodMappingMap)을 추가합니다.
// Method-Class 맵
Map<Method, Object> methodMappingMap = new HashMap<Method, Object>();
요청매핑맵(Request Mapping Map)에서 스캔한 클래스를 "ITestController"으로 형 변환하여 처리한 부분을 메소드(Method)로 수정합니다.
Map<String, ITestController> requestMappingMap = new HashMap<String, ITestController>();
// URL-Method 맵
Map<String, Method> requestMappingMap = new HashMap<String, Method>();
스캔한 클래스를 "ITestController"으로 형 변환하는 부분과 "addRequestFunction"메소드 처리 부분을 주석처리합니다.
"methodMappingMap"에 메소드(Method)와 스캔한 클래스의 인스턴스를 추가합니다.
"requestMappingMap"에 "@TestAnnRequestMapping"어노테인션의 "value"와 메소드(Method)를 추가합니다.
//ITestController controller = (ITestController) controllerInstance;
Method[] methods = findClass.getDeclaredMethods();
for (Method method : methods) {
TestAnnRequestMapping annoRequestMapping = method.getAnnotation(TestAnnRequestMapping.class);
if (null != annoRequestMapping) {
//if (controller.addRequestFunction(annoRequestMapping.value(), method.getName())) {
// requestMappingMap.put(annoRequestMapping.value(), controller);
//}
methodMappingMap.put(method, controllerInstance);
requestMappingMap.put(annoRequestMapping.value(), method);
System.out.println("[RequestMapping] URL : " + annoRequestMapping.value() + "에 " + controllerInstance.toString() + "의 " + method.getName() + "메소드를 매핑함");
}
}
서블릿 콘텍스트(ServletContext)에 메소드매핑맵(methodMappingMap)을 추가합니다.
sce.getServletContext().setAttribute("methodMappingMap", methodMappingMap);
"contextInitialized"메소드 전체 소스입니다.
public void contextInitialized(ServletContextEvent sce) {
System.out.println("ServletContext 초기화 실행");
String basePackage = sce.getServletContext().getInitParameter("ComponentScan-BasePackage");
System.out.println("basePackage : " + basePackage);
//Map<String, ITestController> requestMappingMap = new HashMap<String, ITestController>();
// Method-Class 맵
Map<Method, Object> methodMappingMap = new HashMap<Method, Object>();
// URL-Method 맵
Map<String, Method> requestMappingMap = new HashMap<String, Method>();
System.out.println("[RequestMapping]를 시작합니다.");
List<Class<?>> classes = TestClassScanner.scan(basePackage);
for (Class<?> findClass : classes) {
Class<?>[] interfaces = findClass.getInterfaces();
for (int i = 0; i < interfaces.length; ++i) {
System.out.println(interfaces[i].toString());
}
//System.out.println("[Class] " + findClass.getName());
TestAnnController annoController = findClass.getAnnotation(TestAnnController.class);
if (null != annoController) {
Object controllerInstance = null;
try {
controllerInstance = findClass.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
//e.printStackTrace();
System.out.println(findClass.getName() + "의 컨트롤러 인스턴스를 생성하지 못함");
}
if (null != controllerInstance) {
//ITestController controller = (ITestController) controllerInstance;
Method[] methods = findClass.getDeclaredMethods();
for (Method method : methods) {
TestAnnRequestMapping annoRequestMapping = method.getAnnotation(TestAnnRequestMapping.class);
if (null != annoRequestMapping) {
//if (controller.addRequestFunction(annoRequestMapping.value(), method.getName())) {
// requestMappingMap.put(annoRequestMapping.value(), controller);
//}
methodMappingMap.put(method, controllerInstance);
requestMappingMap.put(annoRequestMapping.value(), method);
System.out.println("[RequestMapping] URL : " + annoRequestMapping.value() + "에 " + controllerInstance.toString() + "의 " + method.getName() + "메소드를 매핑함");
}
}
}
}
}
System.out.println("[RequestMapping]를 종료합니다.");
sce.getServletContext().setAttribute("methodMappingMap", methodMappingMap);
sce.getServletContext().setAttribute("RequestMapping", requestMappingMap);
}
2. "TestDispatcherServlet.java"의 "service"메소드에서 서블릿 콘텍스트(ServletContext)에 추가된 "methodMappingMap"를 가져옵니다.
@SuppressWarnings("unchecked")
Map<Method, Object> methodMappingMap = (Map<Method, Object>)request.getServletContext().getAttribute("methodMappingMap");
기존 "requestMappingMap"를 "ITestController"에서 "Method"으로 수정합니다.
@SuppressWarnings("unchecked")
Map<String, Method> requestMappingMap = (Map<String, Method>)request.getServletContext().getAttribute("RequestMapping");
//Map<String, ITestController> requestMappingMap = (Map<String, ITestController>)request.getServletContext().getAttribute("RequestMapping");
"ITestController"으로 형 변환한 부분을 주석처리하고
//ITestController controller = (ITestController) requestMappingMap.get(requestURI);
//if (null != controller) {
"Method"으로 형 변환 수정합니다.
Method method = (Method) requestMappingMap.get(requestURI);
if (null != method) {
"methodMappingMap"에서 메소드(Method)와 매핑된 클래스의 인스턴스를 가져옵니다.
Object controllerInstance = methodMappingMap.get(method);
기존 "doProcess"메소드를 주석처리합니다.
//modelAndView = controller.doProcess(request, response);
"requestMappingMap"에서 가져온 메소드(Method)와 클래스의 인스턴스를 이용하여 메소드를 실행시킵니다.
Object parameter[] = new Object[2];
parameter[0] = request;
parameter[1] = response;
try {
modelAndView = (ModelAndView)method.invoke(controllerInstance, parameter);
System.out.println("[RequestMapping] URL : " + requestURI + "요청을 " + controllerInstance.toString() + "의 " + method.getName() + "메소드로 실행함");
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
//e.printStackTrace();
System.out.println("[RequestMapping] URL : " + requestURI + "요청을 " + controllerInstance.toString() + "의 " + method.getName() + "메소드를 호출하지 못함");
}
3. "TestRootController.java"에서 계승(extends) 받은 "TestController"를 지웁니다.
public class TestRootController extends TestController {
public class TestRootController {
4. "Servers"탭에서 "tomcat8"를 선택하고 "start"버튼(start the server)을 클릭합니다.
"Console"탭을 클릭하면 클래스를 스캔하고 발견된 어노테이션(Annotation)으로 요청(Request) URL과 메소드를 매핑한 것을 확인할 수 있습니다.
[Console]
ServletContext 초기화 실행
basePackage : com.home.project.test2
[RequestMapping]를 시작합니다.
[RequestMapping] URL : /test2.do에 com.home.project.test2.controller.TestRootController@4394315의 test2메소드를 매핑함
[RequestMapping] URL : /test1.do에 com.home.project.test2.controller.TestRootController@4394315의 test1메소드를 매핑함
[RequestMapping]를 종료합니다.
servletContext에 속성 추가 - 속성명 : methodMappingMap, 속성 값 : {public com.home.project.test2.controller.ModelAndView com.home.project.test2.controller.TestRootController.test2(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)=com.home.project.test2.controller.TestRootController@4394315, public com.home.project.test2.controller.ModelAndView com.home.project.test2.controller.TestRootController.test1(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)=com.home.project.test2.controller.TestRootController@4394315}
servletContext에 속성 추가 - 속성명 : RequestMapping, 속성 값 : {/test1.do=public com.home.project.test2.controller.ModelAndView com.home.project.test2.controller.TestRootController.test1(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse), /test2.do=public com.home.project.test2.controller.ModelAndView com.home.project.test2.controller.TestRootController.test2(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)}
웹 브라우저에서 "http://localhost:8080/test2/test1.do"를 입력합니다.
"Console"탭을 클릭하면 요청(Request) URL로 메소드가 처리된 것을 확인할 수 있습니다.
요청(Requset) URL과 메소드가 매핑되어 컨트롤러(Controller) 클래스에 처리없이 바로 호출할 수 있게 되었습니다. 다만 메소드(Method)를 호출하기 위해서는 메소드가 있는 클래스의 인스턴가 필요하여 메소드매핑맵이 추가되었습니다.