嵌入式tomcat 7 servlet 3.0注释无法正常工作

时间:2012-07-26 12:40:48

标签: java java-ee tomcat servlets

我有一个精简测试项目,其中包含一个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并部署了战争。这一次,web.xml声明的url和我的servlet的注释url工作正常。

所以我的问题是:有没有人知道如何让嵌入式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>

== UPDATE ==

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

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

我已经尝试了它并没有奏效:

将嵌入式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行)。这可能是找到问题根源的好方法,但课程相当大,我现在没有时间这样做。也许如果有人愿意看看这个类是如何工作的,并且在哪些条件下(config params)能够正确检查项目的注释类,它可能会得到一个有效的答案。

4 个答案:

答案 0 :(得分:35)

好吧,我终于通过查看Tomcat7源代码来解决它,即在处理EmbeddedTomcat和servlet 3.0注释的单元测试中。

基本上,您必须像这样启动嵌入式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资源”(例如静态和动态页面,WEB-INF目录等):

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

并将您的课程编译成

[您项目的根目录] /目标/类

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

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

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

感谢@kwak注意到这一点。

API已经发生了一些变化,这里使用嵌入式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();

答案 1 :(得分:1)

    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对我来说效果很好

答案 2 :(得分:0)

鉴于您发布的片段中的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);

答案 3 :(得分:0)

我认为在8.5及更高版本中,它只是一种衬板:

Context.setAddWebinfClassesResources(true);

设置标志,该标志指示是否应将/ WEB-INF / classs视为爆炸的JAR,并且将提供的JAR资源视为位于JAR中。

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