set value to Cacheable annotation from property file - java

I'm using Spring Cacheable annotation and at the moment I'm struggling with a way of adding the cache name from property file.
I tried:
#Cacheable("${some.cache.name}")
and
#Cacheable("#{'${some.cache.name}'}")

There is a SPI to do that that is much more powerful than just using SpEL. You can implement CacheResolver and resolve cache instance(s) at runtime. You could use the annotated type or any name that is provided via the annotation.
You can specify the CacheResolver per annotation, at class-level using #CacheConfig or globally by implementing CacheConfigurer.
Check the documentation for more details

Related

Custom CacheResolver for Redis Repository

When using spring-data-redis caching annotations on a spring-data-jpa repository you can provide a #CacheConfig annotation with the cacheResolver property referencing a custom bean that implements CacheResolver. In this bean you can then amend the cache name that may have been specified in the #Cachable annotation. For instance we use this custom resolver to append the tenant name to the cache in our multi-tenant setup.
We now want to use Redis as a datastore in its own right, not just a cache. When specifying a POJO you want to be stored in Redis you are able to use the spring-data-redis #RedisHash annotation and provide a keyspace as the value property of that annotation. However, this is a constant value and does not seem to be able to be configured. It looks like the next version of spring-data-redis will allow SpEL expressions in the #RedisHash's value property, but it is not clear if that will provide all the functionality we need.
Is there a CacheResolver type class that can be configured for #RedisHashs to allow for custom key space resolving?

Using EmbeddedValueResolver with #Conditional

Problem context: in our application we use custom PropertySourcesPlaceholderConfigurer to extend possibilities of EmbeddedValueResolver with custom property sources. I am very inspired of Spring Boot #ConditionalOnProperty and #ConditionalOnExpression and want to use it with custom placeholders.
What I wanted to do was to write custom #Conditional annotation that uses EmbeddedValueResolver to resolve placeholders (${...}). I noticed that ConfigurationClassPostProcessor implements ResourceLoaderAware interface, and ResourceLoaderAware classes are configured later than EmbeddedValueResolverAware ones. So when we initialize ResourceLoaderAware, our EmbeddedValueResolverAware beans are already configured. So potentially solution seems possible.
What I wanted to do next was to write custom Condition that uses EmbeddedValueResolver from ApplicationContext to resolve some property and match it to some value. But the problem is that Condition uses some another context class called ConditionContext which doesn't include EmbeddedValueResolver. Using Environment#resolvePlaceholder doesn't help - seems like Environment doesn't know anything about placeholders that are available through EmbeddedValueResolver.
So, is that possible to use EmbeddedValueResolver with Spring conditionals?

Order of Configuration in SpringBoot

