Hi all,

we have a legacy webapp running with Spring 6.1.6, which makes use of spring-aop for validation purpose.

The @Aspect looks like

package org.example.server.validation;

import org.example.server.domain.AbstractEntity;
import org.example.shared.exception.validation.ValidationException;
import org.example.shared.exception.validation.ValidationException.ValidationError;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Path;
import jakarta.validation.Path.Node;
import jakarta.validation.ValidatorFactory;
import jakarta.validation.groups.Default;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Iterator;
import java.util.Set;

@Aspect
public class ValidationAspect
{
    private static final Logger logger = LoggerFactory.getLogger(ValidationAspect.class);

    private static final String PROPERTY_PATH_SEPERATOR = ".";

    @Autowired
    private ValidatorFactory validatorFactory;

    @Before(value = "execution(* org.example.server..*.*(@jakarta.validation.Valid (org.example.server.domain.AbstractEntity+))) && args(entity)", argNames = "entity")
    public void validateDefaultGroup(JoinPoint jp, AbstractEntity entity)
    {
        validate(entity, Default.class);
    }

    @Before(value = "execution(* org.example.server..*.*(@jakarta.validation.Valid @Groups (org.example.server.domain.AbstractEntity+))) && args(entity)", argNames = "entity")
    public void validateGroups(JoinPoint jp, AbstractEntity entity)
    {
        Class<?>[] groups = null;
        if (jp.getArgs().length == 1 && jp.getSignature() instanceof MethodSignature)
        {
            final MethodSignature ms = (MethodSignature) jp.getSignature();

            if (ms.getMethod().getParameterAnnotations()[0].length == 2
                    && ms.getMethod().getParameterAnnotations()[0][1] instanceof Groups)
                groups = ((Groups) ms.getMethod().getParameterAnnotations()[0][1]).value();
        }

        if (groups != null)
            validate(entity, groups);
        else
        {
            logger.error("groups: null");
            throw new IllegalStateException("groups: null");
        }
    }

    private void validate(AbstractEntity entity, Class<?>... groups)
    {
      //some code
    }

}

and an XML-based configuration for it like

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

    <aop:aspectj-autoproxy />

    <bean class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"
        id="validatorFactory" />

    <bean class="org.example.server.validation.ValidationAspect" id="validationAspect" />

</beans>

This setup used to work fine with 6.1.6.

Upgrading to 6.1.7 results in

Caused by: java.lang.IllegalArgumentException: error Type referred to is not an annotation type: Configurable
    at org.aspectj.weaver.tools.PointcutParser.parsePointcutExpression(PointcutParser.java:319)
    at org.springframework.aop.aspectj.AspectJExpressionPointcut.buildPointcutExpression(AspectJExpressionPointcut.java:228)
    at org.springframework.aop.aspectj.AspectJExpressionPointcut.obtainPointcutExpression(AspectJExpressionPointcut.java:199)
    at org.springframework.aop.aspectj.AspectJExpressionPointcut.matches(AspectJExpressionPointcut.java:275)
    at org.springframework.aop.support.AopUtils.canApply(AopUtils.java:236)
    at org.springframework.aop.support.AopUtils.canApply(AopUtils.java:298)
    at org.springframework.aop.support.AopUtils.findAdvisorsThatCanApply(AopUtils.java:330)
    at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findAdvisorsThatCanApply(AbstractAdvisorAutoProxyCreator.java:128)
    at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findEligibleAdvisors(AbstractAdvisorAutoProxyCreator.java:97)
    at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean(AbstractAdvisorAutoProxyCreator.java:78)
    at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(AbstractAutoProxyCreator.java:368)
    at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(AbstractAutoProxyCreator.java:320)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:438)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1791)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600)
    ... 63 more

I would expect that in a minor version update, so my question is: Did our legacy code work "by luck" or is there something wrong?

Happy to provide more information, if required.

Comment From: snicoll

Thanks for upgrading and reporting the issue. I believe this is a duplicate of https://github.com/spring-projects/spring-framework/issues/32838.

If you have a chance to test your app with 6.1.8-SNAPSHOT that would be very much appreciated. 6.1.8 is due this Thursday.

Comment From: rzo1

@snicoll Thanks for the fast response. I can confirm, that it works with the SNAPSHOT.