前言
先介绍下接口字段校验的框架Java服务端接口参数校验框架——hibernate-validator使用。
我们项目采用的springboot,在web依赖中已经封装了该框架,如下
所以可以直接使用,无需再引入其他依赖。
这里仅贴出一段在项目中用到的、和问题有关的代码,如下ValidationUtils
类
private static final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
public static <T> void validateProperties(T obj, String... propertyNames) throws GlobalException {
if (propertyNames.length > 0) {
for (String propertyName : propertyNames) {
validateProperty(obj, propertyName);
}
}
}
validateProperties
即是用于校验的方法。
问题场景
在接口中用到了上述validateProperties
方法。
项目启动过程没有问题,结果在调用接口时,抛出如下异常
Caused by: javax.validation.ValidationException: HV000183: Unable to
initialize ‘javax.el.ExpressionFactory’. Check that you have the EL
dependencies on the classpath, or use ParameterMessageInterpolator
instead at
org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.buildExpressionFactory(ResourceBundleMessageInterpolator.java:123)
at
org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.(ResourceBundleMessageInterpolator.java:47)
at
org.hibernate.validator.internal.engine.ConfigurationImpl.getDefaultMessageInterpolator(ConfigurationImpl.java:494)
at
org.hibernate.validator.internal.engine.ConfigurationImpl.getDefaultMessageInterpolatorConfiguredWithClassLoader(ConfigurationImpl.java:670)
at
org.hibernate.validator.internal.engine.ConfigurationImpl.getMessageInterpolator(ConfigurationImpl.java:418)
at
org.hibernate.validator.internal.engine.ValidatorFactoryImpl.(ValidatorFactoryImpl.java:187)
at
org.hibernate.validator.HibernateValidator.buildValidatorFactory(HibernateValidator.java:38)
at
org.hibernate.validator.internal.engine.ConfigurationImpl.buildValidatorFactory(ConfigurationImpl.java:386)
at
javax.validation.Validation.buildDefaultValidatorFactory(Validation.java:103)
at com.sf.iotp.util.ValidationUtils.(ValidationUtils.java:15)
… 58 common frames omitted
问题分析
1.分析异常链路,找到报错代码
首先分析异常链。发现最初是初始化ValidationUtils
类,最后调用到ResourceBundleMessageInterpolator
类的buildExpressionFactory
方法。
2.断点跟进
由上图还无法得知为什么最后会抛出异常,所以我们可以使用断点跟进,看具体是哪一行代码导致的。
最后得知,是由于下面红框代码返回false。
进入该方法查看,发现找不到ExpressionFactory
类的newInstance
方法,导致进入catch语句,返回false
为什么这里会找不到方法呢?
3.对比其他正常项目
还好,我们另一个正常运行的项目也用到了该校验框架,我们可以对比下两边项目的差异。
正常项目的ExpressionFactory
类:
是在tomcat-embed-el包的javax.el包下
报错项目的ExpressionFactory
类:
是在jsp-api包的javax.el包下
可以看到都是javax.el包,但是它们的父包是不一样的。
那报错的项目为什么没有在tomcat-embed-el包里寻找ExpressionFactory
呢?是因为没有这个包吗?
继续分析发现,tomcat-embed-el包是springboot的web依赖,两边的项目都是有的。
既然两边都有,那就说明是由于报错的项目多引入了jsp-api包,导致运行时去这个包寻找了。
所以,只要知道在哪里引入了这个包,就能基本解决问题了。
4.分析依赖树,看是哪里引入了jsp-api包
项目这么多依赖,怎么定位是哪里引入了jsp-api包?于是,maven依赖树命令派上用场了。
我们在报错项目的pom文件所在目录下执行
mvn dependency:tree
然后去搜索关键字即可,此处运行结果就不再展示了
问题解决
反之jsp-api也不是我们项目需要的代码,所以直接去除jsp-api相关依赖
<dependency>
<groupId>/*......*/</groupId>
<artifactId>portal-access</artifactId>
<version>4.8-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
</exclusion>
</exclusions>
</dependency>