Модульность функций сервлета - инкапсуляция BaseServlet

Java

1. Введение проблемы

когда мы только встретилисьservletКогда мы разработали это так:

  • Чтобы написать функцию входа пользователя, мы создаем новуюLoginServlet.
  • Для того, чтобы написать функцию регистрации пользователя, мы создаем новуюSignUpServlet.
  • Чтобы написать функцию обновления пользователя, мы создаем новуюUserUpdateServlet.
  • Чтобы написать функцию удаления пользователя, мы создаем новуюUserDeleteServlet.
  • для……

Ха-ха, это простоUser, но очевидно, что двузначные классы сущностей на обычных предметах — это нормально.

Должны ли мы создавать N сервлетов, как указано выше, для работы каждого класса сущностей? Разве это не десяткиServletсоответствовать развитию функций. . .

Далее эта статья решит эти сомнения для вас OwO


2. Как сервлет отвечает на наши запросы?

Прежде чем обсуждать, как решить вышеуказанные проблемы, давайте посмотрим, как сервлет отвечает на наши запросы.

Рисунок ниже является стандартнымServlet, мы инициируемgetилиpostЗапрос доступаservlet

Но задумывались ли вы когда-нибудь, почему нашget/postзапрос, вы можете позвонить в соответствующийdoGet/doPostметод?


Итак, давайте узнаем!

Сначала давайте посмотрим на то, что мы только что создалиLoginServletструктурная схема

На рисунке вы можете увидеть отношения наследования сервлета,servletИнтерфейс находится на верхнем уровне, иGenericServletДостигнутоservletинтерфейс.

давайте посмотрим на вершинуservletЧто в интерфейсе, давайте сосредоточимся на этомserviceметод будет делать

// servlet接口
public interface Servlet {
    // servlet被创建时进行的初始化方法
    void init(ServletConfig var1) throws ServletException;

    ServletConfig getServletConfig();

    // Called by the servlet container to allow the servlet to respond to a request.
    // 翻译下就是:由Servlet容器调用,以允许Servlet响应请求。
    void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

    String getServletInfo();

    void destroy();
}

Поэтому мы узналиservletв интерфейсеserviceМетоды используются для ответа на запросы.

Однако интерфейс просто определяет набор спецификаций, нам нужно посмотреть, что представляет собой конкретная реализация.

Итак, давайте спустимся и посмотрим, что реализованоservletинтерфейсGenericServlet.

Тем не менее, мы нажимаем вGenericServletКогда исходный код обнаруживается, что класс является абстрактным классом, вservletНа основе обогащения некоторыми методами.

В то же время внутриserviceметод является абстрактным методом, мы предполагаем, что он может быть его подклассомHttpServletДостигнутоserviceметод

public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
    // 里面比较长,这里就列一个service方法.
    public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
}


Итак, давайте спустимся и посмотрим на наследствоGenericServletизHttpServlet.

мы обнаруживаемserviceМетод реализован здесь!


Ха-ха, есть какое-то разделение?答案Вкус становится ближе.

Тогда давайте посмотрим.

Мы видим, что существует множествоifсудить.

существуетif (method.equals("GET"))Следующее называетсяdoGetМетод, Пост тот же.

// HttpServlet
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 获取请求的类型,比如Post、Get等等
    String method = req.getMethod();
    long lastModified;
    // 如果为 GET
    if (method.equals("GET")) {
        lastModified = this.getLastModified(req);
        if (lastModified == -1L) {
            // 调用doGet
            this.doGet(req, resp);
        } else {
            long ifModifiedSince;
            try {
                ifModifiedSince = req.getDateHeader("If-Modified-Since");
            } catch (IllegalArgumentException var9) {
                ifModifiedSince = -1L;
            }

            if (ifModifiedSince < lastModified / 1000L * 1000L) {
                this.maybeSetLastModified(resp, lastModified);
                this.doGet(req, resp);
            } else {
                resp.setStatus(304);
            }
        }
    } else if (method.equals("HEAD")) {
        lastModified = this.getLastModified(req);
        this.maybeSetLastModified(resp, lastModified);
        this.doHead(req, resp);
        // 如果为 Post
    } else if (method.equals("POST")) {
        // 调用 doPost
        this.doPost(req, resp);
    } else if (method.equals("PUT")) {
        this.doPut(req, resp);
    } else if (method.equals("DELETE")) {
        this.doDelete(req, resp);
    } else if (method.equals("OPTIONS")) {
        this.doOptions(req, resp);
    } else if (method.equals("TRACE")) {
        this.doTrace(req, resp);
    } else {
        String errMsg = lStrings.getString("http.method_not_implemented");
        Object[] errArgs = new Object[]{method};
        errMsg = MessageFormat.format(errMsg, errArgs);
        resp.sendError(501, errMsg);
    }

}


В этот момент мы, вероятно, понимаем,ServletКак ответить на нашу просьбу.

Резюме: если нашservletбез перезаписиserviceметод, тоservletпозвонюhttpServletосуществленныйserviceметод ответа на запрос. В зависимости от типа запроса вызываются разные методы.


3. Написание базового сервлета

Далее, это ядро ​​нашей статьиBaseServletВскоре! ! !

