java—在SpringBoot应用程序部署后动态添加servlet或刷新现有servletMap

xuo3flqw  于 2021-06-30  发布在  Java
关注(0)|答案(1)|浏览(373)

我需要一个简单的api代理来为我的spring启动应用程序创建基于来自数据库的值的代理Map。我找到了这个api代理https://github.com/mitre/http-proxy-servlet 这很简单,也符合我的目的
我发现创建动态代理servlet的唯一方法如下所示。

@ManagedBean
public final class ExecutorListener implements ServletContextInitializer {

private static final String TARGET_URI = "targetUri";

@Override
public void onStartup(ServletContext servletContext) throws ServletException {
WebApplicationContext springContext = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
EndpointsRepo endpointrepo = springContext.autowireBean(EndpointsRepo.class);
List<Endpoint> endpoints = endpointrepo.findAll();
for(Endpoint endpoint: endpoints){
    ServletRegistration.Dynamic dispatcher = servletContext.addServlet(endpoint.getEndpointCode(), new ProxyServlet());
    dispatcher.setLoadOnStartup(1);
    dispatcher.setInitParameter(TARGET_URI, endpoint.getEndpointUrl());
    dispatcher.addMapping(endpoint.getUrlPattern());
}

}

代理现在可以正常工作,除非我更新db中的Map值,我需要这些值反映在动态代理中。
有没有办法在不重新启动spring引导应用程序的情况下刷新servletMap?我尝试使用applicationcontext.refresh(),但它是由db datasource发出无法关闭的消息并导致内存泄漏引起的。
如果我尝试在运行时使用springservletregistration添加bean,那么创建的Map将不起作用。似乎只有在启动之前配置servlet,servletMap才能工作,正如springservlet文档所说的那样。

s4chpxco

s4chpxco1#

您不能在初始化阶段之后添加其他servlet,因为servlet api规范(参见Javaservlet规范,版本4.0)禁止这样做。在第4.4节中 ServletContext#addServlet 方法是:
这些方法只能在应用程序初始化期间从 contexInitialized a方法 ServletContextListener 执行或从 onStartup a方法 ServletContainerInitializer 实施。
你仍然可以重复使用 ProxyServlet 类,但您需要编写自己的servlet(Map到 /* ),这将创建 ProxyServlet ,配置它们并将请求路由到正确的请求。
编辑:基本上你需要这样一个类:

public class MetaProxyServlet extends HttpServlet {

   /**
    * To configure a {@link ProxyServlet} without modifying its code.
    */
   private static class ProxyServletConfig implements ServletConfig {

      private static final String  P_TARGET_URI = "targetUri";
      private final String         targetUri;

      @Override
      public String getInitParameter(String name) {
         return P_TARGET_URI.equals(name) ? targetUri : null;
      }
      // Other methods
   }

   /**
    * Serves to modify the pathInfo of the request.
    */
   private static class RewrittenServletRequest extends HttpServletRequestWrapper {

      private final String pathInfo;

      @Override
      public String getPathInfo() {
         return pathInfo;
      }
      // Other methods
   }

   private TreeMap<String, ProxyServlet> proxyServlets = new TreeMap<>();

   /**
    * Adds a reverse proxy between {@code pathPrefix} of this servlets namespace
    * and {@code targetUri}.
    * 
    * @param pathPrefix
    *        must start with '/'
    * @param targetUri
    *        URI to be proxied
    * @throws ServletException
    */
   public void addRedirect(String pathPrefix, String targetUri) throws ServletException {
      final ProxyServlet servlet = new ProxyServlet();
      servlet.init(new ProxyServletConfig(pathPrefix, getServletContext(), targetUri));
      proxyServlets.put(pathPrefix, servlet);
   }

   @Override
   protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      String pathInfo = req.getPathInfo();
      Entry<String, ProxyServlet> entry = proxyServlets.floorEntry(pathInfo);
      // entry.getKey() precedes, but not necessarily is a prefix for pathInfo
      if (entry != null && pathInfo.startsWith(entry.getKey())) {
         String rewrittenPathInfo = pathInfo.substring(entry.getKey().length());
         entry.getValue()//
              .service(new RewrittenServletRequest(req, rewrittenPathInfo), resp);
      } else {
         resp.sendError(HttpServletResponse.SC_NOT_FOUND);
      }
   }

}

完整的代码在要点上。

相关问题