#Cacheble annotation on no parameter method - spring

I want to have #Cacheable annotation on method with no parameter. In that case, I use #Cacheable as follows
#Cacheable(value="usercache", key = "mykey")
public string sayHello(){
return "test"
}
However, when I call this method, it doesn't get executed and it get exception as below
org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 0): Property or field 'mykey' cannot be found on object of type 'org.springframework.cache.interceptor.CacheExpressionRootObject' - maybe not public?
Please suggest.

It seems that Spring doesn't allow you to provide a static text for the cache key in the SPEL, and it doesn't include as default the name of the method on the key, so, you could be in a situation when two methods using the same cacheName and without a key would potentially cache different results with the same key.
The easiest workaround is to provide the name of the method as the key:
#Cacheable(value="usercache", key = "#root.methodName")
public string sayHello(){
return "test"
}
This would set sayHello as the key.
If you really need a static key, you should define a static variable in the class, and use #root.target:
public static final String MY_KEY = "mykey";
#Cacheable(value="usercache", key = "#root.target.MY_KEY")
public string sayHello(){
return "test"
}
You can find here the list of SPEL expressions that you can use in your key.

Try adding single quotes around mykey. It's a SPEL expression, and the singles quotes make it a String again.
#Cacheable(value="usercache", key = "'mykey'")

You can omit the key parameter. Spring will then put the value with key SimpleKey.EMPTY into the cache:
#Cacheable("usercache")
Alternatively (apart from using SPEL outlined in the other solutions) you can always inject the CacheManager and manually handle it.

Add # in the key
#Cacheable(value="usercache", key = "#mykey")
public string sayHello(){
return "test"
}

Related

How can I get value from application.properties using SpelExpressionParser?

I tried to pass "${<some.property>}" as key parameter, but got: "After parsing a valid expression, there is still more data in the expression: 'lcurly({)'".
Here's the function:
fun parse(key: String): String {
val context = StandardEvaluationContex()
context.addPropertyAccessor(EnvironmentAccessor())
return SpelExpressionParser().parseExpression(key).getValue(context)
}
You can use the #Value annotation and access the property in whichever Spring bean you're using
#Value("${propertyName}")
private String propertyName;

Does #Value support letting default value reference another #Value?

#Value("${test.v1:hello}")
private String testV1;
#Value("${test.v2:${test.v1}}")
private String testV2;
When test.v2 is not configured, i want the testV2 is the same with testV1. Does #Value support letting default value reference another #Value?
I think the best thing you can do is to define test.v2=${test.v1} in your application.properties file. This way, if you override the value for test.v2 through some other ways like environment variables, it will have the defined value, otherwise, it will have the test.v1 value.
As an alternative, you can use the "+" operand.
#Value("${test.v1:hello}")
private String testV1;
#Value("${test.v2:}" + "${test.v1}")
private String testV2;
#PostConstruct
public void postConstruct() {
if (StringUtils.isEmpty(testV2) {
testV2 = testV1;
}
}

Using #Value annotation with Spring and SPeL

I am trying to find a way to do the following in my spring boot 1.5 application.
I have a variable who's value is dynamic meaning it comes in from an external system.
String name = "abc"; //gets set externally
I want to try and use the name's value to lookup my property file and see if there is a matching property defined. something like..
#Value("#{myClassName.name.concat('something')}")
String propertyValue;
Now my application.property file has the following property set
assume name has the value "abc"
property file contents:
abc.something:abcValue
Now, when i try to access the value of the variable propertyValue it gets set to the value abc.something and not abcValue.
I probably think I cannot use #Value with #{} to get to that, I was wondering if there was a way to to use #{} inside ${} so that I goes and fetches the property value after calculating the name of the property using #{}.
Let me know if you need more details please.
A bean life-cycle requires properties to be resolved at compile time. So, #Value requires constant parameter.
You can use Environment bean to access your properties programmatically.
import org.springframework.core.env.Environment;
#Service
public class Serivce {
#Autowired
private Environment environment;
public String getProperty(final String keyPart) {
String key = "build.your." + keyPart;
return environment.getProperty(key)
}
}
By the way you can use #('${spring.some.property}') in SpEL to access placeholder.
// This is valid access to property
#Value("#('${spring.some.property}')")
private String property;

Can I use a placeholder to dynamically add text to a string imported from a property file with Spring's #Value annotation

Does anyone know if I can use placeholders with Spring's #Value annotion?
For example:
#Value("${a.url.from.propertiesFile}")
private void setUrl(String myUrlFromProperties)
{
this.url = myUrlFromProperties;
}
where my properties file would have:
a.url.from.propertiesFile=/firstPartOfUrl{dynamicBitToAddTo}restOfUrl
Yes you can do that.
In this case if you have passed a null value in the parameter then the default value will be taken as defined with the annotation #Value.
But if you are passing a not null value in the parameter then it will not take the default value.
Hope this helps you.

spring security : Why can't we access Hibernate entitiy parameters in #PreAuthorize?

I have the following interface method on which I am applying #PreAuthorize :
#PreAuthorize("doSomething(#user.id)")
void something(User user, List<User> accessList);
where User is a Hibernate entity object. It gives me an error :
org.springframework.expression.spel.SpelEvaluationException:
EL1007E:(pos 13): Field or property 'id' cannot be found on null at
org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:204)
There is no way that the user parameter is null, as if I remove the annotation, and inspect the value of user in the method that implements this interface method, there is a valid User object present there. Additionally, just before calling this method, I have made sure that the user object is correctly constructed.
I really can't figure out why would the user field be considered null by the SPEL parser
You can check with the debugger what's going on in MethodSecurityEvaluationContext, inside Object lookupVariable(String name) method:
#Override
public Object lookupVariable(String name) {
Object variable = super.lookupVariable(name);
if (variable != null) {
return variable;
}
if (!argumentsAdded) {
addArgumentsAsVariables();
argumentsAdded = true;
}
and so you can see what's really going on in the addArgumentsAsVariables() method as the convertion of method arguments to SPEL variables is implemented very clearly in Spring.
Spring Security has a better answer for this problem now:
http://docs.spring.io/spring-security/site/docs/3.2.5.RELEASE/reference/htmlsingle/#access-control-using-preauthorize-and-postauthorize
Basically, you can use the #P annotation or #Param annotation if you are using < JDK 8.
You can check LazyParamAwareEvaluationContext,inside loadArgsAsVariables() method, version 3.1.0.
The same key for different Entity, because of implementing interface.
I need to add something to this as the title indicates that we cannot access hibernate properties.
There are two editions of hasPermission, the loaded object and the serialized object. Here is some code from a test case:
#PreAuthorize("isAuthenticated() and hasPermission(#organization, 'edit')")
public long protectedMethod(Organization organization)
{
return organization.getId();
}
And for the latter here we see that we can infact access the id proprty of the organization (which is a hibernate entity):
#PreAuthorize("isAuthenticated() and hasPermission(#organization.getId(), 'organization', 'edit')")
public long protectedMethodSerializableEdtion(Organization organization)
{
return organization.getId();
}

Resources