Совсем недавно, в статье Сервлеты — маленький трюк с Reflection, разбирался прием, с помощью которого можно получить url вида:
Если пойти дальше, то с помощью Reflection можно реализовать конструкцию следующего вида:

Где, у нас в проекте есть несколько классов контроллеров, и у каждого из них свои действия. А самое главное, в методы контроллеров мы можем передавать параметры из URI. Например:
Сначала разберёмся с сервлетом. У нас есть все тот же класс BaseServlet, наследуемый от HttpServlet.
Самое интересное происходит в методе service. Сначала мы получаем имя контроллера с помощью вспомогательного метода getControllerName, где парсится URI. После мы используем ControllerFactory – фабрику контроллеров, которая создает объект нужного нам класса контроллера. Код ControllerFactory очень прост:
Остается разобраться с вызовом метода. Для начала получаем его имя с помощью getMethodName, а дальше передаем необходимые параметры в callMethod. Тут происходит поиск подходящего метода в контроллере, а после его вызов.
Все контроллеры, которые мы хотим использовать должны быть прописаны в методе defaultMap у ControllerFactory.
Перейдем к базовому классу контроллера.
Метод setHttp позволяет нам научить разговаривать наш контроллер. Сюда же можно добавить методы для авторизации, редиректа и т.д.
Теперь сделаем тестовый контроллер под именем Index. В нем будут два метода index и show.
Не забываем настроить mapping в web.xml следующим образом:
Протестируем наш сервлет. По запросу /host/demo/ мы увидим в консоли строку “Index method”, а по запросу /host/demo/index/show?name=Anton — “Hello Anton”.
Для парсинга параметров из URI в методах приходится использовать конструкцию вида:
Совершать такие простые действия по многу раз довольно нудно, да и красота кода страдает. Проблему парсинга параметров можно решить, передовая их как параметры к методу. Для этого нужно использовать аннотации.
Метод show теперь выглядит следующим образом:
Это еще не все, необходимо в методе callMethod передавать параметры из URI в вызываемый метод.
Теперь на запрос /host/demo/index/show?name=Anton&age=20, консоль выводит “Hello Anton, you are 20 years old”.
host/servletName/methodName.Если пойти дальше, то с помощью Reflection можно реализовать конструкцию следующего вида:
Где, у нас в проекте есть несколько классов контроллеров, и у каждого из них свои действия. А самое главное, в методы контроллеров мы можем передавать параметры из URI. Например:
host/servletName/projects/add?title=helloКонтроллеры
Сначала разберёмся с сервлетом. У нас есть все тот же класс BaseServlet, наследуемый от HttpServlet.
package projectManagment; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import projectManagment.controllers.Controller; public class BaseServlet extends HttpServlet { private ControllerFactory factory; public BaseServlet () { super(); factory = new ControllerFactory(); } @Override protected void service(HttpServletRequest request, HttpServletResponse response) { try { String name = getControllerName(request); if (name == null) name = "index"; Controller controller = factory.create(name); Controller.setHttp(request, response, getServletContext()); String method = getMethodName(request); if (method == null) method = "index"; callMethod(controller, method, request, response); } catch (Exception e) { System.out.println("Error: " + e.getMessage()); e.printStackTrace(); } } public String getControllerName (HttpServletRequest request) { String path = request.getRequestURI().substring(request.getContextPath().length()); if (path == null || "".equals(path) || "/".equals(path)) return null; String controller = path.substring(1, path.lastIndexOf("/")); if (controller != null && !"".equals(controller)) return controller; return null; } private String getMethodName (HttpServletRequest request) { String method = request.getRequestURI().substring(request.getContextPath().length()); if (method != null && !"".equals(method) && !"/".equals(method)) return method.substring(method.lastIndexOf("/") + 1, method.length() ); return null; } private void callMethod (Controller controller, String methodName, HttpServletRequest request, HttpServletResponse response) throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { for (Method method : controller.getClass().getMethods()) { if (method.getName().equalsIgnoreCase(methodName)) { controller.getClass().getMethod(methodName, method.getParameterTypes()).invoke(controller); } } } }
Самое интересное происходит в методе service. Сначала мы получаем имя контроллера с помощью вспомогательного метода getControllerName, где парсится URI. После мы используем ControllerFactory – фабрику контроллеров, которая создает объект нужного нам класса контроллера. Код ControllerFactory очень прост:
package projectManagment; import java.util.HashMap; import java.util.Map; import projectManagment.controllers.*; public class ControllerFactory { public ControllerFactory() { map = defaultMap(); } public Controller create (String controllerName) { Class ControllerClass = (Class) map.get(controllerName); if (ControllerClass == null) throw new RuntimeException(getClass() + " was unable to find an controller named '" + controllerName + "'."); Controller controllerInstance = null; try { controllerInstance = (Controller) ControllerClass.newInstance(); } catch (Exception e) { e.printStackTrace(); } return controllerInstance; } protected Map<String, Class<?>> defaultMap() { Map<String, Class<?>> map = new HashMap<String, Class<?>>(); map.put("index", Index.class); return map; } protected Map<String, Class<?>> map; }
Остается разобраться с вызовом метода. Для начала получаем его имя с помощью getMethodName, а дальше передаем необходимые параметры в callMethod. Тут происходит поиск подходящего метода в контроллере, а после его вызов.
Все контроллеры, которые мы хотим использовать должны быть прописаны в методе defaultMap у ControllerFactory.
Перейдем к базовому классу контроллера.
package projectManagment.controllers; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class Controller { static protected HttpServletResponse response; static protected HttpServletRequest request; static protected ServletContext context; public Controller () { request = null; response = null; context = null; } static public void setHttp (HttpServletRequest req, HttpServletResponse res, ServletContext con) { request = req; response = res; context = con; } }
Метод setHttp позволяет нам научить разговаривать наш контроллер. Сюда же можно добавить методы для авторизации, редиректа и т.д.
Теперь сделаем тестовый контроллер под именем Index. В нем будут два метода index и show.
package projectManagment.controllers; public class Index extends Controller { public static void index () { System.out.println("Index method"); } public static void show () { String value = request.getParameter("name"); System.out.println("Hello " + value); } }
Не забываем настроить mapping в web.xml следующим образом:
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <servlet> <servlet-name>Demo</servlet-name> <servlet-class>projectManagment.BaseServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>Demo</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
Протестируем наш сервлет. По запросу /host/demo/ мы увидим в консоли строку “Index method”, а по запросу /host/demo/index/show?name=Anton — “Hello Anton”.
Передача параметров в метод
Для парсинга параметров из URI в методах приходится использовать конструкцию вида:
String strValue = request.getParametr("intParamName"); Integer value = Intger.parseInt(strValue);
Совершать такие простые действия по многу раз довольно нудно, да и красота кода страдает. Проблему парсинга параметров можно решить, передовая их как параметры к методу. Для этого нужно использовать аннотации.
package projectManagment.controllers; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) public @interface Param { String name(); }
Метод show теперь выглядит следующим образом:
public static void show (@Param(name = "name") String name, @Param(name = "age") Integer age) { System.out.println("Hello " + name + ", you are " + age + " years old"); }
Это еще не все, необходимо в методе callMethod передавать параметры из URI в вызываемый метод.
private void callMethod(Controller controller, String methodName, HttpServletRequest request, HttpServletResponse response) throws NoSuchMethodException, RuntimeException, SecurityException, IllegalAccessException, InvocationTargetException { for (Method method : controller.getClass().getMethods()) { if (method.getName().equalsIgnoreCase(methodName)) { Object[] params = new Object[method.getParameterTypes().length]; Annotation[][] parameterAnnotations = method.getParameterAnnotations(); Class[] parameterTypes = method.getParameterTypes(); int i = 0; for(Annotation[] annotations : parameterAnnotations){ Class parameterType = parameterTypes[i]; for(Annotation annotation : annotations){ if(annotation instanceof Param){ Param myAnnotation = (Param) annotation; Object value = null; if (parameterType.getSimpleName().equals("Integer")) { try { value = Integer.parseInt(request.getParameter(myAnnotation.name())); } catch (NumberFormatException e) { value = null; } } else { value = request.getParameter(myAnnotation.name()); } params[i++] = value; System.out.println("param: " + parameterType.getSimpleName()); System.out.println("name : " + myAnnotation.name()); System.out.println("value: " + value); } } } controller.getClass().getMethod(method.getName(), method.getParameterTypes()).invoke(controller, params); } } }
Теперь на запрос /host/demo/index/show?name=Anton&age=20, консоль выводит “Hello Anton, you are 20 years old”.
