【问题标题】:Embedded tomcat 7 servlet 3.0 annotations not working嵌入式 tomcat 7 servlet 3.0 注释不起作用
【发布时间】:2012-07-25 01:14:54
【问题描述】:

我有一个精简的测试项目,其中包含一个 Servlet 3.0 版,使用如下注释声明:

    @WebServlet("/test")
public class TestServlet extends HttpServlet {

    private static final long serialVersionUID = -3010230838088656008L;

    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException{
        response.getWriter().write("Test");
        response.getWriter().flush();
        response.getWriter().close();
    }
}

我也有这样的 web.xml 文件:

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
      version="3.0">

      <servlet>
        <servlet-name>testServlet</servlet-name>
        <servlet-class>g1.TestServlet</servlet-class>
      </servlet>      

      <servlet-mapping>
        <servlet-name>testServlet</servlet-name>
        <url-pattern>/testWebXml</url-pattern>
      </servlet-mapping>

</web-app> 

我尝试使用 Embedded Tomcat 7 进行 JUnit 测试。当我启动 Embedded Tomcat 时,我只能通过 web.xml (/testWebXml) 中声明的 url-pattern 访问 servlet。如果我尝试通过注释 (/test) 声明的 url-pattern 访问它,则找不到 404 页面。

这是我的测试代码:

    String webappDirLocation = "src/main/webapp/";
    Tomcat tomcat = new Tomcat();
    tomcat.setPort(8080);

    tomcat.addWebapp("/jerseyTest", new File(webappDirLocation).getAbsolutePath());

    tomcat.start();
    tomcat.getServer().await();

为了确保我正确设置了我的项目,我还安装了一个实际的 Tomcat 7 并部署了战争。这一次,我的 servlet 的 web.xml 声明的 url 和注释 url 都可以正常工作。

所以我的问题是:有谁知道如何让 Embedded Tomcat 7 考虑到我的 Servlet 3.0 注释?

我还应该声明它是一个 Maven 项目,并且 pom.xml 包含以下依赖项:

    <dependency>
        <groupId>org.apache.tomcat</groupId>
        <artifactId>tomcat-catalina</artifactId>
        <version>7.0.29</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-core</artifactId>
        <version>7.0.29</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.tomcat</groupId>
        <artifactId>tomcat-jasper</artifactId>
        <version>7.0.29</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.8.1</version>
        <scope>test</scope>
    </dependency>

== 更新 ==

这是一个看起来与此类似的问题(除了不起作用的 Servlet 3.0 注释在 Listener 上,而不是 Servlet),它有一个建议的修复:

https://issues.apache.org/bugzilla/show_bug.cgi?id=53903

我试过了,还是不行:

将 Embedded Tomcat 启动代码更改为:

String webappDirLocation = "src/main/webapp/";
Tomcat tomcat = new Tomcat();

tomcat.enableNaming();
tomcat.setPort(8080);


Context ctx = tomcat.addWebapp(tomcat.getHost(), "/embeddedTomcat", new File(webappDirLocation).getAbsolutePath());
((StandardJarScanner) ctx.getJarScanner()).setScanAllDirectories(true);

tomcat.start();

tomcat.getServer().await();

我尝试过的其他事情,也没有成功:

  • 在 web.xml “web-app” 标签中专门设置 metadata-complete="false"

  • 将 Maven 依赖项更新到版本 7.0.30

  • 调试org.apache.catalina.startup.ContextConfig 类。那里有检查@WebServlet 注释的代码,只是它永远不会被执行(第2115 行)。这可能是找到问题根源的好方法,但是课程很大,我现在没有时间这样做。也许如果有人愿意看看这个类是如何工作的,以及在什么条件下(配置参数)它可以正确检查你的项目的类的注释,它可能会得到一个有效的答案。

【问题讨论】:

  • 我也无法在嵌入式 Tomcat 7 中获得 Servlet 3.0(基于注释的)应用程序。当我下拉到 Servlet 2.5 并将我的 servlet 放入 web.xml 时工作正常.我第一次尝试使用嵌入式 Jetty 8 ......但是在将头撞到墙上一整天后,我发现 Servlet 3 注释仅在您使用“hightide”Jetty 变体时可用(我无法开始工作在嵌入式模式下)。
  • 嵌入式 Tomcat 文档几乎不存在。 Eclipse 人员为 Jetty 提供了漂亮的文档,但它充满了巨大的空白(例如,什么是“高潮”以及何时使用它,如何嵌入带有 JSP 支持的版本 8 和基于注释的 servlet 配置等)。感觉所有这些东西都缺少一些基本的难题......我不确定现在是否有人正在使用带有任何这些嵌入式 servlet 引擎的 Servlet 3.0。
  • 只是对上面 Shivan 示例的注释...问题不在于他将基于注释的配置与基于 web.xml 的配置混合在一起。即使您从 web.xml 中删除了 servlet 和 servlet 映射,注释声明的映射本身仍然不起作用。
  • 有趣的是,我已经通过 Maven Jetty 插件获得了在嵌入式 Jetty 8 中运行的基于注释的 Servlet 3.0 应用程序。所以必须有一些可能的途径来完成所有这些工作。但是,这种情况只是在 Maven 构建脚本中运行服务器......这与以编程方式将 Tomcat 或 Jetty 实例嵌入应用程序中并独立发布它不同。
  • “我发现 Servlet 3 注释仅在您使用“hightide”Jetty 变体时可用(我也无法在嵌入式模式下工作)。” - 这不准确,不管目标是不使用码头,那么码头标签应该被删除,它只是在 cmets 中提到

