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

我有一个剥离测试项目,其中包含一个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文件:

   testServlet g1.TestServlet   testServlet /testWebXml   

我尝试使用嵌入式Tomcat 7进行JUnit测试。当我启动嵌入式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包含以下依赖项:

   org.apache.tomcat tomcat-catalina 7.0.29 test   org.apache.tomcat.embed tomcat-embed-core 7.0.29 provided   org.apache.tomcat tomcat-jasper 7.0.29 test   junit junit 4.8.1 test  

==更新==

这是一个看起来与此类似的问题(除了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)能够正确地检查项目的注释类,它可能会得到一个有效的答案。

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

基本上,您必须像这样启动嵌入式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(); 
  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

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