📜 ⬆️ ⬇️

Servlets and Reflection

More recently, in the article Servlets - a small trick with Reflection , I understood the technique with which you can get a url of the form:
host/servletName/methodName.

If we go further, then with the help of Reflection it is possible to implement the construction of the following form:
Controllers


Where, we have several classes of controllers in the project, and each of them has its own actions. And most importantly, we can pass parameters from the URI to the controller methods. For example:
host/servletName/projects/add?title=hello



Controllers


First, let's deal with the servlet. We have the same BaseServlet class inherited from 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); } } } } 


The most interesting thing happens in the service method. First we get the name of the controller using the getControllerName helper method, where the URI is parsed. After we use the ControllerFactory - the controller factory that creates the object of the controller class we need. The ControllerFactory code is very simple:

 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; } 


It remains to deal with the method call. To get started, we get its name using getMethodName, and then we pass the necessary parameters to callMethod. There is a search for a suitable method in the controller, and after his call.

All controllers that we want to use must be registered in the defaultMap method of the ControllerFactory.

Let's go to the base class of the controller.

 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; } } 


The setHttp method allows us to teach our controller to talk. Here you can add methods for authorization, redirect, etc.

Now we will make a test controller named Index. It will have two methods, index and 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); } } 


Do not forget to configure the mapping in web.xml as follows:
 <?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> 


Test our servlet. At the request of / host / demo / we will see the line “Index method” in the console, and at the request of / host / demo / index / show? Name = Anton - “Hello Anton”.

Passing parameters to a method


To parse parameters from URIs in methods, you have to use a construction of the form:
 String strValue = request.getParametr("intParamName"); Integer value = Intger.parseInt(strValue); 

It is quite tedious to perform such simple actions many times, and the beauty of the code suffers. The problem of parsing parameters can be solved by forwarding them as parameters to the method. For this you need to use annotations.

 package projectManagment.controllers; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) public @interface Param { String name(); } 


The show method now looks like this:
 public static void show (@Param(name = "name") String name, @Param(name = "age") Integer age) { System.out.println("Hello " + name + ", you are " + age + " years old"); } 


That's not all, it is necessary in the callMethod method to pass parameters from the URI to the method being called.
 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); } } } 


Now, the query / host / demo / index / show? Name = Anton & age = 20, the console displays “Hello Anton, you are 20 years old”.

Source: https://habr.com/ru/post/120715/


All Articles