整個整合的核心就在這支interceptor裡面,將整個Action傳給Validator進行驗證,若有錯誤就將錯誤塞到FieldError裡面,對熟悉Struts2的人來說應該不難。須額外注意的是如果Action是CGLIB proxy的話,需要轉換成target object進行驗證,因為Spring AOP生成的proxy object裡面field並不會有值。
com.gss.gmo.cao.struts2.interceptor.SpringValidationInterceptor
package com.gss.gmo.cao.struts2.interceptor;
import static org.apache.commons.lang.StringUtils.isBlank;
import static org.apache.commons.lang.StringUtils.isNotBlank;
import static org.springframework.aop.support.AopUtils.getTargetClass;
import static org.springframework.aop.support.AopUtils.isCglibProxy;
import static org.springframework.util.ReflectionUtils.findField;
import static org.springframework.util.ReflectionUtils.getField;
import static org.springframework.util.ReflectionUtils.makeAccessible;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import lombok.Setter;
import org.apache.struts2.interceptor.validation.SkipValidation;
import com.gss.gmo.cao.struts2.interceptor.validation.ValidateFieldConfig;
import com.gss.gmo.cao.struts2.interceptor.validation.ValidateGroupConfig;
import com.gss.gmo.cao.struts2.interceptor.validation.ValidateMessageFormatter;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
/**
* Use Spring and JSR 303 to validate action data.
*
* @author linus_chien
*
*/
public class SpringValidationInterceptor extends MethodFilterInterceptor {
private static final long serialVersionUID = 1L;
@Setter
private Validator validator;
private ReflectionProvider reflectionProvider;
@Inject
public void setReflectionProvider(ReflectionProvider prov) {
this.reflectionProvider = prov;
}
@Override
protected String doIntercept(ActionInvocation invocation) throws Exception {
Object originalAction = invocation.getAction();
if (!(originalAction instanceof ActionSupport)) {
return invocation.invoke();
}
Object targetAction = originalAction;
if (isCglibProxy(originalAction)) {
targetAction = getTargetClass(originalAction).newInstance();
reflectionProvider.copy(originalAction, targetAction, invocation.getInvocationContext().getContextMap(), null, null);
}
Method method = targetAction.getClass().getDeclaredMethod(invocation.getProxy().getMethod(), new Class[] {});
if (method.isAnnotationPresent(SkipValidation.class)) {
return invocation.invoke();
}
ActionSupport originalActionSupport = (ActionSupport) originalAction;
ActionSupport targetActionSupport = (ActionSupport) targetAction;
ValidateGroupConfig groupConfig = method.getAnnotation(ValidateGroupConfig.class);
Class<?>[] groups = {};
if (groupConfig != null) {
groups = groupConfig.groups();
}
ValidateFieldConfig fieldConfig = method.getAnnotation(ValidateFieldConfig.class);
if (fieldConfig != null) {
for (String field : fieldConfig.fields()) {
if (isNotBlank(field)) {
validate(originalActionSupport, targetActionSupport, field, groups);
}
}
} else {
validate(originalActionSupport, targetActionSupport, "", groups);
}
return invocation.invoke();
}
/**
* @param originalActionSupport
* @param targetActionSupport
* @param propertyPathPrefix
* @param groups
*/
private void validate(ActionSupport originalActionSupport, ActionSupport targetActionSupport, String propertyPathPrefix, Class<?>... groups) {
Object target;
if (isBlank(propertyPathPrefix)) {
target = targetActionSupport;
} else {
Field targetField = findField(targetActionSupport.getClass(), propertyPathPrefix);
if (targetField == null) {
return;
}
makeAccessible(targetField);
target = getField(targetField, targetActionSupport);
if (target == null) {
return;
}
}
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(target, groups);
for (ConstraintViolation<Object> constraintViolation : constraintViolations) {
String propertyPath = constraintViolation.getPropertyPath().toString();
if (isNotBlank(propertyPathPrefix)) {
propertyPath = propertyPathPrefix + "." + propertyPath;
}
String errorMessage = constraintViolation.getMessage();
if (originalActionSupport instanceof ValidateMessageFormatter) {
errorMessage = ((ValidateMessageFormatter) originalActionSupport).format(originalActionSupport.getText(propertyPath, ""), errorMessage);
}
originalActionSupport.addFieldError(propertyPath, errorMessage);
}
}
}
import static org.apache.commons.lang.StringUtils.isBlank;
import static org.apache.commons.lang.StringUtils.isNotBlank;
import static org.springframework.aop.support.AopUtils.getTargetClass;
import static org.springframework.aop.support.AopUtils.isCglibProxy;
import static org.springframework.util.ReflectionUtils.findField;
import static org.springframework.util.ReflectionUtils.getField;
import static org.springframework.util.ReflectionUtils.makeAccessible;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import lombok.Setter;
import org.apache.struts2.interceptor.validation.SkipValidation;
import com.gss.gmo.cao.struts2.interceptor.validation.ValidateFieldConfig;
import com.gss.gmo.cao.struts2.interceptor.validation.ValidateGroupConfig;
import com.gss.gmo.cao.struts2.interceptor.validation.ValidateMessageFormatter;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
/**
* Use Spring and JSR 303 to validate action data.
*
* @author linus_chien
*
*/
public class SpringValidationInterceptor extends MethodFilterInterceptor {
private static final long serialVersionUID = 1L;
@Setter
private Validator validator;
private ReflectionProvider reflectionProvider;
@Inject
public void setReflectionProvider(ReflectionProvider prov) {
this.reflectionProvider = prov;
}
@Override
protected String doIntercept(ActionInvocation invocation) throws Exception {
Object originalAction = invocation.getAction();
if (!(originalAction instanceof ActionSupport)) {
return invocation.invoke();
}
Object targetAction = originalAction;
if (isCglibProxy(originalAction)) {
targetAction = getTargetClass(originalAction).newInstance();
reflectionProvider.copy(originalAction, targetAction, invocation.getInvocationContext().getContextMap(), null, null);
}
Method method = targetAction.getClass().getDeclaredMethod(invocation.getProxy().getMethod(), new Class[] {});
if (method.isAnnotationPresent(SkipValidation.class)) {
return invocation.invoke();
}
ActionSupport originalActionSupport = (ActionSupport) originalAction;
ActionSupport targetActionSupport = (ActionSupport) targetAction;
ValidateGroupConfig groupConfig = method.getAnnotation(ValidateGroupConfig.class);
Class<?>[] groups = {};
if (groupConfig != null) {
groups = groupConfig.groups();
}
ValidateFieldConfig fieldConfig = method.getAnnotation(ValidateFieldConfig.class);
if (fieldConfig != null) {
for (String field : fieldConfig.fields()) {
if (isNotBlank(field)) {
validate(originalActionSupport, targetActionSupport, field, groups);
}
}
} else {
validate(originalActionSupport, targetActionSupport, "", groups);
}
return invocation.invoke();
}
/**
* @param originalActionSupport
* @param targetActionSupport
* @param propertyPathPrefix
* @param groups
*/
private void validate(ActionSupport originalActionSupport, ActionSupport targetActionSupport, String propertyPathPrefix, Class<?>... groups) {
Object target;
if (isBlank(propertyPathPrefix)) {
target = targetActionSupport;
} else {
Field targetField = findField(targetActionSupport.getClass(), propertyPathPrefix);
if (targetField == null) {
return;
}
makeAccessible(targetField);
target = getField(targetField, targetActionSupport);
if (target == null) {
return;
}
}
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(target, groups);
for (ConstraintViolation<Object> constraintViolation : constraintViolations) {
String propertyPath = constraintViolation.getPropertyPath().toString();
if (isNotBlank(propertyPathPrefix)) {
propertyPath = propertyPathPrefix + "." + propertyPath;
}
String errorMessage = constraintViolation.getMessage();
if (originalActionSupport instanceof ValidateMessageFormatter) {
errorMessage = ((ValidateMessageFormatter) originalActionSupport).format(originalActionSupport.getText(propertyPath, ""), errorMessage);
}
originalActionSupport.addFieldError(propertyPath, errorMessage);
}
}
}