我正在尝试使用Spring 3.1和嵌入式Jetty 8服务器创建一个没有任何XML配置的简单webapp。
但是,我很难让Jetty认识到Spring WebApplicationInitializer 接口的实现。
项目结构:
src +- main +- java | +- JettyServer.java | +- Initializer.java | +- webapp +- web.xml (objective is to remove this - see below).
上面的 Initializer 类是 WebApplicationInitializer 的简单实现:
import javax.servlet.ServletContext; import javax.servlet.ServletException; import org.springframework.web.WebApplicationInitializer; public class Initializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) throws ServletException { System.out.println("onStartup"); } }
同样, JettyServer 是嵌入式Jetty服务器的简单实现:
import org.eclipse.jetty.annotations.AnnotationConfiguration; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.webapp.Configuration; import org.eclipse.jetty.webapp.WebAppContext; public class JettyServer { public static void main(String[] args) throws Exception { Server server = new Server(8080); WebAppContext webAppContext = new WebAppContext(); webAppContext.setResourceBase("src/main/webapp"); webAppContext.setContextPath("/"); webAppContext.setConfigurations(new Configuration[] { new AnnotationConfiguration() }); webAppContext.setParentLoaderPriority(true); server.setHandler(webAppContext); server.start(); server.join(); } }
我的理解是,在启动时,Jetty将使用 AnnotationConfiguration 来扫描 ServletContainerInitializer的带 注释的实现;它应该找到 初始化器 并将其连接到…
但是,当我从Eclipse中启动Jetty服务器时,在命令行上看到以下内容:
2012-11-04 16:59:04.552:INFO:oejs.Server:jetty-8.1.7.v20120910 2012-11-04 16:59:05.046:INFO:/:No Spring WebApplicationInitializer types detected on classpath 2012-11-04 16:59:05.046:INFO:oejsh.ContextHandler:started o.e.j.w.WebAppContext{/,file:/Users/duncan/Coding/spring-mvc-embedded-jetty-test/src/main/webapp/} 2012-11-04 16:59:05.117:INFO:oejs.AbstractConnector:Started SelectChannelConnector@0.0.0.0:8080
重要的一点是:
No Spring WebApplicationInitializer types detected on classpath
请注意, src / main / java 在Eclipse中定义为源文件夹,因此应位于类路径上。另请注意,“动态Web模块构面”设置为3.0。
我敢肯定有一个简单的解释,但是我正在努力寻找树木的树木!我怀疑关键在于以下几行:
... webAppContext.setResourceBase("src/main/webapp"); ...
这对于使用web.xml的2.5 servlet有意义(请参阅下文),但是在使用 AnnotationConfiguration 时应该是什么呢?
注意:如果我将“配置”更改为以下内容,则一切正常启动:
... webAppContext.setConfigurations(new Configuration[] { new WebXmlConfiguration() }); ...
在这种情况下,它将在 src / main / webapp 下找到 web.xml ,并使用它以通常的方式(完全绕过上述 WebApplicationInitializer 实现)使用 DispatcherServlet 和 AnnotationConfigWebApplicationContext连接 Servlet 。
这感觉上很像类路径问题,但是我很难理解Jetty如何将其自身与 WebApplicationInitializer的 实现相关联 -任何建议将不胜感激!
有关信息,我使用以下内容:
spring3.1.1码头8.1.7 STS 3.1.0
问题在于Jetty的AnnotationConfiguration类不会扫描类路径上的非jar资源(在WEB-INF / classes下除外)。
AnnotationConfiguration
WebApplicationInitializer如果我注册了一个子类,则除了容器和web- inf位置之外,它还会AnnotationConfiguration覆盖其子类configure(WebAppContext)以扫描主机类路径,从而找到我的。
WebApplicationInitializer
configure(WebAppContext)
大多数子类是(非常)从父级复制粘贴。这包括:
parseHostClassPath
parseWebInfClasses
getHostClassPathResource
我正在使用Jetty(8.1.7.v20120910)和Spring(3.1.2_RELEASE)稍有不同的版本,但是我想相同的解决方案会起作用。
编辑:我在github中创建了一个工作示例项目,并进行了一些修改(以下代码从Eclipse可以正常运行,但在带阴影的jar中运行时效果不佳)-https: //github.com/steveliles/jetty-embedded-spring-mvc- noxml
在OP的JettyServer类中,必要的更改将第15行替换为:
webAppContext.setConfigurations (new Configuration [] { new AnnotationConfiguration() { @Override public void configure(WebAppContext context) throws Exception { boolean metadataComplete = context.getMetaData().isMetaDataComplete(); context.addDecorator(new AnnotationDecorator(context)); AnnotationParser parser = null; if (!metadataComplete) { if (context.getServletContext().getEffectiveMajorVersion() >= 3 || context.isConfigurationDiscovered()) { parser = createAnnotationParser(); parser.registerAnnotationHandler("javax.servlet.annotation.WebServlet", new WebServletAnnotationHandler(context)); parser.registerAnnotationHandler("javax.servlet.annotation.WebFilter", new WebFilterAnnotationHandler(context)); parser.registerAnnotationHandler("javax.servlet.annotation.WebListener", new WebListenerAnnotationHandler(context)); } } List<ServletContainerInitializer> nonExcludedInitializers = getNonExcludedInitializers(context); parser = registerServletContainerInitializerAnnotationHandlers(context, parser, nonExcludedInitializers); if (parser != null) { parseContainerPath(context, parser); parseWebInfClasses(context, parser); parseWebInfLib (context, parser); parseHostClassPath(context, parser); } } private void parseHostClassPath(final WebAppContext context, AnnotationParser parser) throws Exception { clearAnnotationList(parser.getAnnotationHandlers()); Resource resource = getHostClassPathResource(getClass().getClassLoader()); if (resource == null) return; parser.parse(resource, new ClassNameResolver() { public boolean isExcluded (String name) { if (context.isSystemClass(name)) return true; if (context.isServerClass(name)) return false; return false; } public boolean shouldOverride (String name) { //looking at webapp classpath, found already-parsed class of same name - did it come from system or duplicate in webapp? if (context.isParentLoaderPriority()) return false; return true; } }); //TODO - where to set the annotations discovered from WEB-INF/classes? List<DiscoveredAnnotation> annotations = new ArrayList<DiscoveredAnnotation>(); gatherAnnotations(annotations, parser.getAnnotationHandlers()); context.getMetaData().addDiscoveredAnnotations (annotations); } private Resource getHostClassPathResource(ClassLoader loader) throws IOException { if (loader instanceof URLClassLoader) { URL[] urls = ((URLClassLoader)loader).getURLs(); for (URL url : urls) if (url.getProtocol().startsWith("file")) return Resource.newResource(url); } return null; } }, });
更新 :Jetty 8.1.8引入了与上面的代码不兼容的内部更改。对于8.1.8,以下似乎有效:
webAppContext.setConfigurations (new Configuration [] { // This is necessary because Jetty out-of-the-box does not scan // the classpath of your project in Eclipse, so it doesn't find // your WebAppInitializer. new AnnotationConfiguration() { @Override public void configure(WebAppContext context) throws Exception { boolean metadataComplete = context.getMetaData().isMetaDataComplete(); context.addDecorator(new AnnotationDecorator(context)); //Even if metadata is complete, we still need to scan for ServletContainerInitializers - if there are any AnnotationParser parser = null; if (!metadataComplete) { //If metadata isn't complete, if this is a servlet 3 webapp or isConfigDiscovered is true, we need to search for annotations if (context.getServletContext().getEffectiveMajorVersion() >= 3 || context.isConfigurationDiscovered()) { _discoverableAnnotationHandlers.add(new WebServletAnnotationHandler(context)); _discoverableAnnotationHandlers.add(new WebFilterAnnotationHandler(context)); _discoverableAnnotationHandlers.add(new WebListenerAnnotationHandler(context)); } } //Regardless of metadata, if there are any ServletContainerInitializers with @HandlesTypes, then we need to scan all the //classes so we can call their onStartup() methods correctly createServletContainerInitializerAnnotationHandlers(context, getNonExcludedInitializers(context)); if (!_discoverableAnnotationHandlers.isEmpty() || _classInheritanceHandler != null || !_containerInitializerAnnotationHandlers.isEmpty()) { parser = createAnnotationParser(); parse(context, parser); for (DiscoverableAnnotationHandler h:_discoverableAnnotationHandlers) context.getMetaData().addDiscoveredAnnotations(((AbstractDiscoverableAnnotationHandler)h).getAnnotationList()); } } private void parse(final WebAppContext context, AnnotationParser parser) throws Exception { List<Resource> _resources = getResources(getClass().getClassLoader()); for (Resource _resource : _resources) { if (_resource == null) return; parser.clearHandlers(); for (DiscoverableAnnotationHandler h:_discoverableAnnotationHandlers) { if (h instanceof AbstractDiscoverableAnnotationHandler) ((AbstractDiscoverableAnnotationHandler)h).setResource(null); // } parser.registerHandlers(_discoverableAnnotationHandlers); parser.registerHandler(_classInheritanceHandler); parser.registerHandlers(_containerInitializerAnnotationHandlers); parser.parse(_resource, new ClassNameResolver() { public boolean isExcluded (String name) { if (context.isSystemClass(name)) return true; if (context.isServerClass(name)) return false; return false; } public boolean shouldOverride (String name) { //looking at webapp classpath, found already-parsed class of same name - did it come from system or duplicate in webapp? if (context.isParentLoaderPriority()) return false; return true; } }); } } private List<Resource> getResources(ClassLoader aLoader) throws IOException { if (aLoader instanceof URLClassLoader) { List<Resource> _result = new ArrayList<Resource>(); URL[] _urls = ((URLClassLoader)aLoader).getURLs(); for (URL _url : _urls) _result.add(Resource.newResource(_url)); return _result; } return Collections.emptyList(); } } });