问题在于Jetty的
AnnotationConfiguration类不会扫描类路径上的非jar资源(在WEB-INF / classes下除外)。
WebApplicationInitializer如果我注册了一个子类,则除了容器和web-
inf位置之外,它还会
AnnotationConfiguration覆盖其子类
configure(WebAppContext)以扫描主机类路径,从而找到我的。
大多数子类是(非常)从父级复制粘贴。这包括:
parseHostClassPath
configure方法末尾的额外解析调用();- 该
parseHostClassPath
方法主要是复制自AnnotationConfiguration
的parseWebInfClasses
; getHostClassPathResource
从类加载器中获取第一个非jar URL 的方法(至少对我来说,这是指向eclipse中我的类路径的文件url)。
我正在使用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(); } } });


