Here is the modified @Validateable annotation:
With this modification, we don't need to provide class type to @Validateable. The validators value is optional. If we leave the validators value empty, our validation aspect will use uncapitalize(beanName) + "Validator" as the default validator value.
package com.mycompany.myapp.aspect.bind;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Validateable {
String[] validators() default {};
}
Below is the modified Validation aspect:
There are some modifications in the validate() method:
package com.mycompany.myapp.aspect.bind;
import java.lang.annotation.Annotation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.apache.commons.lang.StringUtils
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Validator;
import org.springframework.web.bind.annotation.ModelAttribute;
@Aspect
public class Validation implements ApplicationContextAware {
private ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// TODO Auto-generated method stub
if (this.applicationContext == null)
this.applicationContext = applicationContext;
}
private Validator getValidator(String validator) {
return (Validator) applicationContext.getBean(validator);
}
@Before("execution(* (@org.springframework.stereotype.Controller *..*Controller).*(..)) && " +
"@annotation(validateable)")
public void validate(JoinPoint joinPoint, Validateable validateable) throws Throwable {
Object target = null;
BindingResult result = null;
// Get validators' id/names
String[] validators = validateable.validators();
// Get the arguments
Object[] args = joinPoint.getArgs();
// Get annotations of the arguments
Annotation[][] annotations = ((MethodSignature) joinPoint.getSignature()).getMethod()
.getParameterAnnotations();
// Check arguments array and retrieve the target bean and the BindingResult object
for (int i = 0; i < args.length; i++) {
if (target != null && result != null)
break;
else if (args[i] instanceof BindingResult)
result = (BindingResult) args[i];
else if (annotations[i].length > 0) {
for (Annotation annotation: annotations[i]) {
if (annotation.annotationType()
.isAssignableFrom(ModelAttribute.class)) {
target = args[i];
break;
}
}
}
}
if (validators.length == 0)
validators = new String[] {
StringUtils.uncapitalize(
target.getClass().getSimpleName())
.concat("Validator") };
for (String validator: validators) {
// Get the validator bean from application context and validate the target bean
getValidator(validator).validate(target, result);
if (result.hasErrors())
break;
}
}
}
- The validate() method uses @Before instead of @Around. It will accept the normal join point.
- The validate() method doesn't use Architecture.controllerExecution() pointcut. It uses a new pointcut to locate any class annotated with @Controller (I'm not sure if this approach will have performance issue. I usually use the previous approach where the pointcut is declared verbosely.)
- The validate() method uses parameter annotations to locate the position of target bean. We can use AspectJ annotated parameter expression to retrieve the target bean but there is one problem: The position of BindingResult object and the target bean can be random!! This is why we stick to tradional way to locate the annotated parameter.
Because we don't provide validators value, the default validators value will be exampleValidator ^^
@RequestMapping(method=RequestMethod.POST, params="create")
@Validateable
public String create(@ModelAttribute("example") Example example,
BindingResult bindingResult, WebRequest webRequest) {
if (bindingResult.hasErrors()) {
System.out.println("AspectJ validation is amazing!!!");
}
return null;
}