EndpointsServlet class 在 Google 的端点中做什么?

What does the EndpointsServlet class do in Google's Endpoints?

首先,我是 java servlets、maven 项目和 apis 的初学者。

我正在 github 上执行以下操作 tutorial on getting started with google endpoints, which is a tutorial implementing the following maven project source code。在web.xml上,只有一个名为Servlet的EndpointsServlet,如下所示:

<!-- wrap the backend with Endpoints Framework v2. -->
<servlet>
    <servlet-name>EndpointsServlet</servlet-name>
    <servlet-class>com.google.api.server.spi.EndpointsServlet</servlet-class>
    <init-param>
        <param-name>services</param-name>
        <param-value>com.example.echo.Echo</param-value>
    </init-param>
</servlet>

我不明白的是为什么项目上没有其他的servlets?主目录下只有3 java classes个,其中none个是servlet文件。我假设这个项目是一个示例 api,与任何其他 servlet 项目一样具有服务器端逻辑(例如路由和响应请求),这意味着应该不止这个 servlet。

web.xml 上的评论是关于它的作用的明显线索,但我真的不知道用端点框架包装后端意味着什么。另外,我实际上得到了 EndpointsServlet.java 文件,它说 servlet 是一个“无代理 API 服务的处理程序。这个 servlet 理解并在 JSON-REST 中回复。同样,我不真正理解这条评论,甚至阅读它也不理解 servlet 做了什么。下面的 Servlet 代码:

package com.google.api.server.spi;

import com.google.api.server.spi.SystemService.EndpointNode;
import com.google.api.server.spi.config.ApiConfigException;
import com.google.api.server.spi.config.model.ApiClassConfig.MethodConfigMap;
import com.google.api.server.spi.config.model.ApiConfig;
import com.google.api.server.spi.config.model.ApiMethodConfig;
import com.google.api.server.spi.dispatcher.PathDispatcher;
import com.google.api.server.spi.handlers.ApiProxyHandler;
import com.google.api.server.spi.handlers.CorsHandler;
import com.google.api.server.spi.handlers.EndpointsMethodHandler;
import com.google.api.server.spi.handlers.ExplorerHandler;
import com.google.common.collect.ImmutableList;

import java.io.IOException;
import java.util.Enumeration;
import java.util.List;
import java.util.Map.Entry;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * A handler for proxy-less API serving. This servlet understands and replies in JSON-REST.
 */
public class EndpointsServlet extends HttpServlet {
  private static final String EXPLORER_PATH = "explorer";

  private ServletInitializationParameters initParameters;
  private SystemService systemService;
  private PathDispatcher<EndpointsContext> dispatcher;
  private CorsHandler corsHandler;

  @Override
  public void init(ServletConfig config) throws ServletException {
    super.init(config);
    ClassLoader classLoader = getClass().getClassLoader();
    this.initParameters = ServletInitializationParameters.fromServletConfig(config, classLoader);
    this.systemService = createSystemService(classLoader, initParameters);
    this.dispatcher = createDispatcher();
    this.corsHandler = new CorsHandler();
  }

  @Override
  public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
    String method = getRequestMethod(request);
    if ("OPTIONS".equals(method)) {
      corsHandler.handle(request, response);
    } else {
      String path = Strings.stripSlash(
          request.getRequestURI().substring(request.getServletPath().length()));
      EndpointsContext context = new EndpointsContext(method, path, request, response,
          initParameters.isPrettyPrintEnabled());
      if (!dispatcher.dispatch(method, path, context)) {
        response.setStatus(HttpServletResponse.SC_NOT_FOUND);
        response.getWriter().append("Not Found");
      }
    }
  }

  private String getRequestMethod(HttpServletRequest request) {
    Enumeration headerNames = request.getHeaderNames();
    String methodOverride = null;
    while (headerNames.hasMoreElements()) {
      String headerName = (String) headerNames.nextElement();
      if (headerName.toLowerCase().equals("x-http-method-override")) {
        methodOverride = request.getHeader(headerName);
        break;
      }
    }
    return methodOverride != null ? methodOverride.toUpperCase() : request.getMethod();
  }

  private PathDispatcher<EndpointsContext> createDispatcher() {
    PathDispatcher.Builder<EndpointsContext> builder = PathDispatcher.builder();
    List<EndpointNode> endpoints = systemService.getEndpoints();
    // We're building an ImmutableList here, because it will eventually be used for JSON-RPC.
    ImmutableList.Builder<EndpointsMethodHandler> handlersBuilder = ImmutableList.builder();
    for (EndpointNode endpoint : endpoints) {
      ApiConfig apiConfig = endpoint.getConfig();
      MethodConfigMap methods = apiConfig.getApiClassConfig().getMethods();
      for (Entry<EndpointMethod, ApiMethodConfig> methodEntry : methods.entrySet()) {
        if (!methodEntry.getValue().isIgnored()) {
          handlersBuilder.add(
              new EndpointsMethodHandler(initParameters, getServletContext(), methodEntry.getKey(),
                  apiConfig, methodEntry.getValue(), systemService));
        }
      }
    }
    ImmutableList<EndpointsMethodHandler> handlers = handlersBuilder.build();
    for (EndpointsMethodHandler handler : handlers) {
      builder.add(handler.getRestMethod(), Strings.stripTrailingSlash(handler.getRestPath()),
          handler.getRestHandler());
    }
    ExplorerHandler explorerHandler = new ExplorerHandler();
    builder.add("GET", EXPLORER_PATH, explorerHandler);
    builder.add("GET", EXPLORER_PATH + "/", explorerHandler);
    builder.add("GET", "static/proxy.html", new ApiProxyHandler());
    return builder.build();
  }

  private SystemService createSystemService(ClassLoader classLoader,
      ServletInitializationParameters initParameters) throws ServletException {
    try {
      SystemService.Builder builder = SystemService.builder()
          .withDefaults(classLoader)
          .setStandardConfigLoader(classLoader)
          .setIllegalArgumentIsBackendError(initParameters.isIllegalArgumentBackendError())
          .setDiscoveryServiceEnabled(true);
      for (Class<?> serviceClass : initParameters.getServiceClasses()) {
        builder.addService(serviceClass, createService(serviceClass));
      }
      return builder.build();
    } catch (ApiConfigException | ClassNotFoundException e) {
      throw new ServletException(e);
    }
  }

  /**
   * Creates a new instance of the specified service class.
   *
   * @param serviceClass the class of the service to create
   */
  protected <T> T createService(Class<T> serviceClass) {
    try {
      return serviceClass.newInstance();
    } catch (InstantiationException e) {
      throw new RuntimeException(
          String.format("Cannot instantiate service class: %s", serviceClass.getName()), e);
    } catch (IllegalAccessException e) {
      throw new RuntimeException(
          String.format("Cannot access service class: %s", serviceClass.getName()), e);
    }
  }
}

EndpointsServlet 处理具有特定路径前缀的所有 API 调用。它需要一个 RESTful API 调用并将其转换为 POJO(s) 并将其分派给您编写的 Java 方法,然后序列化该方法的 return 值JSON 的方法。它根据您注释代码的方式执行此操作。