【问题标题】:How can I get Jetty to scan the Maven dependency jar files in the class path?如何让 Jetty 扫描类路径中的 Maven 依赖 jar 文件?
【发布时间】:2022-01-22 00:19:49
【问题描述】:

Eclipse 会自动将所有 Maven 依赖项添加到类路径中。

从 Eclipse 启动时,如何让 Jetty 扫描类路径上的 jar 文件以查找 Web 片段?

打包放置在webapp/WEB-INF/lib/*.jar中,扫描就可以了。

Server server = new Server(8080);

WebAppContext webapp = new WebAppContext();
webapp.setContextPath("/myapp");
webapp.setBaseResource(Resource.newResource(new File("webapp")));

// https://stackoverflow.com/questions/11768113/cant-get-jetty-to-scan-for-annotated-classes
webapp.setConfigurations(new Configuration[] { //
    new AnnotationConfiguration(), // @WebServlet, @WebListener...
    new WebXmlConfiguration(), // webapp/WEB-INF/web.xml
    new WebInfConfiguration(), // ?
    new MetaInfConfiguration(), // ?
    new FragmentConfiguration(), // e.g. zkwebfragment-9.6.0.1.jar!/META-INF/web-fragment.xml
});

if(RUNNING_FROM_IDE)
{
  // add project classes to classpath (e.g. target/classes)
  webapp.getMetaData().setWebInfClassesDirs(Arrays.asList(Resource.newResource(MyAppMainClass.class.getProtectionDomain().getCodeSource().getLocation())));

  // how to add maven dependencies?
  // webapp.getMetaData().addWebInfJar(???);
}

server.setHandler(webapp);
server.start();

【问题讨论】:

    标签: java maven jetty


    【解决方案1】:

    尝试 3(也基于 Joakim 的回答):设置 ContainerIncludeJarPattern 以扫描类路径中已经存在的所有内容。无需任何额外的类路径修改。

        Server server = new Server(8080);
        WebAppContext webapp = new WebAppContext();
        webapp.setContextPath("/test");
        webapp.setBaseResource(Resource.newResource(new File("webapp").getCanonicalFile()));
        // https://www.eclipse.org/jetty/documentation/jetty-9/index.html#configuring-webapps
        // order apparently important
        webapp.setConfigurations(new Configuration[] { //
            new WebInfConfiguration(), //
            new WebXmlConfiguration(), //
            new MetaInfConfiguration(), //
            new FragmentConfiguration(), //
            new AnnotationConfiguration(), //
        });
        webapp.setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern", ".*");
        server.setHandler(webapp);
        server.setDumpAfterStart(true);
        server.start();
    

    我的其他问题似乎与 ZK 相关,因此我为此创建了 new question

    【讨论】:

      【解决方案2】:

      根据 Joakim 的回答,我现在有:

      Server server = new Server(port);
      Configuration.ClassList classlist = Configuration.ClassList.setServerDefault(server);
      classlist.addAfter(JettyWebXmlConfiguration.class.getName(), AnnotationConfiguration.class.getName());
      
      WebAppContext webapp = new WebAppContext();
      webapp.setContextPath("/myapp");
      webapp.setBaseResource(Resource.newResource(new File(isRunningFromIde ? "./webapp" : "/path/to/webapp").getCanonicalFile()));    
      webapp.setWelcomeFiles(new String[] { "index.html" });
      //    webapp.setInitParameter(..., ...));
      //    webapp.setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern", "");
      //    webapp.setAttribute("org.eclipse.jetty.server.webapp.WebInfIncludeJarPattern", "");
      
      if(isRunningFromIde)
      {
        List<String> extraClassPath = new ArrayList<>();
        // target/classes
        extraClassPath.add(Paths.get(MyAppMain.class.getProtectionDomain().getCodeSource().getLocation().toURI()).toString());
        // maven dependencies
        for(URL url : ((URLClassLoader) MyAppMain.class.getClassLoader()).getURLs()) {
          if(url.getPath().endsWith(".jar")) {
            extraClassPath.add(Paths.get(url.toURI()).toString());
          }
        }
        webapp.setExtraClasspath(extraClassPath.stream().collect(Collectors.joining(";")));
      }
      
      server.setHandler(webapp);
      server.setDumpAfterStart(true);
      server.start();
      

      感谢服务器转储提示,我想我现在也明白为什么会收到replicate resource warnings from ZK。 WebAppClassLoader 和 AppClassLoader 都包含相同的条目。由于上述额外的类路径,IDE 启动。 docker 容器可能是因为我以java -cp &lt;webapp&gt;/WEB-INF/lib/* MyAppMain.class 开头。

      |  +> WebAppClassLoader{403716510}@1810399e
      |  |  +> URLs size=41
      |  |  |  +> file:<maven-repo-or-webapp-dir>/WEB-INF/lib/zk-9.6.0.1.jar
      |  |  |  ...
      |  |  +> sun.misc.Launcher$AppClassLoader@4e0e2f2a
      |  |  |  +> URLs size=41
      |  |  |  |  +> file:<maven-repo-or-webapp-dir>/WEB-INF/lib/zk-9.6.0.1.jar
      |  |  |  |  ...
      

      【讨论】:

      • 您可能不想添加来自 URLClassLoader 的所有 jar,而是特定的。用 webapp.setParentLoaderPriority(true) 替换 if(isRunningFromIde) 中的整个块,并且没有额外的类路径步骤,您将获得相同的最终结果
      • 确实,我刚刚发现了父类加载器属性。我刚刚在摆弄它,但无法让码头扫描它以获取注释。我希望这个父类加载器将是“容器类路径”,并将 ContainerIncludeJarPattern 设置为.*,但仍然没有运气:(
      • 嗯,我错了。它确实扫描注释,只是 zk web 片段似乎没有正确加载。
      【解决方案3】:

      不要手动设置配置,这是第一个错误。

      您的努力基本上破坏了配置,因为您不尊重原始默认配置或微妙的订单要求。

      这是错误的方式,直接设置webapp配置。

      webapp.setConfigurations(new Configuration[] { //
          new AnnotationConfiguration(), // @WebServlet, @WebListener...
          new WebXmlConfiguration(), // webapp/WEB-INF/web.xml
          new WebInfConfiguration(), // ?
          new MetaInfConfiguration(), // ?
          new FragmentConfiguration(), // e.g. zkwebfragment-9.6.0.1.jar!/META-INF/web-fragment.xml
      });
      

      这是正确的方法,通过调整默认配置列表 在服务器级别。

      Configuration.ClassList classlist = Configuration.ClassList
        .setServerDefault(server);
      classlist.addBefore(
        "org.eclipse.jetty.webapp.JettyWebXmlConfiguration",
        "org.eclipse.jetty.annotations.AnnotationConfiguration");
      

      这应该足够了。

      您可以在服务器转储中看到默认配置顺序。

      server.setDumpAfterStart(true);
      server.start();
      
      // and then on the output (see your logger) ...
      |  |  |  +> Configurations Servlet Dump WebApp@4c163e3 size=5
      |  |  |  |  +> org.eclipse.jetty.webapp.WebInfConfiguration@5e403b4a
      |  |  |  |  +> org.eclipse.jetty.webapp.WebXmlConfiguration@5117dd67
      |  |  |  |  +> org.eclipse.jetty.webapp.MetaInfConfiguration@5be49b60
      |  |  |  |  +> org.eclipse.jetty.webapp.FragmentConfiguration@2931522b
      |  |  |  |  +> org.eclipse.jetty.webapp.JettyWebXmlConfiguration@7674b62c
      |  |  |  +> Handler attributes Servlet Dump WebApp@4c163e3 size=3
      

      此转储功能很有用,因为它可能会告诉您 $HOME/.m2/repository jar 文件不存在于 webapp 自己的类路径中。

      |  |  |  +> WebAppClassLoader{Servlet Dump WebApp}@5fb759d6
      |  |  |  |  +> URLs size=1
      |  |  |  |  |  +> file:/tmp/jetty-0_0_0_0-8080-ROOT_war-_-any-15598896298108484560/webapp/WEB-INF/classes/
      

      如果您在此处没有看到您的 Maven 存储库文件,那么是时候配置您的 WebAppContext.setExtraClasspath(String) 以在启动 web 应用程序之前包含它们了。

      另一个需要注意的功能是 2 个扫描正则表达式,用于识别哪些内容(类文件)被扫描。一个用于 webapp 类,一个用于服务器类。这两个都是相关的 用于发现 servlet 规范中的任何内容,无论是带注释的侦听器、带注释的 servlet、web 片段、servlet 容器初始化器、jsp 级别的详细信息、websocket 级别的详细信息等)

      如果您想配置它们,它们都被设置为WebAppContext.setAttribute(String key, String value) 上的属性。

      默认情况下,这些值为空,表示扫描所有 jar。

      // This one controls server/container level discovery.
      
      // An example of limited scanning would be
      webappContext.setAttribute(
        "org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern"
        ".*/[^/]*servlet-api-[^/]*\\.jar$|.*[^/]*jstl-.*\\.jar$|.*/[^/]*taglibs.*\\.jar$");
      
      
      // This one controls webapp specific discovery.
      webappContext.setAttribute(
        "org.eclipse.jetty.server.webapp.WebInfIncludeJarPattern"
        ".*/[^/]*foo-[^/]*\\.jar$|.*/[^/]*bar.*\\.jar$");
      

      这方面的一个例子是......

      你有一个带有...的网络应用程序

      WEB-INF/lib/
                 foo-api-1.2.3.jar
                 foo-1.2.3.jar
                 bar-0.9.jar
                 boo-1.0.jar
      

      并定义了 webapp 扫描...

      webappContext.setAttribute(
          "org.eclipse.jetty.server.webapp.WebInfIncludeJarPattern",
          ".*/.*foo-api-[^/]\.jar$|./.*bar-[^/]\.jar$|./.*wibble[^/]*\.jar$");
      

      然后将匹配并扫描以下文件:

      WEB-INF/lib/
                 foo-api-1.2.3.jar
                 bar-0.9.jar
      

      【讨论】:

      • 哇,非常感谢。正在消化...
      • 我尝试按照您的说明进行操作(请参阅我的其他答案),但现在我发现基本上我的所有依赖项都是重复的:在 WebAppClassLoader 和 AppClassLoader 中。
      • 文档 (eclipse.org/jetty/documentation/jetty-9/…) 给出了“直接在 WebAppContext 上设置列表”的明确示例,所以我认为这种方法并没有完全错误。
      【解决方案4】:

      我让它以某种方式工作。仍然想知道是否有更标准的方法来实现这一点:

      // launching from IDE: manually add WEB-INF/classes and WEB-INF/lib/*.jar entries
      webapp.getMetaData().setWebInfClassesDirs(Arrays.asList(Resource.newResource(MyAppMainClass.class.getProtectionDomain().getCodeSource().getLocation())));
      for(URL url : ((URLClassLoader) MyAppMainClass.class.getClassLoader()).getURLs()) {
        if(url.getPath().endsWith(".jar")) {
          webapp.getMetaData().addWebInfJar(Resource.newResource(url));
        }
      }
      

      我还在jetty-maven-plugin 中找到了MavenWebInfConfiguration。我使用:

      ((WebAppClassLoader)  webapp.getClassLoader()).addClassPath(...)
      

      【讨论】:

      • 不要像那样弄乱元数据,这很糟糕,并且很容易在 webapp 初始化期间被进程撤消。请参阅上面的正确技术。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-22
      • 2023-03-10
      • 1970-01-01
      相关资源
      最近更新 更多