How to inject bean into BeanDefinitionParser? - java

I have a parser.
class MyAbstractBeanDefinitionParser extends AbstractBeanDefinitionParser {
#Autowired
private String playerName;
// getters/setters
}
I want to inject into the parser a String (playerName). To do that I create a bean and wire it to the context.
ConfigurableListableBeanFactory beanFactory = ctx.getBeanFactory();
beanFactory.registerSingleton("playerName", name);
When I create a GenericXmlApplicationContext it executes BeanDefinitionParser method 'parseInternal' but I don't see my dependency. Here is my question: how can I inject a playerName to MyAbstractBeanDefinitionParser?

If I understand correctly, you're trying to inject a String value to playerName field. This can be easily done using the #Value annotation.
Like this:
class MyAbstractBeanDefinitionParser extends AbstractBeanDefinitionParser {
#Value("The value String you want to inject")
private String playerName;
}
In this example the playerName field will hold the value of:
The value String you want to inject
The only configuration you'll have to do, in order that the above will work, just add to your Bean XML Configuration file the line:
<context:annotation-config/>

This is strictly using the Spring XML bean configuration thus eliminating the bean creation in the java code; Once your context is loaded spring will create the bean which is below:
<bean name = "MyAbstractBeanDefinitionParser" class="x.x.package.MyAbstractBeanDefinitionParser">
<property name="playerName" value="YourString">
</property>
</bean>

Related

from Spring context to Spring annotation

I'm moving my project package from Spring xml file configuration to class annotation configuration.
Im stuck with a bean instantiation failed on a bean defined in a another context xml file.
This is the definition :
<bean id="mglsChecker" class="DefaultMglsAdapter" destroy-method="close">
<constructor-arg value="${mgls.server.address}"/>
<constructor-arg value="${mgls.fname}"/>
<constructor-arg value="${mgls.lcount}"/>
</bean>
the mglsChecker class is defined in an infrastucture package common to the entire "solution".
The issue is that the variables "${}" are not defined so now this class is not instantiated.
I dont understand how it works when my project is xml file configured.
In the original applicationContext.xml I dont see any references to this mglsChecker context file.
Any help where should i look into ? what am i missing ?
thanks,
You can use
#Configuration
class YourConfig {
// you usually don't need to explicitly give the bean name
// if you don't, Spring gives it the config's method name
#Bean(name = "mglsChecker", destroyMethod = "close")
MglsAdapter mglsChecker(#Value("${mgls.server.address}") String address,
#Value("${mgls.fname}") String fname,
#Value("${mgls.lcount}") long lcount) {
return new DefaultMglsAdapter(address, fname, lcount);
}
}
Personally, I prefer creating #Component classes, but for that you need to be able to edit the DefaultMglsAdapter class.
#Component
class DefaultMglsAdapter implements MglsAdapter {
// fields to hold the configs
DefaultMglsAdapter(#Value("${mgls.server.address}") String address,
#Value("${mgls.fname}") String fname,
#Value("${mgls.lcount}") long lcount) {
// set fields
}
#PreDestroy
void close() {
// cleanup
}
}
EDIT: incorporated Andreas' correction :)
Load the properties in the java file via
#Configuration
#PropertySource("classpath:foo.properties")
public class DefaultMglsAdapter{
//...
}
Inject the properties via
#Value( "${mgls.server.address}" )
private String serverAddress;
The variables which are mentioned with "${}" syntax are key/place-holders of properties.
Please search or find such key from *.properties or *.config or *.xml or any such custom properties files. If you find any such properties file then specify classpath or location of that file where you want to configure it as given below:
By XML:
<context:property-placeholder location="classpath:path/to/PropertiesFile"/>
By Annotation:
#Configuration
#PropertySource("classpath:path/to/PropertiesFile")
#Value("${Property}")
Thanks and Regards.

Spring Configuration xml file with two beans of the same type NoUniqueBeanDefinitionException

