Java 프레임워크 만들기 - JSP

자바 어노테이션으로 서블릿의 리퀘스트 매핑 만들기 2 - Java Annotation Servlet Request Mapping, 이클립스(Eclipse)

carrotweb 2021. 5. 10. 19:12
728x90
반응형

이전에는 "@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)를 호출하기 위해서는 메소드가 있는 클래스의 인스턴가 필요하여 메소드매핑맵이 추가되었습니다.

728x90
반응형