46

I'm looking for 2 things:

  1. How to disable all caching during development with Spring boot "dev" profile. There doesn't seem to be a general setting to turn it all off in application.properties. What's the easiest way?

  2. How to disable caching for a specific method? I tried to use SpEl like this:

    @Cacheable(value = "complex-calc", condition="#${spring.profiles.active} != 'dev'}")
    public String someBigCalculation(String input){
       ...
    }
    

But I can get it to work. There are a couple of questions on SO related to this, but they refer to XML config or other things, but I'm using Spring Boot 1.3.3 and this uses auto-configuration.

I don't want to over-complicate things.

Garret Wilson
  • 18,219
  • 30
  • 144
  • 272
Wouter
  • 1,678
  • 3
  • 20
  • 32
  • 13
    add `spring.cache.type=NONE` to your `application-dev.properties`. – M. Deinum Mar 10 '16 at 13:03
  • 14
    `spring.cache.type=NONE` doesn't switch caching off, it prevents things from being cached. i.e. it still adds 27 layers of AOP/interceptor stack to your program, it's just that it doesn't do the caching. It depends what he means by "turn it all off". – David Newcomb Jun 29 '17 at 16:07
  • for ignoring cacheManager usage in Cacheable you need to use `NoOpCacheManager` bean as primary. https://stackoverflow.com/a/41586253/5456789 – Amir Azizkhani Mar 06 '22 at 05:32

5 Answers5

74

The type of cache is by default automatically detected and configured. However you can specify which cache type to use by adding spring.cache.type to your configuration. To disable it set the value to NONE.

As you want to do it for a specific profile add it to that profiles application.properties in this case modify the application-dev.properties and add

spring.cache.type=NONE

This will disable caching.

M. Deinum
  • 115,695
  • 22
  • 220
  • 224
  • 2
    What about my second question using SpEl? There are some specific methods that I don't want caching for during development. – Wouter Mar 10 '16 at 19:34
  • 1
    The [davidxxx](https://stackoverflow.com/a/56901878/1298031) answer is a best answer. It explains exactly the Spring behavior and the good solutions. – Nicolas Dos Santos Aug 28 '19 at 09:26
  • The davidxxx answer doesn't answer to using SpEL in the `@Cacheable` for specific cases, the davidxxx answer is just too broad... @PaulNUK's answer seem to understand that. – maxxyme Sep 24 '20 at 13:29
  • 1
    Sidenote: you can put this in your `application-junit` profile and use `@ActiveProfiles("junit")` in all of your tests. This will deactive the caching for any tests, preventing kept states between test runs (which you will have otherwise when using `@Cachable`). If you wan to test the caching in one test explicit, you can annotate the testclass as follows to active caching only there: `@TestPropertySource(properties = "spring.cache.type=")` – membersound Apr 08 '21 at 09:14
  • Value should be lowercased: ```none``` – user07 Jul 08 '22 at 13:57
57

The David Newcomb comment tells the truth :

spring.cache.type=NONE doesn't switch caching off, it prevents things from being cached. i.e. it still adds 27 layers of AOP/interceptor stack to your program, it's just that it doesn't do the caching. It depends what he means by "turn it all off".

Using this option may fast up the application startup but could also have some overheads.

1)To disable completely the Spring Cache feature

Move the @EnableCaching class in a dedicated configuration class that we will wrap with a @Profile to enable it :

@Profile("!dev")
@EnableCaching
@Configuration
public class CachingConfiguration {}

Of course if you already have a Configuration class that is enabled for all but the dev environment, just reuse it :

@Profile("!dev")
//... any other annotation 
@EnableCaching
@Configuration
public class NoDevConfiguration {}

2) Use a fake (noop) Cache manager

In some cases, activating @EnableCaching by profile is not enough because some of your classes or some Spring dependencies of your app expect to retrieve from the Spring container a bean implementing the org.springframework.cache.CacheManager interface.
In this case, the right way is using a fake implementation that will allow Spring to resolve all dependencies while the implementation of the CacheManager is overhead free.

We could achieve it by playing with @Bean and @Profile :

import org.springframework.cache.support.NoOpCacheManager; 

@Configuration
public class CacheManagerConfiguration {

    @Bean
    @Profile("!dev")
    public CacheManager getRealCacheManager() {
        return new CaffeineCacheManager(); 
        // or any other implementation
        // return new EhCacheCacheManager(); 
    }

    @Bean
    @Profile("dev")
    public CacheManager getNoOpCacheManager() {
        return new NoOpCacheManager();
    }
}

Or if it is more suitable, you can add the spring.cache.type=NONE property that produces the same result as written in the M. Deinum answer.

davidxxx
  • 125,838
  • 23
  • 214
  • 215
4

For your second question do something like this:

Write a method that determines whether or not a particular profile is active (environment is your injected Environment)

boolean isProfileActive(String profile) { 
   return Arrays.asList(environment.getActiveProfiles()).contains(profile);
}

then use that for your spel condition on the cacheable annotation

Kerem Baydoğan
  • 10,475
  • 1
  • 43
  • 50
PaulNUK
  • 4,774
  • 2
  • 30
  • 58
4

If you have just a default profile and don't want to go through creating a dev and prod profiles just for this, I think this could be a very quick solution for your project:

Set this in your application.properties:

appconfig.enablecache=true

Based on your requirements, you can change it to true/false.

Now, when defining your @Caching bean, do this:

@Bean
public CacheManager cacheManager(@Value("${appconfig.enablecache}") String enableCaching) {
    if (enableCaching.equals("true")) {
        return new EhCacheCacheManager();
        //Add your Caching Implementation here.
    }
    return new NoOpCacheManager();
}

When property is set to false, NoOpCacheManager() is returned, effectively switching off your caching.

0

In any class you want to globally enable or disable your caching, on a per method basis, you can do the following, which allows you to control at the class level how your methods are cached.

You can change it in application.properties with my.cache.enabled=true. This setup currently defaults to it being enabled.

Of course then you can change it in application-dev.properties as well, and set it to a different value. The advantage of this approach is it lets you make this more complex if you need to for some reason, on a per method basis as well.

public class MyClass {

  @Value("${my.cache.enabled:true}")
  public String isMyCacheEnabled;

  public boolean isMyCacheEnabled() {
    return (isMyCacheEnabled == null) || isMyCacheEnabled.equals("") || Boolean.valueOf(isMyCacheEnabled);
  }

  @Cacheable(
      cacheManager = "myCacheManager",
      cacheNames = "myCacheKey",
      condition = "#root.target.isMyCacheEnabled()",
      key = "#service + '-' + #key")
  public String getMyValue(String service, String key) {
    // do whatever you normally would to get your value
    return "some data" + service + key;
  }
}
Brad Parks
  • 66,836
  • 64
  • 257
  • 336