1

I need to get a token from an external API and keep it cached. I need an api key, email and password.

Naturally I thought that creating an object with all of these elements would be the way to go:

public class LoginObj {
    private String apiKey;
    private String email;
    private String password;
    //... sets, gets and constructors

but it doesn't seem to cache in a method like this:

@Cacheable(value = "token")
public String getToken(LoginClass login) {
    System.out.println("no cache");
    return webClient.post()
            .uri("my/uri")
            .header("Api-Key", login.getApiKey())
            .accept(MediaType.APPLICATION_JSON)
            .bodyValue(new LoginClass( login.getEmail() , login.getPassword() ))
            .retrieve()
            .bodyToMono(JsonNode.class)
            .block()
            .get("token")
            .asText();
}

the "no cache" println keeps showing up, which shows that the method still been called.

The only way that it seems to work is like this:

@Cacheable(value = "token")
public String getToken(String apiKey, String email, String senha) {
    System.out.println("no cache");
    return webClient.post()
            .uri("my/uri")
            .header("Api-Key", apiKey)
            .accept(MediaType.APPLICATION_JSON)
            .bodyValue(new LoginObj(email, senha))
            .retrieve()
            .bodyToMono(JsonNode.class)
            .block()
            .get("token")
            .asText();
}

Seems like whenever there is an object as a parameter it won't work. Can I pass an object as a parameter or is this a limitation on this case?

arksdf
  • 363
  • 1
  • 2
  • 13
  • Which version of spring are you using? – aksappy Jul 28 '21 at 15:05
  • 3
    Most likely your `LoginObj` is not overriding `equals()` and does the default which is `==` so `a.equals(b)` returns false despite having the same string values. – OscarRyz Jul 28 '21 at 15:09
  • @arksappy 2.4.1 – arksdf Jul 28 '21 at 15:11
  • @OscarRyz I didn't get it – arksdf Jul 28 '21 at 15:13
  • 2
    @arksdf Spring Caching uses the `equals` method on the provided types in the method signature. You have to provide this `equals` method in your class `LoginObj` in order to have a proper caching behavior. – Seelenvirtuose Jul 28 '21 at 15:19
  • 1
    The cache relies on the `equals()` method to know if is an existing key or not. You're not showing the full `LoginObj` implementation but is probable you didn't override it, and thus `a.equals(b)` is returning false and executing the method again. Check https://stackoverflow.com/questions/2265503/why-do-i-need-to-override-the-equals-and-hashcode-methods-in-java – OscarRyz Jul 28 '21 at 15:19

1 Answers1

1

Probably you're not overriding equals & hashCode() in LoginObj

For example the following will print "false"

class L {
   String s;
   
   public static void main(String ... args) {
      var a = new L();
      a.s = "should be the same";

      var b = new L();
      b.s = "should be the same";    

      System.out.println(a.equals(b));
   }
}  

It prints false because despite the fact they contain the same value ("should be the same") they're different objects. Think of a person, or a dog, just because they have the same name doesn't mean they're the same.

You have to explicitly define what makes an instance of your class equals to another, in your case you want the three strings to match

class LoginObj {
     ...
     @Override
     public boolean equals(Object o) {
         return o instanceof LoginObj
            && apiKey.equals((LoginObj)o).apiKey
            && email.equals((LoginObj)o).email
            && password.equals((LoginObj)o).password
     }
     ...

}

You have to override hashCode as well. See this answer for more details: Why do I need to override the equals and hashCode methods in Java?.

OscarRyz
  • 196,001
  • 113
  • 385
  • 569