<bean id="global-validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">bean>
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">bean>
虽然这个很简单,不过笔者在测试的时候总是报错 Unable to initialize ‘javax.el.ExpressionFactory’. Check that you have the EL dependencies on the classpath, or use ParameterMessageInterpolator instead,然后无法注入这两个Bean
<dependency>
<groupId>org.hibernategroupId>
<artifactId>hibernate-validatorartifactId>
<version>5.4.0.Finalversion>
dependency>
<dependency>
<groupId>org.glassfish.webgroupId>
<artifactId>javax.elartifactId>
<version>2.2.6version>
dependency>
上面就是对于Hibernate Validator的依赖的处理,不过还是不行https://stackoverflow.com/questions/42718869/hibernate-validation-unable-to-initialize-javax-el-expressionfactory-error/
上面这里最后的有说明,我引用了两个不同版本的 You have 2 different incompatible versions of javax.el,我自己也是查看了Maven的依赖了啊,可是找不到啊!
看到了这句话 Have you removed all the other javax.el related dependencies? If you have several conflicting versions of javax.el, you’re going to have this error.
Thank You! It works if jsp-api removed.
原来我自己依赖了一个自己实现了EL如下 ,移除了单元测试就行了,创建Bean也不会报错。
<dependency>
<groupId>javax.servlet.jspgroupId>
<artifactId>jsp-apiartifactId>
<version>2.1version>
<scope>providedscope>
dependency>
@Autowired
private Validator validator;
//这里使用Hibernate的API
Set> set = validator.validate(验证的对象);
//也可以使用SPring的API,不过不太好使用
一般情况下,我们对于Service层验证,可能使用面向接口的编程,那么这种@NotNull放置的位置应该怎么放置呢?
我是这样想的,就和spring一样的实现,如果某个方法上有我们定义的注解,或者某个类上有我们的注解就全部的处理方法参数的校验问题
定义注解
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestValidateParam {
}
package com.common.utils.Hibernatevalidatedemo.ValidateParameter;
import org.springframework.validation.FieldError;
import java.util.List;
/**
* descrption: 参数异常,传递给上层应用进行处理
* authohr: wangji
* date: 2017-08-14 16:44
*/
public class ParamValidException extends RuntimeException{
private List fieldErrors;
public ParamValidException(List errors) {
this.fieldErrors = errors;
}
public List getFieldErrors() {
return fieldErrors;
}
public void setFieldErrors(List fieldErrors) {
this.fieldErrors = fieldErrors;
}
}
这里是处理AOP的逻辑 < aop:aspectj-autoproxy proxy-target-class=”true” /> 开启AOP,下面就是使用Hibernate的原始的方法进行参数的校验。
但是有个问题ConstraintViolation中无法获取到方法中参数的参数的名称,返回给上层应用处理不方便,spring给我们是想了一个这个
LocalVariableTableParameterNameDiscoverer 非常方便的获取,然后返回给上层应用的时候就非常清楚的晓得是哪个字段出了问题。
在AOP中可以非常方便的获取调用的方法,调用方法的实例,调用方法的参数等等信息
package com.common.utils.Hibernatevalidatedemo.ValidateParameter;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.hibernate.validator.internal.engine.path.PathImpl;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.validation.FieldError;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import javax.validation.executable.ExecutableValidator;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* descrption: 使用AOP进行参数校验
* authohr: wangji
* date: 2017-08-14 16:20
*/
@Component
@Aspect
@Slf4j
public class ValidateParamUseAop {
//获取方法的参数的名称,一般的API无法获取,需要通过解析字节码获取,通常ASM,spring也是使用这个进行获取。
private static final LocalVariableTableParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
private static final Validator validator;
static {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
}
@Pointcut("@within(com.common.utils.Hibernatevalidatedemo.ValidateParameter.TestValidateParam)||@annotation(com.common.utils.Hibernatevalidatedemo.ValidateParameter.TestValidateParam)")
public void pointcut() {}
@Before("pointcut()")
public void before(JoinPoint point) throws NoSuchMethodException, SecurityException, ParamValidException{
// 获得切入目标对象
Object target = point.getThis();
// 获得切入方法参数
Object [] args = point.getArgs();
// 获得切入的方法
Method method = ((MethodSignature)point.getSignature()).getMethod();
// 执行校验,获得校验结果
Set> validResult = validMethodParams(target, method, args);
ValidateConstraintViolationThrowExpection(validResult,method);
}
@Around("pointcut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable
{
Object returnValue = null;
try {
returnValue = joinPoint.proceed();
// 可以通过ProceedingJoinPoint.getArgs()获取方法调用参数,
// 对其进行修改,然后通过ProceedingJoinPoint.proceed(Object[] args)来传入修改过的参数继续调用。
} catch (Exception e) {
log.error("捕获异常的信息",e);
throw e;
}
Object target = joinPoint.getThis();
Method method = ((MethodSignature)joinPoint.getSignature()).getMethod();
Set> validResult = validReturnValue(target,method,returnValue);
ValidateConstraintViolationThrowExpection(validResult,method);
//如果有异常这里就不不执行了
return returnValue;
}
public void ValidateConstraintViolationThrowExpection(Set> validResult,Method method){
if(!validResult.isEmpty()){
String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);
log.info(parameterNames.toString());
List errors = new ArrayList();
for(ConstraintViolation item :validResult){
//获取参数路径的信息(参数的位置,参数的名称等等)
PathImpl path = (PathImpl)item.getPropertyPath();
int paramIndex = path.getLeafNode().getParameterIndex();
String parameterName = parameterNames[paramIndex];
FieldError fieldError = new FieldError("",parameterName,item.getMessage());
errors.add(fieldError);
}
throw new ParamValidException(errors);
}
}
//进行返回值的校验
private Set> validReturnValue(T obj, Method method, Object returnValue){
ExecutableValidator validatorParam = validator.forExecutables();
return validatorParam.validateReturnValue(obj,method,returnValue);
}
//进行参数校验
private Set> validMethodParams(T obj, Method method, Object [] params){
ExecutableValidator validatorParam = validator.forExecutables();
return validatorParam.validateParameters(obj,method,params);
}
}
@TestValidateParam
public void doTest(@NotBlank(message = "不能为空")String message,String tt) {
}
package com.common.utils;
import com.common.utils.Hibernatevalidatedemo.ValidateParameter.ParamValidException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.FieldError;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.Set;
/**
* descrption: 全局异常处理
* authohr: wangji
* date: 2017-08-01 8:54
*/
@Slf4j
public class GlobalHandlerExceptionResolver implements HandlerExceptionResolver {
private MappingJackson2JsonView jsonView = new MappingJackson2JsonView();
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setView(jsonView);
modelAndView.addObject("error",e.getMessage());
if(e instanceof ConstraintViolationException){
Set> constraintViolations = ((ConstraintViolationException) e).getConstraintViolations();
for(ConstraintViolation item : constraintViolations){
log.info("Item:"+item.getPropertyPath().toString()+" message:"+item.getMessage());
}
}else if(e instanceof ParamValidException){
List fieldErrorList = ((ParamValidException) e).getFieldErrors();
for(FieldError fieldError:fieldErrorList){
log.info("参数:"+fieldError.getField()+"\t错误原因:"+fieldError.getDefaultMessage());
}
}
modelAndView.addObject("result",false);
return modelAndView;
}
}