SpEL in #Qualifier refer to same bean - spring

I am interested to inject a bean reference, which is resolved based on another property on the same bean:
#Autowired
#Qualifier("#{'prefix' + actualQualifier}")
private OtherBean otherBean
private String actualQualifier;
This would ensure that the relationship between "actualQualifier" and "otherBean" is correct.
There is a number of beans configured of the type OtherBean.
I can make sure that "actualQualifier" has a value set before autowiring/injection begins.
I am unable to find any way to reference another property value (in the JavaBean sense) on the same bean that is currently being autowired.

AFAIK, this will not work. SpEL has no access to variables of the enclosing class. And anyway, it looks like #Qualifier does not process SpEL expressions.
I did some tests and never found how to use a SpEL expression as a #Qualifier value. This page from Spring forums (and the error messages from Spring) let me think that in fact #Qualifier only takes a String and does not try to evaluate a SpEL expression.
My conclusion is that way will lead you in a dead end.
As suggested in this other answer, I think you'd better use a selector bean and set otherBean in an init method :
#Bean(initMethod="init")
class MyBean {
...
#Autowired
private BeanSelector beanSelector;
private OtherBean otherBean
private String actualQualifier;
public void init() {
otherBean = beanSelector(actualQualifier);
}
...
}
and put all intelligence about the choice of otherBean in beanSelector.

Related

What is best way to read properties from application.properties?

So, I have created configuration class with #Component and #ConfigurationProperties(prefix = "properties"), set default values for some of my application properties and changed some of them in application.yaml/properties
Now, I know I can access it using #Value("properties.*") but it can lead to having many variables which will be repetitive in another classes too
#Value("${properties.user-id-length}")
private int userIdLength;
I also can access my configuration class (as it is Spring Bean) through #Autowire it to variable in every single class I need make use of it. The cons for that is that more complex configuration class containing inner classes, which contain inner classes etc. will not look too great in code
#Autowired // Not recommended, but for simplicity
private MyConfigurationClass myConfigurationClass;
// some method
int userIdLength = myConfigurationClass.getUserIdLength();
String serverLocation = myConfigurationClass.getAmazon().getSes().getSenderEmailAddress()
Another way is to create additional helper class like Constant and set needed static fields with #Value but it can be time consuming and I'm not sure it is THAT different from first solution
public static int USER_ID_LENGTH;
#Value("${properties.user-id-length}")
private void setUserIdLength(int length){
Constant.USER_ID_LENGTH = length;
}
So, which aproach is the best? Or are there another ways to do that?
Well, not much of the feedback but in the meantime I figured out that using both #Value and #ConfigurationProperties leads to some problems.
MyProp.class
#Getter
#Setter
#ConfigurationProperties(prefix = "prop")
public class MyProp{
private String default = "Default String"
}
SomeClass.class
#Component
public class SomeClass.class{
#Value("${prop.default}")
public String message;
}
Above example causes Exception BeanCreationException
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'someClass': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'prop.default' in value "${prop.default}"
Explanation to this can be found here in #davidxxx's post and occurs unless we set all fields in application.properties. Because of that my first proposal cannot be done in the terms I thought it can be done and explanation was not easy to find, so I hope it will help someone one day.

Spring return dynamic instance based of String value

Java Spring question:
I have a interface MyInterface with one method
void exec (String str);
I have many implementation of MyInterface, say Oneimpl, anotherimpl yetanotherimpl...and so on and can keep adding new implementations.
how do I obtain an instance of a specific implementation using just the name of the implementing class passed as a STRING value , say "someRandomImpl"
The code should be dynamic and can provide a instance of new implementations without code change.
implements ApplicationContextAware
it will autowired ApplicationContext object
use the object like
context.getBean(beanName)
then you get the bean

Spring constructor injection

