Aspect-oriented programming (AOP) is a relatively new and promising programming paradigm grown out mainly of object-oriented programming. Instead of functions, modules or OOP’s objects it uses aspects as modularity units. An aspect is a result of some functionality scattered across multiple classes or methods (a crosscutting concern, as it is usually named in AOP) being isolated as a standalone module or class. Working with aspects allows to integrate crosscutting functionalities into an object-oriented application.

Some advantages of using AOP are:

  • separation of В the cross-cutting concerns from core application logic;
  • encapsulation of crosscutting functionality in one class/module;
  • application code becomes much more readable and easier to maintain.

This article will explain how to validate user form submissions using aspect-oriented programming, which ensures validity of the objects passed to application’s service methods. Meanwhile, we will introduce more details of the AOP approach, and, in particular, its implementation in the Spring AOP framework.

We’ll start with a User bean class that is passed to createUser() method of DAO class. It has several simple properties such as first name, last name, an email, and a custom annotation that provides parameters for validation:


public class User {

@CheckRegexp(expression = "[A-Za-z]{2,}", messageRef = "error_validate_firstName",
 policy = ValidationPolicy.ADD)
private String firstName;

@CheckRegexp(expression = "[A-Za-z]{2,}", messageRef = "error_validate_lastName",
 policy = ValidationPolicy.ADD)
private String lastName;

@CheckRegexp(expression = "[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9]+" +
 "(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})", messageRef = "error_validate_email",
 policy = ValidationPolicy.ADD)
private String email;

// getters and setters

}

CheckRegexp annotation has the following properties:

  • expression — В a regular expression to validate corresponding field
  • messageRef — an error message identifier that specifies the message that will be returned if current fieldВ doesn’tВ match regular expression
  • ValidationPolicy — an array of actions, e.g. add user, edit user, etc.

Full code for CheckRegexp annotation type declaration:


@Retention (RetentionPolicy.RUNTIME)
public @interface CheckRegexp {
 String expression() default ".*";
 String messageRef() default "";
 ValidationPolicy[] policy() default {ValidationPolicy.ADD};
}

Let’s now define UserDao interface that contains methods for creating and editing users and for removing them from database. To be able to validate new or existing users we will provide annotations for these methods:


public interface UserDao {
 @Validated(policy = ValidationPolicy.ADD)
 void createUser(@CheckObject User user) throws CustomException;
}

Now it is time to define some AOP concepts and terminology:

  • Aspect: a module of cross-cutting implementation. Aspect represents a mechanism which enables you to call a certain method (advice) before, around or after execution of some other method. In Spring AOP framework aspects are implemented using regular classes. Aspects and the rest of the code are combined into executable unit using weaver.
  • Join point: a point during the execution of the application where the aspect code is actually run, e.g. execution of a method or handling an exception.
  • Advice: an action taken by an aspect at a particular join point. In our example we will use a “before”-type advice action.
  • Pointcut: a set of join points where advice should be executed.
  • Weaving: a process of linking aspects with application types or objects to create an advised object.

Main validation aspect may thus be defined like this:

@Aspect
public class ValidationAspect {

    @Pointcut("execution (public * com.verification.*.*(..))")
    public void allPublicMethods() {
    }

    @Before("allPublicMethods()")
    public void validateBefore(JoinPoint joinPoint) throws Exception {
        Signature signature = joinPoint.getSignature();
        MethodSignature mSignature = (MethodSignature) signature;
        Object[] args = joinPoint.getArgs();

        try {
            ArgumentsValidator.validate(mSignature.getMethod(), args);
        } catch (CustomException exception) {
            throw exception;
        }
    }
}

This is a regular java class with @Aspect annotation which tells AspectJ that it should verify user’s data using specified regular expressions before creating and saving a user. Method allPublicMethods() defines a pointcut expression which matches if a method execution point represents the execution of any public method within the specified package. Note the “before” advice part — it specifies that the validation should be called before entering the method.

ArgumentsValidator class contains map of verifiers (RegExpValidator, ObjectValidator), processes annotations of createUser() method in UserDao class, creates an instance of validator and calls validate() method of ObjectValidator class. Here’s the code for validate():