标签: java jakarta-ee tomcat servlets


【解决方案1】:

我终于通过查看 Tomcat7 源代码解决了这个问题,即在处理 EmbeddedTomcat 和 servlet 3.0 注释的单元测试中。

基本上,您必须像这样启动 Embedded Tomcat 7 以使其知道您的带注释的类:

String webappDirLocation = "src/main/webapp/";
Tomcat tomcat = new Tomcat();
tomcat.setPort(8080);

StandardContext ctx = (StandardContext) tomcat.addWebapp("/embeddedTomcat",
                new File(webappDirLocation).getAbsolutePath());

//declare an alternate location for your "WEB-INF/classes" dir:     
File additionWebInfClasses = new File("target/classes");
VirtualDirContext resources = new VirtualDirContext();
resources.setExtraResourcePaths("/WEB-INF/classes=" + additionWebInfClasses);
ctx.setResources(resources);

tomcat.start();
tomcat.getServer().await();

为了清楚起见,我应该提到这适用于标准 Maven 项目,您的“网络资源”(例如静态和动态页面、WEB-INF 目录等)位于:

[你项目的根目录]/src/main/webapp

你的类被编译成

[你项目的根目录]/target/classes

(这样你就有了[你的项目的根目录]/target/classes/[some package]/SomeCompiledServletClass.class)

对于其他目录布局,这些位置需要相应更改。

==== 更新:嵌入式 Tomcat 8 ====

感谢@kwak 注意到这一点。

API 发生了一些变化,以下是使用 Embedded Tomcat 8 时上述示例的变化:

String webappDirLocation = "src/main/webapp/";
Tomcat tomcat = new Tomcat();
tomcat.setPort(8080);

StandardContext ctx = (StandardContext) tomcat.addWebapp("/embeddedTomcat",
                new File(webappDirLocation).getAbsolutePath());

//declare an alternate location for your "WEB-INF/classes" dir:     
File additionWebInfClasses = new File("target/classes");
WebResourceRoot resources = new StandardRoot(ctx);
resources.addPreResources(new DirResourceSet(resources, "/WEB-INF/classes", additionWebInfClasses.getAbsolutePath(), "/"));
ctx.setResources(resources);

tomcat.start();
tomcat.getServer().await();

【讨论】:

  • VirtualDirContext 不再嵌入 tomcat8 中
  • @kwak 感谢您的关注。更新了我的答案以包括 tomcat8
  • @ShivanDragon - 非常感谢您的详细解决方案!我刚刚在我的嵌入式 Tomcat 8.0.12 上测试了您的解决方案,它可以正常工作!太感谢了!它为我节省了很多时间! +1
  • 这也可以:((StandardJarScanner) ctx.getJarScanner()).setScanAllDirectories(true);
  • ((StandardJarScanner) ctx.getJarScanner()).setScanAllDirectories(true);真的很有用。再来一个cmets:如果@WebFilter@WebServlet注解的类被打包成一个jar文件,应该在嵌入的tomcat服务器类中加入这一行:DirResourceSet lib = DirResourceSet(this, "/WEB-INF/lib",目录.LIBS.fullPath(), "/");资源.addJarResources(lib);
【解决方案2】:
    Tomcat tomcat = new Tomcat();
    tomcat.setPort(port);

    Context ctx = tomcat.addWebapp("/", new File(docBase).getAbsolutePath());
    StandardJarScanner scan = (StandardJarScanner) ctx.getJarScanner();
    scan.setScanClassPath(true);
    scan.setScanBootstrapClassPath(true); // just guessing here
    scan.setScanAllDirectories(true);
    scan.setScanAllFiles(true);

    tomcat.start();
    tomcat.getServer().await();

使用 MVN 的 Tomcat Embed 7.0.90 对我来说效果很好

【讨论】:

    【解决方案3】:

    鉴于您发布的 sn-p 中的 JNDI 魔力:

    try {
        listBindings = context.getResources().listBindings(
                "/WEB-INF/classes");
    } catch (NameNotFoundException ignore) {
        // Safe to ignore
    }
    

    您可以自己在 JNDI 中设置相同类型的条目吗?

    Context ctx = tomcat.addWebapp(...);
    FileDirContext fdc = new FileDirContext();
    fdc.setDocBase(new File("/path/to/my/classes").getAbsolutePath());
    ctx.getResources().createSubcontext("/WEB-INF/classes").bind("myclasses", fdc);
    

    【讨论】:

    • 你在正确的轨道上,“WEB-INF/classes”目录需要在启动Tomcat之前以某种方式“别名”,但是它不是很直观,我不得不查找他们的单位测试找出答案。
    【解决方案4】:

    我认为在 8.5 及更高版本中它只是一个班轮:

    Context.setAddWebinfClassesResources(true);

    设置标志,指​​示是否应将 /WEB-INF/classes 视为分解的 JAR 和 JAR 资源,就像它们在 JAR 中一样。

    http://tomcat.apache.org/tomcat-8.5-doc/api/org/apache/catalina/core/StandardContext.html#setAddWebinfClassesResources(boolean)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-02-22
      • 2011-02-02
      • 2011-12-21
      • 2012-06-04
      • 2020-03-04
      • 1970-01-01
      • 2012-12-06
      • 1970-01-01
      相关资源
      最近更新 更多