Good evening all,
I'm new to Spring, let along Spring's caching. That said, I have a group of Maven projects all under the umbrella of a project, each of which requires some amount of caching. That said, my initial attempt at caching with multiple cache managers was failing... in some research I read some about using a custom CacheResolver and binding multiple cacheManagers within that. This thread spawned my interest.
Based on the answers there, I had some questions and was advised to post it in a new question. The gist of my question(s) is:
First off, is there an advantage to having multiple cacheManagers. For example on the project I'm working on, I originally thought we would need multiple managers, but I think if we were to put a singular manager in a -common maven project that all of our other projects use, then we could get away with the one (and it manages all of our caches (30+)).
Secondly, in the above example, let's assume I declare multiple cacheManagers in CustomCacheManager. I don't quite follow on how I determine which manager to use?
Thanks in advance!
Declare one cache manager as primary. Here is an example for reference:
#Configuration
#EnableCaching
#PropertySource(value = { "classpath:/cache.properties" })
public class CacheConfig {
#Bean
#Primary
public CacheManager hazelcastCacheManager() {
ClientConfig config = new ClientConfig();
HazelcastInstance client = HazelcastClient.newHazelcastClient(config);
return new HazelcastCacheManager(client);
}
#Bean
public CacheManager guavaCacheManager() {
GuavaCacheManager cacheManager = new GuavaCacheManager("mycache");
CacheBuilder<Object, Object> cacheBuilder = CacheBuilder.newBuilder()
.maximumSize(100)
.expireAfterWrite(10, TimeUnit.MINUTES);
cacheManager.setCacheBuilder(cacheBuilder);
return cacheManager;
}
}
Then specify it class level:
#Service
#CacheConfig(cacheManager="hazelcastCacheManager")
public class PolicyServiceImpl implements PolicyService {
}
You can also put it to method level:
#Service
public class PolicyServiceImpl implements PolicyService {
#Override
#Cacheable(value = "POLICY_", key = "#id", cacheManager= "guavaCacheManager")
public Policy getPolicyDetails(int id) {
return new Policy(id, "IX4546");
}
}
Also another way of mentioning at method level or class level would be:
#CacheConfig(cacheManager = "ehCacheManager")
#Target(value = ElementType.TYPE)
#Retention(value = RetentionPolicy.RUNTIME)
public #interface EhCacheable {
}
#EhCacheable
#Service
public class PolicyServiceImpl implements Policy {
}
You can also refer this link for custom cache resolver: https://github.com/isaolmez/spring-cache-samples/tree/master/spring-cache-custom
Related
I am using custom timers to instrument a ton of fields via Micrometer. Ideally I do not want the metrics reported for this specific meter that have a count of zero between the configured step interval. This is not crucial but would love to potentially reduce noise of what is getting sent to NR every x seconds.
I've created an extension off NewRelicMeterRegistry that overrides the publish() method to add the functionality before the default behavior.
public class FilteringNewRelicMeterRegistry extends NewRelicMeterRegistry {
public FilteringNewRelicMeterRegistry(NewRelicConfig config, Clock clock) {
super(config, clock);
}
/**
* Remove field metrics that have not been used since the last publish.
*/
#Override
protected void publish() {
getMeters().stream()
.filter(filterByMeterId(...)))
.filter(meter -> ((Timer) meter).count() == 0)
.forEach(this::remove);
super.publish();
}
}
But for the life of me, I can't figure out how to get the AutoConfiguration to prefer this implementation over the default NewRelicMeterRegistry.
How do I get spring-boot or micrometer to honor my implementation and use that as the designated bean in the application context for autowiring purposes?
Also if there is an out of the box way to override this behavior via micrometer abstractions or utility, awesome that would be even better! Please let me know. I've tried using MeterRegistryCustomizer but that didn't seem to have what I needed.
I want to avoid using Spring's scheduling functionality via #Scheduled, would like to do this on an "on publish" basis.
if you don't want to default auto configuration disable default with this
#SpringBootApplication(exclude = { NewRelicMetricsExportAutoConfiguration.class })
and extends your FilteringNewRelicMeterRegistry class with StepMeterRegistry and configure with your responsibilities, because StepMeterRegistry is subclasses to MeterRegistry and micrometer detect your configuration
after register your custom configuration with this configuration class same as NewRelicMetricsExportAutoConfiguration StepMeterRegistry is need StepRegistryConfig and Clock is use default NewRelicConfig and clock and register like this, I read NewRelicMetricsExportAutoConfiguration and simplify configuration like this
#Configuration(proxyBeanMethods = false)
#AutoConfigureBefore({ CompositeMeterRegistryAutoConfiguration.class, SimpleMetricsExportAutoConfiguration.class })
#AutoConfigureAfter(MetricsAutoConfiguration.class)
#ConditionalOnProperty(prefix = "management.metrics.export.newrelic", name = "enabled", havingValue = "true",
matchIfMissing = true)
#EnableConfigurationProperties(NewRelicProperties.class)
public class FilteringNewRelicConfiguration {
private final NewRelicProperties properties;
public FilteringNewRelicConfiguration(NewRelicProperties properties) {
this.properties = properties;
}
#Bean
public NewRelicConfig newRelicConfig() {
return new NewRelicPropertiesConfigAdapter(this.properties);
}
#Bean
public FilteringNewRelicMeterRegistry filteringNewRelicMeterRegistry(NewRelicConfig newRelicConfig, Clock clock) {
return new FilteringNewRelicMeterRegistry(newRelicConfig, clock)
}
}
I have a set of cached methods that look somewhat like this:
#Cacheable(value = "myCacheName", keyGenerator = "myKeyGenerator")
public Product getProduct(ProductRequest request) {
// ...
}
And I need to set different time to live (expiration interval) for objects returned by these methods.
Problem: According to the documentation, the offered way is using #RedisHash(timeToLive=…) or #TimeToLive annotations on the return type of the methods. However, I don't want to pollute my domain classes with caching related logic. In addition, some of my methods return strings or objects of classes which I can not modify. I would prefer to implement it in a more configurable way. There is also a configuration property called spring.cache.redis.time-to-live, but it applies the same time-to-live in all places.
Question: Is there a way to specify time to live/expiration interval on the method level? Or generally, how to implement it in a more elegant way?
Hi if you want to use only Spring annotations one way to do this is the following.
#CacheConfig annotation allows you to define specific CacheManager to use further more the #Cacheable annotation also allows defining cacheManager
#CacheConfig(cacheNames="myCacheName",cacheManager="timeoutCacheManager")
class ProductReader {
#Cacheable(value = "myCacheName", keyGenerator = "myKeyGenerator")
public Product getProduct(ProductRequest request) {
// ...
}
}
#Bean
public CacheManager timeoutCacheManager(RedisTemplate redisTemplate) {
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
cacheManager.setDefaultExpiration(mytimeToLive);
return cacheManager;
}
Here is also a fragment of a more extensive cache configuration that is resulting again in a CacheManager. This time it configures multiple regions:
#Bean (name="cacheManager")
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration conf_ready_info = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMillis(50000));
RedisCacheConfiguration conf_base_info = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMillis(60000));
Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<String, RedisCacheConfiguration>();
cacheConfigurations.put("base_info", conf_base_info);
cacheConfigurations.put("ready_info", conf_ready_info);
return RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(connectionFactory)
.withInitialCacheConfigurations(cacheConfigurations).build();
}
I took the last example from:
set expire key at specific time when using Spring caching with Redis
Using only #Cacheable(value = "myCacheName", keyGenerator = "timeoutCacheManager")
I'm running a PoC around replacing bean injection at runtime after a ConfigurationProperties has changed. This is based on spring boot dynamic configuration properties support as well summarised here by Dave Syer from Pivotal.
In my application I have a simple interface implemented by two different concrete classes:
#Component
#RefreshScope
#ConditionalOnExpression(value = "'${config.dynamic.context.country}' == 'it'")
public class HelloIT implements HelloService {
#Override
public String sayHello() {
return "Ciao dall'italia";
}
}
and
#Component
#RefreshScope
#ConditionalOnExpression(value = "'${config.dynamic.context.country}' == 'us'")
public class HelloUS implements HelloService {
#Override
public String sayHello() {
return "Hi from US";
}
}
application.yaml served by spring cloud config server is:
config:
name: Default App
dynamic:
context:
country: us
and the related ConfigurationProperties class:
#Configuration
#ConfigurationProperties (prefix = "config.dynamic")
public class ContextHolder {
private Map<String, String> context;
Map<String, String> getContext() {
return context;
}
public void setContext(Map<String, String> context) {
this.context = context;
}
My client app entrypoint is:
#SpringBootApplication
#RestController
#RefreshScope
public class App1Application {
#Autowired
private HelloService helloService;
#RequestMapping("/hello")
public String hello() {
return helloService.sayHello();
}
First time I browse http://locahost:8080/hello endpoint it returns "Hi from US"
After that I change country: us in country: it in application.yaml in spring config server, and then hit the actuator/refresh endpoint ( on the client app).
Second time I browse http://locahost:8080/hello it stills returns "Hi from US" instead of "ciao dall'italia" as I would expect.
Is this use case supported in spring boot 2 when using #RefreshScope? In particular I'm referring to the fact of using it along with #Conditional annotations.
This implementation worked for me:
#Component
#RefreshScope
public class HelloDelegate implements HelloService {
#Delegate // lombok delegate (for the sake of brevity)
private final HelloService delegate;
public HelloDelegate(
// just inject value from Spring configuration
#Value("${country}") String country
) {
HelloService impl = null;
switch (country) {
case "it":
this.delegate = new HelloIT();
break;
default:
this.delegate = new HelloUS();
break;
}
}
}
It works the following way:
When first invocation of service method happens Spring creates bean HelloDelegate with configuration effective at that moment; bean is put into refresh scope cache
Because of #RefreshScope whenever configuration is changed (country property particularly in this case) HelloDelegate bean gets cleared from refresh scope cache
When next invocation happens, Spring has to create bean again because it does not exist in cache, so step 1 is repeated with new country property
As far as I watched the behavior of this implementation, Spring will try to avoid recreating RefreshScope bean if it's configuration was untouched.
I was looking for more generic solution of doing such "runtime" implementation replacement when found this question. This implementation has one significant disadvantage: if delegated beans have complex non-homogeneous configuration (e.g. each bean has it's own properties) code becomes lousy and therefore unsafe.
I use this approach to provide additional testability for artifacts. So that QA would be able to switch between stub and real integration without significant efforts. I would strongly recommend to avoid using such approach for business functionality.
Have two different modules currently Let say Project A and Project B. Project B imported/used into/in Project A. Currently Project B already have CacheManager.
Project B
public class CacheConfig {
#Bean
public CacheManager cacheManager() {
// using SimpleCacheManager()
}
}
But now planed to implement CacheManager in Project A for someother Purpose.
class SomeCacheConfig{
#Bean
public CacheManager someCacheManager(){
// using SimpleCacheManager()
}
}
While loading application throws below exception.
java.lang.IllegalStateException: No CacheResolver specified, and no unique bean of type CacheManager found. Mark one as primary (or give it the name 'cacheManager') or declare a specific CacheManager to use, that serves as the default one.
Can you please help me how to achieve multiple cacheManager in multiple modules/projects.
ok then.
put #Primary on the CacheManager bean that will use as default.
#Primary
#Bean(name = "primaryCacheManager")
public CacheManager primaryCacheManager() {
return new SimpleCacheManager();
}
#Bean(name = "myCacheManager")
public CacheManager myCacheManager() {
return new SimpleCacheManager();
}
and when you want to use another one(i.e. not a default), explictly define a name of CacheManager bean with #Qualifier annotation.
#Autowired
#Qualifier("myCacheManager")
private CacheManager myCacheManager;
or if you use annotation base Spring Cache implementation, you can also define a CacheManager name as property of those annotations
#Cacheable(value = "some",cacheManager = "myCacheManager")
public String getSome(){
return "";
}
You can use the CompositeCacheManager implementation provided by Spring (https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/cache/support/CompositeCacheManager.html)
This allows you to compose a list of cache managers. The composite manager will iterate through the list and get the cache in the first manager it exists in. Please note that "Note: Regular CacheManagers that this composite manager delegates to need to return null from getCache(String) if they are unaware of the specified cache name, allowing for iteration to the next delegate in line. However, most CacheManager implementations fall back to lazy creation of named caches once requested; check out the specific configuration details for a 'static' mode with fixed cache names, if available."
What eventually worked for me as Erik Ahlswede suggested
#Bean
public CacheManager cacheManager() {
return new CompositeCacheManager(
new ConcurrentMapCacheManager("cacheA") {
#Override
protected Cache createConcurrentMapCache(final String name) {
return new ConcurrentMapCache(name,
CacheBuilder.newBuilder()
.expireAfterWrite(CACHE_TTL_IN_SECONDS, TimeUnit.SECONDS)
.maximumSize(MAX_ENTRIES_IN_CACHE)
.build().asMap(), false);
}
},
new ConcurrentMapCacheManager("cacheB") {
#Override
protected Cache createConcurrentMapCache(final String name) {
return new ConcurrentMapCache(name,
CacheBuilder.newBuilder()
.expireAfterWrite(CACHE_TTL_IN_SECONDS, TimeUnit.SECONDS)
.maximumSize(MAX_ENTRIES_IN_CACHE)
.build().asMap(), false);
}
}
);
}
And then use it with
#Cacheable(cacheNames = "someComplicatedAction", cacheManager = "cacheA")
public String someComplicatedAction() {
}
I'd like to configure "transactional" beans from my Spring #Configuration class instead of annotating the class implementation itself with #Transactional.
Kind of like the old school way, configuring transactional advice from an XML file, but without needing a String reference to my class/method names to create pointcuts.
The reason is that the bean implementation is in another code base, and the module it belongs to doesn't depend on Spring. Read : I'm not touching the source code of that bean, just instanciating it. The class is final, can't extend it either to add Spring annotations to the child class.
Let's say that all the methods must be transactional, for simplicity.
The bean implementation :
/** This class has no Spring dependency... */
// #Transactional <- which means I can't use this here
public final class ComplexComponentImpl implements ComplexComponent {
private SomeRepository repo;
public ComplexComponentImpl(SomeRepository repository) { this.repo = repository }
public void saveEntities(SomeEntity e1, SomeEntity e2) {
repo.save(e1);
throw new IllegalStateException("Make the transaction fail");
}
What I want to do in my configuration class (and which doesn't work in my unit test) :
#Configuration
#EnableTransactionManagement
public class ComplexComponentConfig {
#Bean
#Transactional // <- Make the bean transactional here
public ComplexComponent complexComponent() {
return new ComplexComponentImpl(repository());
}
// ...
}
The example above doesn't work, indeed, as nothing gets "transactional" at runtime : entity e1 is persisted even though the exception is thrown.
Note that my transaction management setup works works perfectly well with an implementation class marked with #Transactional.
Question : Is is it possible to declare #Beans transactional from a #Configuration class, or is there any alternative taking into accounts the constraints above ?
Found something built-in that is the sum of #Mecon's and #Erik Gillespie's answers, with limited boilerplate.
Spring already provides a TransactionProxyFactoryBean that just sets up a transactional proxy on any object. Much of the setup could be refactored to some utility method :
#Configuration
#EnableTransactionManagement
public class ComplexComponentConfig {
/** NOT A #Bean, this object will be wrapped with a transactional proxy */
public ComplexComponent complexComponentImpl() {
return new ComplexComponentImpl(repository());
}
#Bean
public ComplexComponent complexComponent() {
TransactionProxyFactoryBean proxy = new TransactionProxyFactoryBean();
// Inject transaction manager here
proxy.setTransactionManager(txManager());
// Define wich object instance is to be proxied (your bean)
proxy.setTarget(complexComponentImpl());
// Programmatically setup transaction attributes
Properties transactionAttributes = new Properties();
transactionAttributes.put("*", "PROPAGATION_REQUIRED");
proxy.setTransactionAttributes(transactionAttributes);
// Finish FactoryBean setup
proxy.afterPropertiesSet();
return (ComplexComponent) proxy.getObject;
}
// ...
}
I think you probably can't use #Transactional in that manner. One of spring's in-built PostProcessors, are supposed to scan all classes (beans) that have that annotation, and the create Aspects accordingly.
About alternatives: I would write an Adapter class for each 3rd party class I have to use. And then make those Adapter classes be Spring Beans.
You can't use #Transactional in that way but you can programmatically configure aspects with Spring.
Spring documentation for programmatically defining aspects:
http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-aspectj-programmatic
http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/aop-api.html
The examples in the documentation are very simple. Defining transaction aspects will likely be more complicated and I wouldn't be surprised if you find it easier to just use the convenience of XML-based proxies or take #Mecon's advice and write adapters.
You can use spring's AOP capabilities to add the transaction interceptor to your bean. Just create an Advisor bean that specifies a pointcut and adds a TranscationInterceptor.
#Bean
public ComplexComponent complexComponentImpl() {
return new ComplexComponentImpl(repository());
}
#Bean
public Advisor advisorBean(TransactionManager txManager) {
Class<?> targetClass = ComplexComponent .class;
int propagationBehavior = TransactionDefinition.PROPAGATION_REQUIRED;
return allMethodsTxAdvice(txManager, targetClass, propagationBehavior);
}
/**
* Extracted method for reuse.
*/
private DefaultPointcutAdvisor allMethodsTxAdvice(TransactionManager txManager, Class<?> targetClass, int propagationBehavior) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
String pointcutExpression = MessageFormat.format("execution(* {0}.*(..)))", targetClass.getName());
pointcut.setExpression(pointcutExpression);
MatchAlwaysTransactionAttributeSource tas = new MatchAlwaysTransactionAttributeSource();
TransactionAttribute transactionAttribute = new DefaultTransactionAttribute(propagationBehavior);
tas.setTransactionAttribute(transactionAttribute);
TransactionInterceptor transactionInterceptor = new TransactionInterceptor(txManager, tas);
return new DefaultPointcutAdvisor(pointcut, transactionInterceptor);
}
PS: You don't need to call afterPropertiesSet just return the FactoryBean and spring will handle all lifecycle callbacks. E.g.
#Bean
public FactoryBean<Object> complexComponent(TransactionManager tx) {
TransactionProxyFactoryBean proxyFactory = new TransactionProxyFactoryBean();
proxyFactory.setTransactionManager(tx);
proxyFactory.setTarget(complexComponentImpl());
Properties transactionAttributes = new Properties();
transactionAttributes.put("*", "PROPAGATION_REQUIRED");
proxyFactory.setTransactionAttributes(transactionAttributes);
return proxyFactory;
}