Java 프레임워크 만들기 - JSP

자바 서블릿으로 컨트롤러 만들기(메소드 처리) 1 - Java Servlet Controller Request Mapping Method Invoke, 이클립스(Eclipse)

carrotweb 2021. 4. 20. 16:28
728x90
반응형

이전에 "Request Mapping"를 통해 모든 요청(Request)에 대해 자동으로 서비스(Service Class)로 호출되어 처리되도록 처리하였습니다. 그러나 모든 요청(Request)을 처리할 때마다 새로운 서비스(Service Class)를 생성해야 하고 서비스(Service Class)는 하나의 메소드만 처리되게 되어 다양한 메소드를 만들수 없습니다. 또한 단순한 요청(Request)이나 웹 페이지 호출이라면 비효율적입니다. 그래서 요청(Request)을 그룹핑해서 하나의 컨트롤러(Controller)로 만들고 요청(Request)별로 컨트롤러(Controller)의 메소드(Method)가 호출되어 처리되게 하겠습니다. SpringFramework의 Controller과 유사한 기능으로 처리되게 하겠습니다.

1. "test2" 프로젝트의 "Java Resources"에 "src/main/resources"에서 "/com/home/project/test2/config"에 있는 "requestmapping.properties"파일을 수정합니다.

/test1.do=com.home.project.test2.service.TestService1

기존은 하나의 요청(Request)에 하나의 서비스(Service Class)가 처리되도록 되어 있습니다.

그래서 서비스(Service Class)가 아닌 컨트롤러(Controller)와 메소드(Method)로 처리되게 수정합니다.

/test1.do=com.home.project.test2.controller.TestRootController@test1

"@"를 구분자로 하여 다음과 같이 컨트롤러(Controller)와 메소드(Method)가 됩니다.

public class TestRootController {
	public RequestDispatcher test1() {
	}
}

동적으로 생성된 컨트롤러(Controller) 클래스에서 공통으로 실행하기 위해 interface(인터페이스)를 만듭니다.

2. "test2" 프로젝트의 "Java Resources"에서 "src/main/java"의 "com.home.project.test2"를 선택한 후 오른쪽 버튼을 클릭하여 컨텍스트 메뉴 [New > Package]를 클릭하여 "Name"의 기존 패키지명에 ".controller"를 추가하고 "Finish"버튼을 클릭합니다. 추가된 "com.hom.project.test2.controller"에서 오른쪽 버튼을 클릭하여 컨텍스트 메뉴 [New > Interface]를 클릭하여 "ITestController.java"를 생성합니다.

 

"AddRequestFunction"메소드는 요청(Request)과 컨트롤러(Controller)의 메소드(Method)가 매핑되도록 합니다.

"doProcess"메소드는 "TestDispatcherServlet.java"의 "service"에서 컨트롤러(Controller)를 호출할 수 있게 합니다.

public interface ITestController {

	/**
	 * URL에 대한 메소드(클래스 함수)를 맵에 추가합니다. 
	 * @param url 요청 URL
	 * @param methodName 메소드(클래스 함수) 명
	 * @return 맵 추가 여부
	 */
	public abstract boolean addRequestFunction(String url, String methodName);

	/**
	 * 프로세스를 처리합니다.
	 * @param request HTTP 요청 객체
	 * @param response HTTP 응답 객체
	 * @return RequestDispatcher 객체
	 */
	public abstract RequestDispatcher doProcess(HttpServletRequest request, HttpServletResponse response);
}

 

3. "test2" 프로젝트의 "Java Resources"에서 "src/main/java"의 "com.home.project.test2.controller"를 선택한 후 오른쪽 버튼을 클릭하여 컨텍스트 메뉴 [New > Class]를 클릭하여 "TestController.java"를 생성합니다.

"TestController.java"에 "ITestController" 인터페이스가 구현(implements)되도록 코드를 수정합니다.