Is it possible to construct an object manually and let some other arguments be injected by Spring?
e.g.
class A
#Autowired
private SomeDao dao;
A(String x, String y) {}
Your example is using field injection, not constructor injection.
The best way is generally to use JavaConfig. Your #Bean methods can take parameters (which Spring will autowire), which you can combine with your other options when you call new.
Maybe org.springframework.web.context.support.SpringBeanAutowiringSupport class it's what you are looking for, try to invoke:
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
You can use autowireBean from the AutowireCapableBeanFactory. Given your applicationContext, you call getAutowireCapableBeanFactory() and then autowire your instance:
applicationContext.getAutowireCapableBeanFactory().autowireBean( new A("x", "y" ) );

Spring: Using #Qualifier with Property Placeholder

I am trying to use a property placeholder as the attribute for #Qualifier, as follows:
#Autowired
#Qualifier("${beanName}")
private MyBean myBean;
However, this does not work, even though the property placeholder is available as a String value:
#Value("${beanName}")
private String beanName;
What would be a good workaround here, thanks very much.
I encountered exactly same issue. Just use Resource
#Resource(name="${beanName}")
private MyBean myBean;
I can't leave a comment so this is my answer:
As Adam B said maybe you can use spring profiles to achieve the result you are aiming to (wich result are you aiming?).
Another thing you can do is:
configure a map (using the spring util namespace) in your xml context configuration like this:
<util:map id="mapId" key-type="java.lang.String" value-type="com.xxx.interface-or-superclass">
<beans:entry key="${property.bean.name.1}" value-ref="bean1-defined-elsewehere"/>
<beans:entry key="${property.bean.name.2}" value-ref="bean2-defined-elsewehere"/>
<beans:entry key="${property.bean.name.3}" value-ref="bean3-defined-elsewehere"/>
</util:map>
then you can load this map in a bean called eg. "com.xxx.BeanSelector"
#Value("#{mapId}")
private Map<String, com.xxx.interface-or-superclass> myMap;
and add to this bean a methos like this:
public interface-or-superclass getBean(String beanName){
return myMap.get(beanName);
}
ok now you can have your final class similar to this:
#Autowired
private BeanSelector beanSelector;
#Value("${property.name.the.bean.you.want.to.use}")
private String beanName;
private interface-or-superclass myBean;
then you can istantiate myBean (maybe inside the method afterPropertiesSet() if you are implementing the interface InitializingBean)
in this way:
myBean = beanSelector.getBean(beanName);
// then check ifthe bean is not null or something like that
Ok it's a little messy and maybe you can act in a different way based on what you want achieve, but it's a workaround.
Just a try (don't really known the problem to solve) . You can use fixed bean name as usual (autowire with placeholder is not supported) but you can load different bean implementation from different xml based on property value.
.
Else think about a solution based on bean alias.
My 2 cents

How do I get a property value from an ApplicationContext object? (not using an annotation)

If I have:
#Autowired private ApplicationContext ctx;
I can get beans and resources by using one of the the getBean methods. However, I can't figure out how to get property values.
Obviously, I can create a new bean which has an #Value property like:
private #Value("${someProp}") String somePropValue;
What method do I call on the ApplicationContext object to get that value without autowiring a bean?
I usually use the #Value, but there is a situation where the SPeL expression needs to be dynamic, so I can't just use an annotation.
In the case where SPeL expression needs to be dynamic, get the property value manually:
somePropValue = ctx.getEnvironment().getProperty("someProp");
If you are stuck on Spring pre 3.1, you can use
somePropValue = ctx.getBeanFactory().resolveEmbeddedValue("${someProp}");
Assuming that the ${someProp} property comes from a PropertyPlaceHolderConfigurer, that makes things difficult. The PropertyPlaceholderConfigurer is a BeanFactoryPostProcessor and as such only available at container startup time. So the properties are not available to a bean at runtime.
A solution would be to create some sort of a value holder bean that you initialize with the property / properties you need.
#Component
public class PropertyHolder{
#Value("${props.foo}") private String foo;
#Value("${props.bar}") private String bar;
// + getter methods
}
Now inject this PropertyHolder wherever you need the properties and access the properties through the getter methods

Resources