我有一个简化的测试项目,其中包含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>
我已经尝试使用嵌入式Tomcat 7进行JUnit测试。启动嵌入式Tomcat时,我只能通过在web.xml(/ testWebXml)中声明的url-pattern访问servlet。如果我尝试通过通过注释(/ test)声明的url模式访问它,则表示未找到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注释是在侦听器上,而不是在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”标签中专门设置meta-complete =“ false”
将Maven依赖项更新到7.0.30版
调试org.apache.catalina.startup.ContextConfig
类。那里有检查@WebServlet
批注的代码,只是它从未执行过(第2115行)。这可能是解决问题根源的一种好方法,但是班级很多,而且我现在没有时间这样做。也许如果有人愿意看一下该类的工作原理,以及在什么条件(配置参数)下可以正确检查项目的类是否有该注释,它可能会得到一个有效的答案。
我终于通过查看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 / [某些软件包] /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);
我认为在8.5及更高版本中,它只是一种衬板:
Context.setAddWebinfClassesResources(true);
设置标志,该标志指示是否应该将/ WEB-INF / classs视为爆炸的JAR,并且将JAR资源提供为好像在JAR中一样。