OValというアノテーションベースのアノテーションによる入力制限を付けれるバリデーションFWを使ってみました。
そのときのコード例をメモします。
依存関係
pom.xml
<!-- https://mvnrepository.com/artifact/net.sf.oval/oval -->
<dependency>
<groupId>net.sf.oval</groupId>
<artifactId>oval</artifactId>
<version>1.90</version>
</dependency>
build.gradle
// https://mvnrepository.com/artifact/net.sf.oval/oval
compile group: 'net.sf.oval', name: 'oval', version: '1.90'
Springで使う場合
net.sf.oval.integration.spring.SpringValidator
が
org.springframework.validation.Validator
を実装しているらしいです。
まだ使っていないので、使ってみたらまたレポートします。
基本的なコードの例
ビーンクラス
public class SampleBean{
/** ID */
@Assert(expr = "null!=_value&&''!=_value", lang = "js"
//lang = は"bsh" / "beanshell", "groovy", or "js" / "javascript".のどれか好きなの選んで使ってね
//この程度だったら@NotEmpty使えばよかったかも…
,message="IDが入力されてないです")
private String id;
/** No */
private Integer no;
/** 登録日 */
@Assert(expr = "null!=_value&&''!=_value", lang = "js"
,message="登録日が入力されてないです")
@MatchPattern(pattern=DateUtil.DATE_MATCH_PATTERN
,message="登録日の形式がおかしいです"
,when="js:null!=_value&&''!=_value")
@DateRange(format=DateUtil.DATE_HH_MM_FORMAT,message="登録日の日付範囲がおかしいです")
@ValidateWithMethod(methodName="isValidDate", parameterType=String.class)
,message="12月の日付を入力してください")
private String abcTime;
//getter,setter省略
private boolean isValidDate(String arg){//12月だけtrue返すよ
if(arg.matches("\d{4}/12/.*")){
return true;
}else{
return false;
}
}
}
バリデートの仕方
net.sf.oval.Validator validator = new net.sf.oval.Validator();
List<ConstraintViolation> clist = validator.validate( validatedObject );
staticメソッドでバリデーションしたい
という場合は標準機能にはないので、バリデートのアノテーションとチェックメソッドを自作します。
java8で書いたほうがスマートなんですが、java7の環境しか使わせてもらえないので(T-T)、java7のコードです。
ValidateWithStaticMethod.java(アノテーション)
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import net.sf.oval.ConstraintTarget;
import net.sf.oval.ConstraintViolation;
import net.sf.oval.configuration.annotation.Constraint;
import net.sf.oval.configuration.annotation.Constraints;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD, ElementType.TYPE })
@Constraint(checkWith = ValidateWithStaticMethodCheck.class)
public @interface ValidateWithStaticMethod {
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD, ElementType.TYPE })
@Constraints
public @interface List {
ValidateWithStaticMethod[] value();
String when() default "";
}
ConstraintTarget[] appliesTo() default ConstraintTarget.CONTAINER;
String errorCode() default "net.sf.oval.constraint.ValidateWithMethod";
boolean ignoreIfNull() default true;
boolean ignoreIfNullOrEmpty() default true;
String message() default "メッセージが定義されていません(ValidateWithStaticMethod)";
Class<?> clazz();
String methodName();
String[] profiles() default {};
int severity() default 0;
String target() default "";
String when() default "";
}
ValidateWithStaticMethodCheck.java(チェックロジック)
import static net.sf.oval.Validator.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import net.sf.oval.ConstraintTarget;
import net.sf.oval.Validator;
import net.sf.oval.configuration.annotation.AbstractAnnotationCheck;
import net.sf.oval.context.OValContext;
import net.sf.oval.exception.ReflectionException;
/**
* Staticメソッドによるチェック
*/
public class ValidateWithStaticMethodCheck extends AbstractAnnotationCheck<ValidateWithStaticMethod> {
private static final long serialVersionUID = 1L;
private final Map<Class<?>, Method> validationMethodsByClass = new WeakHashMap<Class<?>, Method>();
private boolean ignoreIfNull;
private boolean ignoreIfNullOrEmpty;
private Class<?> clazz;
private String methodName;
@Override
public void configure(final ValidateWithStaticMethod constraintAnnotation) {
super.configure(constraintAnnotation);
setMethodName(constraintAnnotation.methodName());
setClazz(constraintAnnotation.clazz());
setIgnoreIfNull(constraintAnnotation.ignoreIfNull());
setIgnoreIfNullOrEmpty(constraintAnnotation.ignoreIfNullOrEmpty());
}
@Override
protected Map<String, String> createMessageVariables() {
final Map<String, String> messageVariables = getCollectionFactory().createMap(4);
messageVariables.put("ignoreIfNull", Boolean.toString(ignoreIfNull));
messageVariables.put("ignoreIfNullOrEmpty", Boolean.toString(ignoreIfNullOrEmpty));
messageVariables.put("methodName", methodName);
return messageVariables;
}
@Override
protected ConstraintTarget[] getAppliesToDefault() {
return new ConstraintTarget[] { ConstraintTarget.VALUES };
}
public String getMethodName() {
return methodName;
}
public boolean isIgnoreIfNull() {
return ignoreIfNull;
}
public boolean isSatisfied(final Object validatedObject, final Object valueToValidate, final OValContext context, final Validator validator)
throws ReflectionException {
if (valueToValidate == null && ignoreIfNull)
return true;
if(ignoreIfNullOrEmpty){
if (valueToValidate == null)
return true;
//emptyの判定条件を追加したい場合、ここに追記してください
if ( valueToValidate instanceof String && "".equals(valueToValidate) )
return true;
if ( valueToValidate instanceof Object[] && 0==((Object[])valueToValidate).length )
return true;
if ( valueToValidate instanceof List && 0==((List<?>)valueToValidate).size() )
return true;
}
boolean result;
Method method;
try {
Class<?> parameterType = valueToValidate.getClass();
method = clazz.getMethod(methodName, parameterType);
result = (boolean)method.invoke(null, valueToValidate);
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new ReflectionException( e );
}
return result;
}
public void setIgnoreIfNull(final boolean ignoreIfNull) {
this.ignoreIfNull = ignoreIfNull;
requireMessageVariablesRecreation();
}
public void setIgnoreIfNullOrEmpty(final boolean ignoreIfNullOrEmpty) {
this.ignoreIfNullOrEmpty = ignoreIfNullOrEmpty;
requireMessageVariablesRecreation();
}
public Class<?> getClazz() {
return clazz;
}
public void setClazz(Class<?> clazz) {
this.clazz = clazz;
}
public void setMethodName(final String methodName) {
this.methodName = methodName;
synchronized (validationMethodsByClass) {
validationMethodsByClass.clear();
}
requireMessageVariablesRecreation();
}
}
アノテーションの付け方
//ValidationMethods.isFormatTelNo(String str)を使う場合のコード例
@ValidateWithStaticMethod(clazz=ValidationMethods.class
,methodName="isFormatTelNo"
,message="電話番号がおかしいです。。。")
private String telNo;
同じアノテーション複数つけたい
こう書けばいいみたいです。
@ValidateWithStaticMethod.List(value={
@ValidateWithStaticMethod(clazz=ValidationMethods.class
,methodName="isFormatTelNo"
,message="電話番号の形式がおかしいです"),
@ValidateWithStaticMethod(clazz=ValidationMethods.class
,methodName="isOkTelNumberSize"
,message="電話番号の桁数がおかしいです")
})
private String telNo;
標準で提供されているアノテーション一覧
- @Assert
- @AssertConstraintSet
- @AssertFalse
- @AssertFieldConstraints
- @AssertNull
- @AssertTrue
- @AssertURL
- @AssertValid
- @CheckWith
- @DateRange
- @Digits
- @EqualToField
- @Future
- @HasSubstring
- @InstanceOf
- @InstanceOfAny
- @Length
- @MatchPattern
- @Max
- @MaxLength
- @MaxSize
- @MemberOf
- @Min
- @MinLength
- @MinSize
- @NoSelfReference
- @NotBlank
- @NotEmpty
- @NotEqual
- @NotEqualToField
- @NotMatchPattern
- @NotMemberOf
- @NotNegative
- @NotNull
- @Past
- @Range
- @Size
- @ValidateWithMethod