Pelicer
Pelicer

Reputation: 1584

SpelEvaluationException: Attempted to call method on null context object

I'm trying to do a simple cache task. I have a Holiday object, which has two fields: referenceDate and isHoliday. I then have a method that will make a HTTP request to a rest api in order to check if a date is a holiday or not. What I want to achieve is: if the current cached Holiday object has the same referenceDate as the one passed as a parameter, return the cached value. I have a specific class to perform that check. Here is the code:

Holiday class

@AllArgsConstructor
@Getter
public class Holiday {
    public LocalDate referenceDate;
    public boolean isHoliday;
}

CacheService class

@DomainService
public class CacheService {

    @Autowired
    private CacheManager cacheManager;

    public boolean isReferenceDateCached(final LocalDate referenceDate){
        final Holiday holiday = (Holiday) cacheManager.getCache("holiday").get("holidaycheck");
        return(holiday.getReferenceDate().equals(referenceDate));
    }
}

HolidatInfraService class

@AllArgsConstructor
@Service
@Slf4j
public class HolidayInfraService {

    @Autowired
    private final CacheService cacheService;

    @Cacheable(value = "holiday", key = "holidaycheck", condition = "#cacheService.isReferenceDateCached(#holidayDateToCheck)")
    public Holiday isHoliday(final LocalDate holidayDateToCheck) {
        //some code to call a rest api
    }

}

And this is the error I get from my unit test when I try to holidayInfraService.isHoliday(someDate):

org.springframework.expression.spel.SpelEvaluationException: EL1011E: Method call: Attempted to call method isReferenceDateCached(java.time.LocalDate) on null context object

From this exception message seems pretty obvious that cacheService is null. However, when I debbuged the code and got inside isHoliday, cacheService is not null. Maybe it hasn't been autowired yet by the time the annotation runs? It is also the first time I'm working with SPEL, so maybe something there too. If in fact cacheService is not autowired yet, is there a workaround?

Upvotes: 4

Views: 18271

Answers (3)

ThilankaD
ThilankaD

Reputation: 1099

Try

@Cacheable(value = "holiday", key = "holidaycheck", condition = "#{cacheService.isReferenceDateCached(#holidayDateToCheck)}")
public Holiday isHoliday(final LocalDate holidayDateToCheck) {
    //some code to call a rest api
}

Upvotes: -1

Ben
Ben

Reputation: 86

Because you are using the @Cacheable(...) annotation, Spring will evaluate that SpEL expression with the context class MethodBasedEvaluationContext. This will set the root object to be a CacheExpressionRootObject, and property lookups will be performed using that root object.

Since you are trying to refer to a property on a bean, the easiest solution would be to directly reference that bean in the SpEL expression. This is done with an @ (e.g. @myBeanName). Spring will then look for a bean with that name in the ApplicationContext. Remember that a bean defined without an explicit name will be named using the lower camel-came of the class name. For example, a class with the name MyBeanName will have a bean name of myBeanName.

Try changing your condition= block to

@Cacheable(value = "holiday", key = "holidaycheck", condition = "@cacheService.isReferenceDateCached(#holidayDateToCheck)")
public Holiday isHoliday(final LocalDate holidayDateToCheck) {
    //some code to call a rest api
}

Upvotes: 4

geobreze
geobreze

Reputation: 2422

In SpEL's MethodBasedEvaluationContext to access method variable, you need to use #, but no # is needed for class variables. Property lookup will automatically find getter for the property without # symbol. So, your SpEL should be

cacheService.isReferenceDateCached(#holidayDateToCheck)

And if this SpEL won't work, try creating getter method for cacheService.

Upvotes: 0

Related Questions