public class TestController implements ITestController {

"ITestController"의 메소드 구현을 위해 "Add unimplemented methods"(구현되지 않은 메소드(클래스 함수) 추가)를 클릭하여 오버라이드(Override)합니다.

@Override
public boolean AddRequestFunction(String url, String methodName) {
	// TODO Auto-generated method stub
	return false;
}

@Override
public RequestDispatcher doProcess(HttpServletRequest request, HttpServletResponse response) {
	// TODO Auto-generated method stub
	return null;
}

 

요청(Request) URL별로 컨트롤러(Controller)의 메소드(Method)를 관리하는 "methodMappingMap" 변수를 추가합니다.

/**
 * URL-Method 핸들러 맵
 */
private Map<String, Method> methodMappingMap = new HashMap<String, Method>();

 

동적으로 컨트롤러(Controller) 클래스에서 메소드(Method) 찾기 위해 "getMethod"메서드를 추가합니다.

계승(extends) 받는 컨트롤러(Controller) 클래스의 모든 메소드들은 "HttpServletRequest"과 "HttpServletResponse"를 파라메타로 가지고 있어야 찾을 수 있습니다.

/**
 * 컨트롤 클래스에있는 메소드를 찾아 Method 객체를 가져옵니다. 
 * @param methodName 메소드 명
 * @return Method 객체
 */
private Method getMethod(String methodName) {
	Method method = null;
	Class<?> thisClass = this.getClass();

	Class<?>[] parameterTypes = new Class[2];
	parameterTypes[0] = HttpServletRequest.class;
	parameterTypes[1] = HttpServletResponse.class;

	try {
		method = thisClass.getMethod(methodName, parameterTypes);
		if (null != method) {
			System.out.println(this.toString() + "의 " + method.getName() + "메소드를 찾음");
		} else {
			System.out.println(this.toString() + "의 " + methodName + "메소드를 찾지 못함");
		}
	} catch (NoSuchMethodException | SecurityException e) {
		System.out.println(this.toString() + "의 " + methodName + "메소드를 찾지 못함");
		method = null;
		//e.printStackTrace();
	}

	return method;
}

 

수정된 "requestmapping.properties"에서 읽은 요청(Request) URL과 메소드(Method)를 "methodMappingMap"에 추가하기 위해 "addRequestFunction"를 수정합니다.

@Override
public boolean addRequestFunction(String url, String methodName) {
	boolean result = false;
	if (!methodMappingMap.containsKey(url)) {
		Method method = getMethod(methodName);
		if (null != method) {
			methodMappingMap.put(url, method);
			System.out.println("[RequestMapping] URL : " + url + "에 " + this.toString() + "의 " + methodName + "메소드를 매핑함");
			result = true;
		}
	} else {
		System.out.println("[RequestMapping] URL : " + url + "이 중복되어 " + this.toString() + "의 " + methodName + "메소드를 매핑하지 못함");
	}
	return result;
}

 

중복되는 요청(Request)를 처리하지 않게 하였습니다.

중복 처리하고 싶으시면 다음 처럼 수정하시면 됩니다.

@Override
public boolean addRequestFunction(String url, String methodName) {
	boolean result = false;
	Method method = getMethod(methodName);
	if (null != method) {
		result = true;
		if (!methodMappingMap.containsKey(url)) {
			methodMappingMap.put(url, method);
			System.out.println("[RequestMapping] URL : " + url + "에 " + this.toString() + "의 " + methodName + "메소드를 매핑함");
		} else {
			methodMappingMap.replace(url, method);
			System.out.println("[RequestMapping] URL : " + url + "에 " + this.toString() + "의 " + methodName + "메소드를 중복 매핑함");
		}
	}
	return result;
}

 

메소드를 "invoke"로 호출하기 위해 "methodInvoke"메서드를 추가합니다.

/**
 * 컨트롤 클래스에있는 메소드를 호출합니다.
 * @param method 메소드 명
 * @param request HTTP 요청 객체
 * @param response HTTP 응답 객체
 * @return RequestDispatcher 객체
 */
private RequestDispatcher methodInvoke(Method method, HttpServletRequest request, HttpServletResponse response) {
	RequestDispatcher requestDispatcher = null;

	Object parameter[] = new Object[2];
	parameter[0] = request;
	parameter[1] = response;

	try {
		requestDispatcher = (RequestDispatcher)method.invoke(this, parameter);
		System.out.println(this.toString() + "의 " + method.getName() + "메소드를 호출함");
	} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
		System.out.println(this.toString() + "의 " + method.getName() + "메소드를 호출하지 못함");		requestDispatcher = null;
		//e.printStackTrace();
	}

	return requestDispatcher;
}

 

요청(Request) URL에 대해 컨트롤러(Controller) 클래스의 메소드(Method)가 호출되도록 "doProcess"를 수정합니다.

@Override
public RequestDispatcher doProcess(HttpServletRequest request, HttpServletResponse response) {
	String contextPath = request.getContextPath();
	String requestURI = request.getRequestURI();
	if (null != contextPath && 0 < contextPath.length()) {
		requestURI = requestURI.replaceFirst(contextPath, "");
	}

	Method method = methodMappingMap.get(requestURI);
	if (null == method) {
		return null;
	}

	return methodInvoke(method, request, response);
}

 

이어서, "TestController"를 계승(extends) 받아 "TestRootController"를 생성하고 "TestDispatcherServlet"클래스의 "service"메소드에서 "Controller"로 처리하게 하겠습니다.

728x90
반응형