Learning record 02 -- a large number of servlets that appear when processing requests using native servlets

Recently, when reviewing servlets, I found that when using native servlets to process requests, one servlet can only process one request, and when there are multiple businesses, only many servlets can be written. Although springmvc and other frameworks can solve this problem, and no one will use native servlets to process requests in actual development, I still want to try to write a simple tool to solve this problem. First review the native servlet processing a request:

@WebServlet("/login")
public class LoginServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("loginServlet");
    }
}

However, a controller has many businesses. For example, there are login, register, loginbywx (wechat scanning code login), checkEmail (check whether the mailbox is available), checkUsername (check whether the user name is available), etc. in the userController. Plus other controllers, the servlet needs to write hundreds of servlets, which is obviously not feasible. So I wonder if I can let my back-end know where the request should go by standardizing the url of the request, For example, /user/login Do is to call the login() method in the userController class, so I wrote a class called DispatcherServlet (distributor) to intercept the url in the service method to get two things: which method I want to call and which class this method is in:

@WebServlet("*.do")
public class DispatcherServlet extends HttpServlet {
 @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        //For all /* do request set encoding set
        req.setCharacterEncoding("utf8");

        String uri = req.getRequestURI();
        //The unified uri format is: /controller/operation Do, get the method name to be executed
        int startIndex = uri.lastIndexOf("/")+1;
        int endIndex = uri.indexOf(".");
        //Get the method name to be called
        String operation = uri.substring(startIndex,endIndex);

        //Get the name of the controller
        String beanName = uri.substring(uri.indexOf("/")+1,uri.lastIndexOf("/"));

    }
}

The next step is to create the objects of this class for use when calling methods and find the methods in this class through reflection. I use the configuration file method here. The xml content and xml parsing methods are as follows:

 

I have five business modules and a total of five controllers. This xml starts to look like spring, but I don't need to introduce constraint files such as DTDs. In other words, the tags here are written as "myBeans", "myBean" or even "abc". The key is xml parsing

<?xml version="1.0" encoding="utf-8"?>
<beans>
    <bean id="user" class="com.joker.controller.UserController"/>
    <bean id="department" class="com.joker.controller.DepartmentController"/>
    <bean id="article" class="com.joker.controller.ArticleController"/>
    <bean id="meeting" class="com.joker.controller.MeetingController"/>
    <bean id="home" class="com.joker.controller.HomeController"/>
</beans>

Parsing xml:

The core code here is "

                    Element element = (Element) node;
                    String id = element.getAttribute("id");"

After strongly converting the Element object, call the getAttribute () method to obtain the id written by myself in the tag and the value in the class

@WebServlet("*.do")
public class DispatcherServlet extends HttpServlet {

    private static Map<String,Object> requestMapping = new HashMap<>();

    //The following static code block is run only once. When this class is loaded (the first time a *.do request is received), the Mapping relationship between configuration initialization uri and controller of XML
    static {
        try {
            InputStream is = DispatcherServlet.class.getClassLoader().getResourceAsStream("applicationContext.xml");
            //Parsing xml files
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
            Document dom = documentBuilder.parse(is);
            //Take out the contents in <bean></bean> or <bean/ > and package them into com sun. org. apache. xerces. internal. dom. Deepnodelistimpl object
            NodeList bean = dom.getElementsByTagName("bean");

            for (int i = 0; i < bean.getLength(); i++) {
                Node node = bean.item(i);
                if (node.getNodeType() == Node.ELEMENT_NODE){
                    Element element = (Element) node;
                    String id = element.getAttribute("id");
                    String className = element.getAttribute("class");
                    Object o = Class.forName(className).getDeclaredConstructor().newInstance();
                    requestMapping.put(id,o);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

After this step, five key value pairs are stored in the member variable requestMapping. The key is "user", the value is the UserController object, the key is "article", and the value is the ArticleController object.

At this time, the above information obtained by intercepting the url can be used, such as /user/login For a request like do, I get two strings through the subString() method, user and login. User is the key in my member variable requestMapping. For login, I just need to call the getdeclaraedmethod method in the Class class Class and fill in the login parameter to get this method and call it.

Write the service method:

@Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        //For all /* do request set encoding set
        req.setCharacterEncoding("utf8");

        String uri = req.getRequestURI();
        //The unified uri format is: /controller/operation Do, get the method name to be executed
        int startIndex = uri.lastIndexOf("/")+1;
        int endIndex = uri.indexOf(".");
        //Get the method name to be called
        String operation = uri.substring(startIndex,endIndex);

        //Get the name of the controller
        String beanName = uri.substring(uri.indexOf("/")+1,uri.lastIndexOf("/"));

        //When loading this class, the The information configured in the XML will create instances of all controllers. Now, according to the uri, find the corresponding controller instance in the container
        Object controllerBean = requestMapping.get(beanName);
        //After obtaining the instance, find the method name to be called according to the uri, find the method through reflection and call
        try {
            Method methodToInvoke = controllerBean.getClass().getDeclaredMethod(operation, HttpServletRequest.class, HttpServletResponse.class);
            methodToInvoke.setAccessible(true);
            methodToInvoke.invoke(controllerBean,req,resp);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

The code of the whole class is as follows:

/**
 * @Author:Joker
 * @Date:2022/5/3 10:09
 * @Description:The page distributor finds the corresponding controller instance according to the url address requested by the front end and invoke s its corresponding method
 */

@WebServlet("*.do")
public class DispatcherServlet extends HttpServlet {

    private static Map<String,Object> requestMapping = new HashMap<>();

    //The following static code block is run only once. When this class is loaded (the first time a *.do request is received), the Mapping relationship between configuration initialization uri and controller of XML
    static {
        try {
            InputStream is = DispatcherServlet.class.getClassLoader().getResourceAsStream("applicationContext.xml");
            //Parsing xml files
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
            Document dom = documentBuilder.parse(is);
            //Take out the contents in <bean></bean> or <bean/ > and package them into com sun. org. apache. xerces. internal. dom. Deepnodelistimpl object
            NodeList bean = dom.getElementsByTagName("bean");

            for (int i = 0; i < bean.getLength(); i++) {
                Node node = bean.item(i);
                if (node.getNodeType() == Node.ELEMENT_NODE){
                    Element element = (Element) node;
                    String id = element.getAttribute("id");
                    String className = element.getAttribute("class");
                    Object o = Class.forName(className).getDeclaredConstructor().newInstance();
                    requestMapping.put(id,o);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        //For all /* do request set encoding set
        req.setCharacterEncoding("utf8");

        String uri = req.getRequestURI();
        //The unified uri format is: /controller/operation Do, get the method name to be executed
        int startIndex = uri.lastIndexOf("/")+1;
        int endIndex = uri.indexOf(".");
        //Get the method name to be called
        String operation = uri.substring(startIndex,endIndex);

        //Get the name of the controller
        String beanName = uri.substring(uri.indexOf("/")+1,uri.lastIndexOf("/"));

        //When loading this class, the The information configured in the XML will create instances of all controllers. Now, according to the uri, find the corresponding controller instance in the container
        Object controllerBean = requestMapping.get(beanName);
        //After obtaining the instance, find the method name to be called according to the uri, find the method through reflection and call
        try {
            Method methodToInvoke = controllerBean.getClass().getDeclaredMethod(operation, HttpServletRequest.class, HttpServletResponse.class);
            methodToInvoke.setAccessible(true);
            methodToInvoke.invoke(controllerBean,req,resp);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

A beggar SpringMVC

Tags: Java

Posted by yarin on Fri, 03 Jun 2022 02:57:58 +0530