Совсем недавно, в статье Сервлеты — маленький трюк с 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”.