I am trying to understand how beans that we make using #Configuration tends to override the beans that are generated by SpringBoot by default. I have been working on a project where in many cases we create beans for things like ZuulConfigs and the assumption is, whatever we are making shall take precedence over the default generated bean. I have been trying to figure this out but can't. Basically,
Is Spring achieving this via some custom class loader
If not how is this precedence working. Can I give some precedence in similar manner to my beans
Can I generate similar hierarchy in my project,if so how
The help is highly appreciated
Spring AutoConfiguration is used to provide a basic configuration if certain classes are in the classpath or not.
If you want to configure the order in which beans are instantiated by spring you can use
#DependsOn("A")
public class B {
...
}
This would create bean "A", then "B". Hence you can order the configuration depending upon the beans need first to be done. Anyways Spring automatically detects the dependencies by analyzing the bean classes.
for more help check this question
Spring Boot AutoConfiguration Order
Alternative :
There is also "#AutoConfigureOrder" annotation(where you can prioritise the configuration), you can have a look in the code for deeper understanding.
Documentation of AutoConfiguration is here
First of all, class loading and bean creation are two different things. We don't need to create a bean to load a class, however, a class has to be loaded in order to create a bean.
Now, coming back to Spring's example, Spring looks into all the packages configured by #componentScan and creates beans of all the classes annotated with #Bean, #Configuration and/or #Component. Spring's container keeps track of all the beans created and hence, when it encounters user defined bean with same name and class type as default bean, it replaces the original definition with user defined one (e.g. we can create our custom #ObjectMapper to override Spring boot's own instance). You can also use #Primary annotation to make you bean take precedence if another definition with same class exists (documentation here).
Below are the answers for your questions:
Spring uses reflection to load the classes and create instances. Although you can load the classes with your custom class loader (more on that here), you don't need to worry about it for #Configuration.
Yes, you can use #Primary annotation to give your bean a precedence. You can also use #Order(here) to define the creation order for your beans.
With #Primary, #Order and #Qualifier annotation you can define your own hierarchy for bean creation.
Can I give some precedence in similar manner to my beans
Yes.
A) To define a specific order your Configuration classes will be handled (by the way, a Configuration class does not have to be annotated with #Configuration (so-called full definition), but it's enough to be annotated with #Component, #ComponentScan, #Import, #ImportResource or just have a method annotated with #Bean - so-called lite definition), you should
1) add your Configuration Candidates to your SpringApplication's primarySource, for example, in your main method like that
SpringApplication.run(
new Class[]{YourSpringBootApplication.class, Config1.class, Config2.class, ...},
args);
2) and annotate each of your Configuration Candidates with #Order annotation, any other ordering means like Ordered interface, #DependsOn etc will be ignored by ConfigurationClassPostProcessor, the order in the primarySource array will also be ignored.
Then ConfigurationClassPostProcessor will sort your Configuration Candidates and handle them according the #Order annotation value you specified.
B) The precedence can also be achieved by defining your own AutoConfiguration classes. Although both Configuration and AutoConfiguration are handled by the same ConfigurationClassPostProcessor, they are essentially distinctive machineries. To do so
1) define in your classpath /META-INF/spring.factories file and put in the EnableAutoConfiguration section of it your AutoConfiguration classes like that
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
your.package.AutoConfig1,your.package.AutoConfig2
2) and annotate your AutoConfiguration classes with #AutoConfigureOrder, #AutoConfigureAfter, or #AutoConfigureAfter annotations, any other ordering means again will be ignored.
Like #Strelok pointed out, AutoConfiguration classes, your own and provided e.g. by spring-boot-autoconfigure library alike, will be added to the end of the list of Configuration Candidates.
Remember, however, that the order the Configuration Candidates will be handled by ConfigurationClassPostProcessor does not necessarily coincide with the order the beans defined by the Configuration classes will be created. For example, you might define your Configuration class that overrides TomcatServletWebServerFactory to make your own customization of Tomcat web server like
#Configuration
public class EmbeddedTomcatConfig {
#Bean
public TomcatServletWebServerFactory containerFactory() {
...
return customizedTomcatWebServerFactory;
}
but this method will be called right at the moment when your Spring Boot application decides to create a Web server, regardless of how you defined the precedence for your EmbeddedTomcatConfig Configuration class.
Is Spring achieving this via some custom class loader
There is no need to. Although you could, as always with Spring, define your own ClassLoader for BeanFactory, standard ClassLoader is good enough if everything you need for Configuration in your application is available in the classpath. Please notice, that at first phase ConfigurationClassPostProcessor does not load (i.e. does not resolve) the Configuration candidates classes (otherwise, most of the classes in spring-boot-autoconfigure library will fail to load). Instead it analyzes their annotations with bytecode analyzer, ASM by default. For that purpose, it is just enough to get a binary form, a byte array, of a class to feed it to bytecode analyzer.
Just know this: Spring Boot (specifically) auto configuration classes are always configured last. After all user beans have been created. Spring Boot auto configuration classes almost always use the #ConditionalXXXX annotations to make sure that any beans of the same type/name and other conditions that are configured in your application will take precedence over the Spring Boot auto-configured beans.
If you want your #Component to take precedence over other #Component while scanning all the components by spring, use #Order(Ordered.LOWEST_PRECEDENCE) i.e. the max value to load your component over other.
#Primary is used to give your bean a default preference, we can override the default preference using #Qualifier

How to use a Spring #Value from properties to set an annotation attribute

I'm trying to set an attribute in an annotation, using Spring #Value, but I get Type mismatch: cannot convert from Value to String. Here is what I tried:
#Table(name = "myTable", catalog = #Value("${database.myCatalog}") )
Is it possible? And if yes, how to do it?
I think you are a little bit confused with how Spring uses that annotation.
As far as I know, the only way that annotation can only be set at field or method/constructor parameters.
Also, for Spring to resolve it, the POJO must be a Spring managed bean. That means that it must be defined in the Spring (Web)ApplicationContext implementation to be resolved.
Your question seems like you are annotating a JPA Entity which is not a Spring bean but a Class to be used by the JPA implementation that you are using (e.g. Hibernate).

Can't I use annotation to indicate a bean is a primary bean

We know in Spring, <bean> has an attribute "primary" to indicate a bean is the first candidate if there are multiple beans are available to be autowired to a property.
But now all my bean definition are declared using #Component/#Service, etc, I can't find the corresponding "primary" attribute I can use to declare a bean.
Please advise how can I achieve this, thanks.
In Spring 3.0, you use #Primary.
Indicates that a bean should be given
preference when multiple candidates
are qualified to autowire a
single-valued dependency. If exactly
one 'primary' bean exists among the
candidates, it will be the autowired
value.
May be used on any class directly or
indirectly annotated with Component or
on methods annotated with Bean.
Using Primary at the class level has
no effect unless component-scanning is
being used. If a Primary-annotated
class is declared via XML, Primary
annotation metadata is ignored, and
<bean primary="true|false"/> is
respected instead.
See ref docs.
The #Primary annotation will only work if you are using Spring 3.0.
In Spring 2.5 there's no equivalent annotation for the primary attribute. You have to use the #Qualifier annotation to specify which bean you want to inject. Another option is to define your own qualifier annotation for the same purpose.
See the docs for more information.

Categories

Resources