I am trying to set up a test applicationContext file for running integration tests on a project that I am working on.
I have two classes that have fields that are marked as #Resource. One class I can change and one I cannot as it is imported from a different project that I don't have any permissions to change. I cannot get my configuration file to set these #Resouces fields without giving me an org.springframework.beans.factory.NoUniqueBeanDefinitionException.
Simple example:
appconfig.xml file
...Typical spring setup...
<bean id="baseUrl" class="java.lang.String">
<constructor-arg value="myURL"/>
</bean>
<bean id="supportedLang" class = "java.lang.String">
<constructor-arg value="en"/>
</bean>
Class that uses baseURL, (I have control to change, simplified Version)
#Service("myService")
public class MyService implements AnotherService{
#Resource
private String baseUrl;
public String getBaseUrl(){return baseUrl;}
public void setBaseURL(String baseURL){this.baseUrl = baseUrl;}
}
Class that uses supportedLang (I don't have access to change this class simplified version)
#Service
public class LangSupportImpl implements InitializaingBean, LangSupport{
#Resource(name= "supportedLang")
private String twoLetterSupportedLang;
public getTwoLetterSupportedLang(){return this.twoLetterSupportedLang;}
}
If I don't set up the beans in the application config file I get a no bean defined error instead.
Any help would be greatly appreciated.
Try to use #Resource(name = "baseUrl") in your MyService class. This will tell Spring which exact bean to take and will resolve ambiguity.
Another option is to change XML configuration and add primary="true" to declaration of baseUrl bean

SpringFramework: set bean name programmatically

For springframwork based application, when using xml to declare beans, bean id can be configured by passing a unique value and even a parameter and then solve the value at runtime.
Now I hope to replace all xml configuration to java annotation.
Say I want to create two database beans with different id.
bean.xml
<bean id="A.database" class="org.apache.commons.dbcp.BasicDataSource">
<!-- collaborators and configuration for this bean go here -->
</bean>
<bean id="B.database" class="org.apache.commons.dbcp.BasicDataSource">
<!-- collaborators and configuration for this bean go here -->
</bean>
Then I optimize the upper code to one bean with two property file for two application
bean.xml
<bean id="${appName.database}" class="org.apache.commons.dbcp.BasicDataSource">
<!-- collaborators and configuration for this bean go here -->
</bean>
applicationA.properties
appName.database=A.database
applicationB.properties
appName.database=B.database
The whole application is composed of "framework" module which provides beans common for each application, like database bean, jdbcTemplate bean, and "application" module which provides property value for placeholder and initializes beans with unique id. So even if I start multiple application at the same time, they will find corresponding bean from the context.
Generally speaking, I hope to do
#Bean(name = "${beanName}")
public ABean getBean() {}
and resolve ${beanName} at application level.
By reading SpringFramwork document, I found the answer: BeanNameGenerator
NameGenerator.class
public class NameGenerator implements BeanNameGenerator{
#Override
public String generateBeanName(BeanDefinition definition,
BeanDefinitionRegistry registry) {
if(definition.getBeanClassName().contains("Toto")) {
return "toto";
}
return return definition.getBeanClassName();
}
}
AppConfiguration.class
#Configuration
#ComponentScan(basePackages = {"com.example.domain"}, nameGenerator = NameGenerator.class)
public class Config {
}
Domain class with #Component
#Component
public class Toto {
private int id;
}
BootApplication with domain bean name : toto
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(
DemoApplication.class, args);
for (String name : ctx.getBeanNamesForType(Toto.class)) {
System.out.println(name);
}
}
}
If you want to follow that type of approach create multiple configuration classes you annotated with different Spring profiles.
At start up you can pass a parameter on which profile to use and hence what beans to load within the associated profile.
A more efficient way to do it is use the same property naming convention across all application .properties files. Set a parameter placeholder for the file name which resolves to a JVM arg passed at runtime which is loaded by the#PropertySource annotation.
There's no need to have duplicate beans defined for different environments if it's just properties that are changing.

Spring: configure a static field singleton instance

There is CXF's PolicyBasedWSS4JInInterceptor that creates a singleton instance:
public static final PolicyBasedWSS4JInInterceptor INSTANCE
= new PolicyBasedWSS4JInInterceptor();
Having no Spring skills I'm struggling with how to set its acestor's (AbstractWSS4JInterceptor) properties map via Spring's bean definitions in a cxf.xml file. Basically I want to configure WSS-related properties like "signaturePropFile" in cxf.xml.
Can someone show how to set the property map of PolicyBasedWSS4JInInterceptor.INSTANCE? Thanks!
I would declare a singleton bean:
<bean id="interceptor" class="whatever.your.package.PolicyBasedWSS4JInInterceptor" scope="singleton"/>
And then inject it wherever I need it
<bean id="anotherBean" ...>
<property name="interceptor" ref="interceptor"/>
</bean>
This other bean would have a normal PolicyBasedWSS4JInInterceptor property like this:
private PolicyBasedWSS4JInInterceptor interceptor;
public PolicyBasedWSS4JInInterceptor getInterceptor() {
return interceptor;
}
public void setPolicyBasedWSS4JInInterceptor(PolicyBasedWSS4JInInterceptor interceptor) {
this.interceptor = interceptor;
}
And you would get the same as declaring it static, expressed in Spring terms. It's up to you which way you prefer, just remember that doing this the Spring way you have your interceptor inside your IoC container, and thus you can instrument it if necessary, etc.
The "action"-based WS-Security properties such as "signaturePropFile" do not work with the WS-SecurityPolicy based interceptors in CXF. CXF has separate configuration tags that you can just pass as JAX-WS properties when using WS-SecurityPolicy, and so you don't need to access any properties of the INSTANCE class. See here for more information:
http://cxf.apache.org/docs/ws-securitypolicy.html
Colm.

Spring: How to make class a bean if one constructor-arg is a ref bean but another is not?

I have a class
public class MakeMeBean {
#Autowired private IAmBean var1;
private IAmNOTBean var2;
public MakeMeBean() {}
public MakeMeBean(IAmNOTBean var) {
this.var2 = var;
}
}
I want to make this class as a bean so I make a wireup.xml as
<bean id="make-me-bean" class="com.blah.blah.MakeMeBean">
<constructor-arg index="0" ref=<PUT REFERENCE BEAN HERE>
<constructor-arg index="1" <I don't want to put anything>
</bean>
Question
a.) How can I make a bean in which one instance variable is a bean and another not? I don't want to inject var2(another bean in wireup.xml)
b.) <PUT REFERENCE BEAN HERE> is a bean imported from jar file, how can I make reference to this bean in wireup.xml
You can't just have some beans in context that you created and another half that spring created (at least not that simple), if you want to manage the instances over spring, spring should have the objects on its context. Of course you have the possibility to instantiate the objects in the context, and after the instantiation you could invoke some setters to set some properties.
In order to use another to user another bean, that I suppose comes from another Spring context, the other spring context needs to be imported in the first one. In order to import a context file you can use:
<import resource="resourcePath" />

Categories