在 osgi-container 中找不到 hibernate-validator 的 el 实现
El implementation is not found for hibernate-validator in osgi-container
我尝试 运行 在 osgi 容器中使用 Hibernate 验证器。
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>2.2.4</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.4</version>
</dependency>
<dependency>
<groupId>org.apache.servicemix.bundles</groupId>
<artifactId>org.apache.servicemix.bundles.hibernate-validator</artifactId>
<version>5.0.2.Final_1</version>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.7</version>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.8.1</version>
</dependency>
public class HibernateValidationProviderResolver implements ValidationProviderResolver {
@Override
public List<ValidationProvider<?>> getValidationProviders() {
List<ValidationProvider<?>> list = new ArrayList<>(1);
list.add(new HibernateValidator());
return list;
}
}
Configuration<?> configuration = Validation.byDefaultProvider().providerResolver(
new HibernateValidationProviderResolver()
).configure();
ValidatorFactory validatorFactory = configuration.buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
Set<ConstraintViolation<Group>> constraintViolations = validator.validate(group);
public class Group {
@NotNull
@Size(min=2)
private String title;
}
试试运行,equinox控制台没问题:
10 RESOLVED org.glassfish.web.javax.el_2.2.4
39 RESOLVED org.apache.servicemix.bundles.hibernate-validator_5.0.2.Final_1
47 RESOLVED javax.validation.api_1.1.0.Final
49 RESOLVED javax.el-api_2.2.4
如果我使用 title = null
传递组 class 实例,则验证没问题并且 constraintViolations 包含一个违规 "not null"。
如果我用 title = "A"
传递 Group class 实例(一个字符对最小长度 = 2),那么它会抛出一个异常
Caused by: javax.el.ELException: Provider com.sun.el.ExpressionFactoryImpl not found
Caused by: java.lang.ClassNotFoundException: com.sun.el.ExpressionFactoryImpl
它 100% 是由 osgi 引起的,但是我应该如何在 osgi 中设置 hibernate-validator?我能找到的所有文章都描述了 HibernateValidationProviderResolver
的创建,仅此而已。
更新 1
Maven: javax.el:javax.el-api:2.2.4
Export-Package: javax.el;version="2.2.4"
Import-Package: javax.el;version="2.2.4"
Maven: org.glassfish.web:javax.el:2.2.4 MANIFEST.MF
Export-Package: com.sun.el;uses:="javax.el";version="2.2.4"
Private-Package: com.sun.el.lang;version="2.2.4",com.sun.el.parser;version="2.2.4",com.sun.el.util;version="2.2.4"
Import-Package: com.sun.el;version="2.2.4",javax.el;version="2.2"
Maven: org.apache.servicemix.bundles:org.apache.servicemix.bundles.hibernate-validator:5.0.2.Final_1
Implementation-Version: 5.0.2.Final
Import-Package: javax.el,javax.persistence;resolution:=optional, ...
Export-Package: org.hibernate.validator.internal.engine.messageinterpola
tion.el;uses:="javax.el,javax.validation,org.hibernate.validator.intern
al.engine.messageinterpolation";version="5.0.2.Final",org.hibernate.val
idator.internal.engine.messageinterpolation;uses:="javax.validation.met
adata,org.hibernate.validator.internal.engine.messageinterpolation.el,j
avax.el,javax.validation,org.hibernate.validator.internal.util.logging"
;version="5.0.2.Final", ...
在 hibernate bundle 中导入的任何版本,el-api 和 el-impl 导出中的 2.2.4 和 el-impl 导入 el-api 作为 2.2,而不是 2.2.4 .所有捆绑包都已解决。
更新 2
决定 1
我对@hwellmann 想法的实现。 @hwellmann,对吗?
public void createGroup(Group group) {
ClassLoader prevClassLoader = Thread.currentThread().getContextClassLoader();
try {
ClassLoader[] classLoaders = new ClassLoader[] {
prevClassLoader,
ExpressionFactoryImpl.class.getClassLoader()
};
// build composite classloader
Thread.currentThread().setContextClassLoader(compositeClassLoader);
Set<ConstraintViolation<Group>> constraintViolations = validator.validate(group);
} finally {
Thread.currentThread().setContextClassLoader(prevClassLoader);
}
}
可以用,但看起来很奇怪。并且在每个验证处理上更改 TCCL 看起来是一些开销。
决定 2
当我将自己的消息属性添加到每个验证注释时,错误消失了,例如组:
public class Group {
@NotNull
@Size(min=2, message="field.too_short")
private String title;
}
在这种情况下,似乎休眠插值器未启动,因此未从 TCCL 检索 ExpressionFactoryImpl
(之前我们读取 min=2 值,现在我们不读取)。如果对我们来说没问题,这个决定是最简单的。
我将进一步调查这个领域并在那里分享我的观察结果。
完整的堆栈跟踪可能会提供比异常消息更多的洞察力。
我的猜测是您正在通过 META-INF/service/javax.el.ExpressionFactory
看到服务查找失败的结果。进行查找的包显然看不到 com.sun.el
.
将此包导入您的应用程序包并将线程上下文类加载器设置为您的包类加载器可能会有所帮助。
JBoss 保险丝中存在相关问题:
https://access.redhat.com/solutions/1479723
他们建议降级到 4.3.1.Final
:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.3.1.Final</version>
</dependency>
我在使用 Hibernate 5.2.5 和 Jetty 9.x 时遇到了类似的问题。基本上,看起来 Hibernate 没有检查类路径上是否已经有 EL 实现,而是尝试使用 javax.el-api 而不是 Jetty 9 的 apache-el。升级到 Hibernate 5.2.10 为我解决了这个问题。类路径上不再有冲突的 el-api。
背景
我使用 hibernate 验证器版本 5.4。3.Final。
从5.3.1.Final开始,提到ExpressionFactoryImpl是硬性需求
5.3 中存在 OSGi 问题。1.Final,问题是 模块化环境中的 ClassLoader 问题。它已在 5.3.3.Final 中修复。票号为HV-1155
修复
根据以上信息,
无需使用 更新 2 -> 决策 1。
我发现您可以简单地更改 OP 从
发布的代码
Configuration<?> configuration = Validation.byDefaultProvider().providerResolver(
new HibernateValidationProviderResolver()
).configure();
至
Configuration<?> configuration = Validation.byProvider(HibernateValidator.class).providerResolver(
new HibernateValidationProviderResolver()
).externalClassLoader(getClass().getClassLoader()).configure();
11.15. Customizing class-loading 提到了这个技巧。
此外,如果您使用的是 6.0.0.CR3 以上版本的 hibernate 验证程序,则无需应用上述技巧。HV-1426 涵盖它。
我尝试 运行 在 osgi 容器中使用 Hibernate 验证器。
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>2.2.4</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.4</version>
</dependency>
<dependency>
<groupId>org.apache.servicemix.bundles</groupId>
<artifactId>org.apache.servicemix.bundles.hibernate-validator</artifactId>
<version>5.0.2.Final_1</version>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.7</version>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.8.1</version>
</dependency>
public class HibernateValidationProviderResolver implements ValidationProviderResolver {
@Override
public List<ValidationProvider<?>> getValidationProviders() {
List<ValidationProvider<?>> list = new ArrayList<>(1);
list.add(new HibernateValidator());
return list;
}
}
Configuration<?> configuration = Validation.byDefaultProvider().providerResolver(
new HibernateValidationProviderResolver()
).configure();
ValidatorFactory validatorFactory = configuration.buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
Set<ConstraintViolation<Group>> constraintViolations = validator.validate(group);
public class Group {
@NotNull
@Size(min=2)
private String title;
}
试试运行,equinox控制台没问题:
10 RESOLVED org.glassfish.web.javax.el_2.2.4
39 RESOLVED org.apache.servicemix.bundles.hibernate-validator_5.0.2.Final_1
47 RESOLVED javax.validation.api_1.1.0.Final
49 RESOLVED javax.el-api_2.2.4
如果我使用 title = null
传递组 class 实例,则验证没问题并且 constraintViolations 包含一个违规 "not null"。
如果我用 title = "A"
传递 Group class 实例(一个字符对最小长度 = 2),那么它会抛出一个异常
Caused by: javax.el.ELException: Provider com.sun.el.ExpressionFactoryImpl not found
Caused by: java.lang.ClassNotFoundException: com.sun.el.ExpressionFactoryImpl
它 100% 是由 osgi 引起的,但是我应该如何在 osgi 中设置 hibernate-validator?我能找到的所有文章都描述了 HibernateValidationProviderResolver
的创建,仅此而已。
更新 1
Maven: javax.el:javax.el-api:2.2.4
Export-Package: javax.el;version="2.2.4"
Import-Package: javax.el;version="2.2.4"
Maven: org.glassfish.web:javax.el:2.2.4 MANIFEST.MF
Export-Package: com.sun.el;uses:="javax.el";version="2.2.4"
Private-Package: com.sun.el.lang;version="2.2.4",com.sun.el.parser;version="2.2.4",com.sun.el.util;version="2.2.4"
Import-Package: com.sun.el;version="2.2.4",javax.el;version="2.2"
Maven: org.apache.servicemix.bundles:org.apache.servicemix.bundles.hibernate-validator:5.0.2.Final_1
Implementation-Version: 5.0.2.Final
Import-Package: javax.el,javax.persistence;resolution:=optional, ...
Export-Package: org.hibernate.validator.internal.engine.messageinterpola
tion.el;uses:="javax.el,javax.validation,org.hibernate.validator.intern
al.engine.messageinterpolation";version="5.0.2.Final",org.hibernate.val
idator.internal.engine.messageinterpolation;uses:="javax.validation.met
adata,org.hibernate.validator.internal.engine.messageinterpolation.el,j
avax.el,javax.validation,org.hibernate.validator.internal.util.logging"
;version="5.0.2.Final", ...
在 hibernate bundle 中导入的任何版本,el-api 和 el-impl 导出中的 2.2.4 和 el-impl 导入 el-api 作为 2.2,而不是 2.2.4 .所有捆绑包都已解决。
更新 2
决定 1
我对@hwellmann 想法的实现。 @hwellmann,对吗?
public void createGroup(Group group) {
ClassLoader prevClassLoader = Thread.currentThread().getContextClassLoader();
try {
ClassLoader[] classLoaders = new ClassLoader[] {
prevClassLoader,
ExpressionFactoryImpl.class.getClassLoader()
};
// build composite classloader
Thread.currentThread().setContextClassLoader(compositeClassLoader);
Set<ConstraintViolation<Group>> constraintViolations = validator.validate(group);
} finally {
Thread.currentThread().setContextClassLoader(prevClassLoader);
}
}
可以用,但看起来很奇怪。并且在每个验证处理上更改 TCCL 看起来是一些开销。
决定 2
当我将自己的消息属性添加到每个验证注释时,错误消失了,例如组:
public class Group {
@NotNull
@Size(min=2, message="field.too_short")
private String title;
}
在这种情况下,似乎休眠插值器未启动,因此未从 TCCL 检索 ExpressionFactoryImpl
(之前我们读取 min=2 值,现在我们不读取)。如果对我们来说没问题,这个决定是最简单的。
我将进一步调查这个领域并在那里分享我的观察结果。
完整的堆栈跟踪可能会提供比异常消息更多的洞察力。
我的猜测是您正在通过 META-INF/service/javax.el.ExpressionFactory
看到服务查找失败的结果。进行查找的包显然看不到 com.sun.el
.
将此包导入您的应用程序包并将线程上下文类加载器设置为您的包类加载器可能会有所帮助。
JBoss 保险丝中存在相关问题: https://access.redhat.com/solutions/1479723
他们建议降级到 4.3.1.Final
:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.3.1.Final</version>
</dependency>
我在使用 Hibernate 5.2.5 和 Jetty 9.x 时遇到了类似的问题。基本上,看起来 Hibernate 没有检查类路径上是否已经有 EL 实现,而是尝试使用 javax.el-api 而不是 Jetty 9 的 apache-el。升级到 Hibernate 5.2.10 为我解决了这个问题。类路径上不再有冲突的 el-api。
背景
我使用 hibernate 验证器版本 5.4。3.Final。
从5.3.1.Final开始,提到ExpressionFactoryImpl是硬性需求
5.3 中存在 OSGi 问题。1.Final,问题是 模块化环境中的 ClassLoader 问题。它已在 5.3.3.Final 中修复。票号为HV-1155
修复
根据以上信息, 无需使用 更新 2 -> 决策 1。 我发现您可以简单地更改 OP 从
发布的代码Configuration<?> configuration = Validation.byDefaultProvider().providerResolver(
new HibernateValidationProviderResolver()
).configure();
至
Configuration<?> configuration = Validation.byProvider(HibernateValidator.class).providerResolver(
new HibernateValidationProviderResolver()
).externalClassLoader(getClass().getClassLoader()).configure();
11.15. Customizing class-loading 提到了这个技巧。
此外,如果您使用的是 6.0.0.CR3 以上版本的 hibernate 验证程序,则无需应用上述技巧。HV-1426 涵盖它。