@Override
public void validate(Object param, Annotation annotation, Annotation methodAnnotation) throws CustomException {
    Class<?> beanClass = param.getClass();
    Field[] fields = beanClass.getDeclaredFields();
    Map<Class<?>, Class<? extends IVerifier>> verifiersMap = ArgumentsValidator.getVerifiersMap();

    for (Field field : fields) {
        Annotation[] fieldAnnotations = field.getAnnotations();
        for (Annotation fieldAnnotation : fieldAnnotations) {
            if (verifiersMap.containsKey(fieldAnnotation.annotationType())) {
                Class<? extends IVerifier> verifierClass = verifiersMap.get(fieldAnnotation.annotationType());
                IVerifier verifier = null;

                try {
                    verifier = verifierClass.newInstance();
                } catch (Exception exception) {
                    exception.printStackTrace();
                    throw new CustomException("", "can't create varifier instance");
                }

                Object value = null;

                try {
                    value = new PropertyDescriptor(field.getName(), beanClass).getReadMethod().invoke(param);
                } catch (Exception exception) {
                    exception.printStackTrace();
                    throw new CustomException("", "can't create property descriptor");
                }

                verifier.validate(value, fieldAnnotation, methodAnnotation);
            }
        }
    }
}

Another validate() method of the RegExpValidator class handles annotations of the User bean class and throws CustomException if the field doesn’t match the regular expression. CustomException extends from java.lang.Exception and has method to retrieve friendly error message from the resource file that contains error keys and values:

error_validate_firstName=Invalid format of First name
error_validate_lastName=Invalid format of Last name
error_validate_email=Invalid email address

Code for RegExpValidator’s validate():

@Override
public void validate(Object param, Annotation annotation, Annotation methodAnnotation) throws CustomException {
    CheckRegexp regexpAnnotation = (CheckRegexp) annotation;
    ValidationPolicy policy;

    if (methodAnnotation != null)  {
        policy = ((Validated) methodAnnotation).policy();
    } else {
        policy = ValidationPolicy.ADD;
    }

    for (int i = 0; i < regexpAnnotation.policy().length; i++) {
        if (policy.equals(regexpAnnotation.policy()[i])) {
            Pattern pattern = Pattern.compile(regexpAnnotation.expression());
            Matcher matcher = pattern.matcher((String) param);

            if (!matcher.matches()) {
                throw new CustomException(regexpAnnotation.messageRef(), "regexp validation error");
            }

            break;
        }
    }
}

The @AspectJ support is enabled by including the following element into the aspects.xml config file:

<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:aop="http://www.springframework.org/schema/aop"
 xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

 <aop:aspectj-autoproxy />

 <bean id="userDao" class="com.verification.UserDaoImpl" />

 <!-- Aspect -->
 <bean id="validaionAspect" class="com.verification.ValidationAspect" />

</beans>

So to get UserDaoImpl instance and validate the object we have to do the following:

private static void createUser(User user) {
    UserDao userVerifier = (UserDao) aspectContext.getBean("userDao");

    try {
        userVerifier.createUser(user);
    } catch (CustomException exception) {
        System.out.println(exception.getFriendlyMessage());
    }
}

Summary

The idea of identifying cross-cutting concepts and putting all the related code into separate classes can help to isolate validation logic from base logic of the application. It becomes easier to understand and support the application, and if we need to add more properties to user form we will only have to define additional validation rules in User class and won’t have to make any changes to validation logic.

We have prepared a sample project with all the code. Download it here: Form validation using AOP in Spring.zip

The following libraries were used in the application (also included in the zip):

  • aopalliance-1.0.jar
  • aspectjrt-1.6.9.jar
  • aspectjweaver-1.6.8.jar
  • commons-logging-1.0.4.jar
  • org.springframework.aop-3.0.5.jar
  • org.springframework.asm-3.0.5.jar
  • org.springframework.beans-3.0.5.jar
  • org.springframework.context-3.0.5.jar
  • org.springframework.core-3.0.5. jar
  • org.springframework.expression-3.0.5. jar
Insert math as
Block
Inline
Additional settings
Formula color
Text color
#333333
Type math using LaTeX
Preview
\({}\)
Nothing to preview
Insert