When doing a POC, we use a custom annotation with a SPEL expression. The SPEL expression contains a reference to a bean method:
@CheckEntity(keyPath = "@validatorService.isColorRed(#color)")
The custom annotation is integrated with with AOP (Aspects). When executing the Aspect method gives on evaluating the SPEL expression the following error:
org.springframework.expression.spel.SpelEvaluationException: EL1057E: No bean resolver registered in the context to resolve access to bean 'validatorService'
Can you help solving the access to the bean ?
First I tried to inject the validatorService (@Service) at different locations in the code. Below you find the simplified but essential code.
The custom annotation is:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CheckEntity {
String message() default "Check color msg";
String keyPath() default "";
}
Using the custom annotation with a SPEL expression using a bean.
@Service
public class ColorService {
ColorIsRedValidatorService validatorService;
public ColorService(ColorIsRedValidatorService validatorService) {
this.validatorService = validatorService;
}
@CheckEntity(keyPath = "@validatorService.isColorRed(#color)")
public String checkColorIsRed( String color) {
return "ok::" + color;
}
}
The Aspect code is:
@Component
@Aspect
public class CheckEntityAspect {
private Logger logger = LoggerFactory.getLogger(CheckEntityAspect.class);
private final ColorIsRedValidatorService validatorService;
public CheckEntityAspect(ColorIsRedValidatorService validatorService) {
this.validatorService = validatorService;
}
@Before("execution(* *.*(..)) && @annotation(checkEntity)")
public void checkEntity(JoinPoint joinPoint, CheckEntity checkEntity) {
Object[] args = joinPoint.getArgs();
ExpressionParser elParser = new SpelExpressionParser();
Expression expression = elParser.parseExpression(checkEntity.keyPath());
logger.info( "Within entity color Check: " + (String) expression.getValue(args));
// gives EXCEPTION: EL1057E: No bean resolver registered in the context to resolve access to bean 'validatorService'
// ...
}
}
The validatorService is also simplified:
@Service
public class ColorIsRedValidatorService {
public void isColorRed( String color) throws ColorIsNotRedException {
if( ! color.toLowerCase().matches( "red")) {
throw new ColorIsNotRedException( "Invalid red color: " + color);
}
}
}
As marked in the code, evaluating the SPEL expression gives the "No bean resolver registered" error.
In the approach above I autowired a validatorService at different spots in the code.
Alternatively, I created a config file launching the validator bean. Also without success.
@Configuration
public class ColorBeanResolved {
@Bean
ColorIsRedValidatorService validatorService() {
return new ColorIsRedValidatorService();
}
}
First, the parameter value passes to Expression#getValue()
should be the EvaluationContext
but not the join point 's argument.
Second, to make the SPEL can refer to a spring bean , you have to configure a BeanResolver
for the EvaluationContext
(See the docs for details).
Third , @validatorService
will look for a bean which the name is validatorService
from the context resolved by the BeanResolver
.But you do not define any beans with this name. You only has a bean called colorIsRedValidatorService
but not validatorService
.
So change to the followings should fix your problem :
@Component
@Aspect
public class CheckEntityAspect {
@Autowired
private ApplicationContext context;
@Before("execution(* *.*(..)) && @annotation(checkEntity)")
public void checkEntity(JoinPoint joinPoint, CheckEntity checkEntity) {
Object[] args = joinPoint.getArgs();
ExpressionParser elParser = new SpelExpressionParser();
Expression expression = elParser.parseExpression(checkEntity.keyPath());
StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariable("color", args[0]);
context.setBeanResolver(new BeanFactoryResolver(this.context));
logger.info( "Within entity color Check: " + (String) expression.getValue(context));
}
}
And explicitly configure the bean name of ColorIsRedValidatorService
to validatorService
@Service("validatorService")
public class ColorIsRedValidatorService {
}