在Tomcat 8部署OmniFaces 3.1无BV时出现NoClassDefFoundError求助
这个问题其实是个常见的「可选依赖」理解误区——虽然OmniFaces官方说明里提到JSR303 Bean Validation是功能可选的,但这不代表在类加载层面可以完全缺失这个依赖。
问题原因
当Tomcat启动时,JSF的配置处理器会扫描OmniFaces的标签库类,而OmniFaces的部分类(比如涉及验证相关的注解处理逻辑)在字节码层面引用了javax.validation.ConstraintViolation等JSR303类。即使你没用到o:validateBean或者JsfLabelMessageInterpolator,JSF在加载这些OmniFaces类时,依然会尝试解析这些依赖类,找不到就会抛出NoClassDefFoundError。
你提供的堆栈跟踪也明确指向了这一点:
java.lang.NoClassDefFoundError: javax/validation/ConstraintViolation at java.lang.Class.getDeclaredMethods0(Native Method) at java.lang.Class.privateGetDeclaredMethods(Class.java:2701) at java.lang.Class.getDeclaredMethods(Class.java:1975) at com.sun.faces.util.Util.classHasAnnotations(Util.java:1145) at com.sun.faces.application.ApplicationInstanceFactoryMetadataMap.onPut(ApplicationInstanceFactoryMetadataMap.java:76) at com.sun.faces.application.ApplicationInstanceFactoryMetadataMap.onPut(ApplicationInstanceFactoryMetadataMap.java:43) at com.sun.faces.util.MetadataWrapperMap.put(MetadataWrapperMap.java:99) at com.sun.faces.config.processor.AbstractConfigProcessor.loadClass(AbstractConfigProcessor.java:425) at com.sun.faces.config.processor.FaceletTaglibConfigProcessor.processHandlerClass(FaceletTaglibConfigProcessor.java:446) at com.sun.faces.config.processor.FaceletTaglibConfigProcessor.processTags(FaceletTaglibConfigProcessor.java:393) at com.sun.faces.config.processor.FaceletTaglibConfigProcessor.processTagLibrary(FaceletTaglibConfigProcessor.java:327) at com.sun.faces.config.processor.FaceletTaglibConfigProcessor.process(FaceletTaglibConfigProcessor.java:271) at com.sun.faces.config.ConfigManager.initialize(ConfigManager.java:445) at com.sun.faces.config.ConfigureListener.contextInitialized(ConfigureListener.java:237) at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4577) at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5041) at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1427) at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1417) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75) at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134) at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:943) at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:839) at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1427) at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1417) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75) at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134) at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:943) at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:258) at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) at org.apache.catalina.core.StandardService.startInternal(StandardService.java:422) at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:770) at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) at org.apache.catalina.startup.Catalina.start(Catalina.java:682) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:353) at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:493) Caused by: java.lang.ClassNotFoundException: javax.validation.ConstraintViolation at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1275) at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1104) ... 44 more
解决方案
推荐第一种最直接的方式:
1. 添加JSR303依赖到pom.xml
把以下依赖加入你的pom.xml,引入JSR303的API和常用实现(Hibernate Validator):
<!-- JSR303 Validation API --> <dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>2.0.1.Final</version> </dependency> <!-- Hibernate Validator(JSR303的主流实现) --> <dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>6.2.5.Final</version> </dependency>
版本选择要和你的Java EE/JSF版本兼容,OmniFaces 3.1对应Java EE 8,所以使用Validation API 2.x是适配的。
2. 调整JSF类扫描范围(不推荐)
如果你确实不想引入JSR303依赖,可以尝试配置JSF仅扫描必要的类,但这种方式需要修改faces-config.xml或Tomcat的配置,步骤繁琐且可能遗漏OmniFaces的其他功能,所以不建议采用。
补充说明
OmniFaces官方提到的「可选」,是指当你不使用特定验证组件时,不需要依赖JSR303的功能,但类加载层面因为代码本身引用了这些类,所以必须保证它们在classpath中存在——这是Java类加载机制的限制,并非OmniFaces的设计问题。
内容的提问来源于stack exchange,提问作者Verticon