Идеи:

  1. НапишиBaseServletнаследоватьHttpServlet, и перезапишитеserviceметод.
  2. Используя механизм отражения, завершите单Servletмногоцелевого.
  3. давай будем нормальными业务ServletнаследоватьBaseServlet
  4. Обычное использование (описано ниже)

Далее поговорим с кодом, мы будем简单理解Немного.


3.0 Сначала ознакомьтесь с тем, как использовать

Вот краткое введение в использование

  1. Во-первых, поставить@WebSerlvetнаписано как/xxxx/`的形式,这个`/означает соответствие всем
  2. напишите свой метод
  3. URL-адрес отправки внешнего интерфейса записывается как(省略)/UserServlet/xxХорошо, этоxxОтносится к имени метода, которое вы написали сами.

Ха-ха, не правда ли, это очень легко и быстро использовать!

И, самое главное, мы изначальноServletМожно написать только один метод, и становится возможным написать多个метод, реализующий нашу功能模块化цель!


Но не волнуйтесь, давайте перейдем к самому главномуBaseServletпишу


3.1 Написание BaseServlet

Одним словом: BaseServlet в основном использует反射的机制Вышеупомянутая функция завершена.


Мы хотим продумать идею конкретной операции, и лучше всего добавить код.

  1. Прежде всего, нам нужно знать запрос, отправленный внешним интерфейсом, и какой метод мы хотим вызвать, например, вход или регистрацию?

    Ответ: по@WebServletв примечаниях/xx/*, мы можем указать, что если нам нужно вызвать метод входа в систему, мы можем записать его как/xx/login, этот логин (то есть имя метода) является ключевым словом, которое мы используем, чтобы определить, какой метод вызывать.

  2. Получить имя вызываемого метода, что, если мы вызовем этот метод?

    Ответ: через反射чтобы получить метод и вызвать его.


Полный код и анализ:

public class BaseServlet extends HttpServlet {
    // 覆写service方法
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        // 1. 这里获取URL或者URI都可以,URI: /lemonfish/UserServlet/login    URL: http://localhost/lemonfish/UserServlet/login
        String requestURI = req.getRequestURI();
        // 2. 获取最后`/`的索引
        int beginIndex = requestURI.lastIndexOf("/");
        // 3. 使用substring,获取方法名称
        String methodName = requestURI.substring(beginIndex + 1);
        try {
            /***
             * 记住谁调用了“service”方法,this就是谁,
             * 因为我们自己编写的UserServlet继承了BaseServlet
             * 因此,这个service方法也是属于UserServlet的
             * 而前端访问的是UserServlet
             * 因此 this 是 UserServlet的一个对象
             * 
             * 4. 这里 根据 方法名称 和 方法参数的class类型 ,利用反射获取UserServlet的该方法。
             * 
             * 如果大家这两行代码看不太懂,可以先去复习下 反射 的知识。
             ***/
            Method method = this.getClass().getDeclaredMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);

            // 5. 使用this调用该方法。
            method.invoke(this, req, resp);
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}


4. Пример демонстрации BaseServlet

4.1 Напишите базовый сервлет

как указано выше


4.2 Напишите UserServlet и наследуйте BaseServlet

В этом UserServlet есть два метода, один из нихloginЛогин, одинsignUpрегистр


4.3 Запустите Tomcat и получите доступ к UserServlet

Запускаем tomcat и вводим в адресную строкуhttp://localhost/lemonfish/UserServlet/login(Это регулируется в соответствии с вашим собственным портом и путем, мой порт здесь 80, иapplicationContextдаlemonfish), нажмите клавишу Enter.

Посмотрим, что выдает консоль.

Дангданг! ! !

Вот успешный выводLog In, указывая на то, что мы на полпути.

Но не волнуйтесь, давайте попробуем еще разsignUpметод

Ха-ха, теперь мы можем быть уверены!

Мы успешно прошлиBaseServletзавершил визитServletВ то же время можно использовать несколько методов.

Таким образом, когда мы разрабатываем, мы можем написать соответствующий код в соответствии с потребностями нашего бизнеса.xxxServletВот и все.


4.4 Обработка небольшой ямы пересылки

При написании пути пересылки не забудьте добавить/О, это будет добавлено непосредственно к корневому пути

если написать какhello.jsp, затем добавляется к текущему пути (то естьUserServletпод),

такserviceметод пойдетUserServletНайдите имя нижеhello.jspметод, он сообщит об ошибке!

request.getRequestDispatcher("/hello.jsp").forward(request, response);


5. Адрес исходного кода DEMO

Для всеобщего удобства я решил загрузить исходный код этой демонстрации прямо наGithub

Всем удобно читать и учиться. Если вы считаете, что это хорошо, пожалуйста, вознаградите меня.Star(●'◡'●)


Хотя облако кода использовалось много раньше, в основном его следует использовать в последнее время.Github.


6. Пишите в конце

пакет одинBaseServletЭто значительно повысило эффективность нашей разработки, особенно до того, как мы коснулись фреймворка.

Я надеюсь, что каждый сможет что-то получить после прочтения~~(●'◡'●)

Если вы сочтете это полезным, вы можете нажать 👍b( ̄▽ ̄)d, это лучше всего, ххх