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.
Related
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
I have used the following ways to get the values from the properties. But I would like to know which one of these is the best to use to follow the coding standard? Also, are there any other ways that we can get the values from the properties file in Spring?
PropertySourcesPlaceholderConfigurer
getEnvironment() from the Spring's Application Context
Spring EL #Value
Along with the other configuration classes (ApplicationConfiguration etc.) I create a class with the annotation #Service and here I have the following fields to access the properties in my file:
#Service
public class Properties (){
#Value("${com.something.user.property}")
private String property;
public String getProperty (){ return this.property; }
}
Then I can autowire the class and get the properties from my properties file
The answer is,
it depends.
If the properties are configuration values,
then configure a propertyConfigurer
(below is an example for a Spring xml configuration file).
<bean id="propertyConfigurer"
class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="ignoreResourceNotFound" value="true" />
<property name="locations">
<list>
<value>classpath:configuration.properties</value>
<value>classpath:configuration.overrides.properties</value>
</list>
</property>
</bean>
When configured this way,
the properties from the last file found override those found earler
(in the locations list).
This allows you to ship the standard configuration.properties file bundled in the war file and store a configuration.overrides.properties at each installation location to account for installation system differences.
Once you have a propertyConfigurer,
annotate your classes using the #Value annotation.
Here is an example:
#Value("${some.configuration.value}")
private String someConfigurationValue;
It is not required to cluster the configuration values into one class,
but doing so makes it easier to find where the values are used.
#Value will be the simple and easy way to use, as it will inject value from property file to your field.
Both the older PropertyPlaceholderConfigurer and the new PropertySourcesPlaceholderConfigurer added in Spring 3.1 resolve ${…} placeholders within bean definition property values and #Value annotations.
unlike getEnvironment
using property-placeholder will not expose the properties to the
Spring Environment – this means that retrieving the value like this
will not work – it will return null
when you are using <context:property-placeholder location="classpath:foo.properties" /> and you use env.getProperty(key); it will always return null.
see this post for the problem using getEnvironment : Expose <property-placeholder> properties to the Spring Environment
Moreover, in Spring Boot you can use #ConfigurationProperties to define your own properties with hierarchical and type-safe in application.properties. and you don't need to put #Value for every field.
#ConfigurationProperties(prefix = "database")
public class Database {
String url;
String username;
String password;
// standard getters and setters
}
in application.properties:
database.url=jdbc:postgresql:/localhost:5432/instance
database.username=foo
database.password=bar
Quote from : properties with spring
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>
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.
I am having difficulty understanding why something in Spring Java Config using #Autowired does not work.
First, I am trying to move all my #Autowired annotations in the Java Config classes. This has the effect of making my "POJOs" back into real POJOs. I can then not only test them easily outside of a Spring context, but can also use mock objects easily and readily.
So I first tried this:
#Configuration
public class Module3ConfigClass {
#Autowired
private Module1Bean1 module1Bean1;
#Autowired
private Module2Bean1 module2Bean1;
#Bean
public Module3Bean1 module3Bean1() {
return new Module3Bean1(module1Bean1, module2Bean1);
}
}
However, when the Module3Bean1 constructor is invoked, both passed in Beans are null. If you didn't follow my made up naming convention above, both of those beans would be created by a separate Java Config configuration file. Also note that everything is wired up correctly - I know this because everything works perfectly when the #Autowired tags are on the corresponding private member fields inside of Module3Bean1.
FWIW, I tried adding an #DependsOn annotation to module3Bean1() method, but had the same results. I guess I just would really like to understand this behavior, is it correct (I suspect it is, but why)?
Finally, I found an acceptable workaround shown here:
#Configuration
public class Module3ConfigClass {
#Bean
#Autowired
public Module3Bean1 module3Bean1(Module1Bean1 module1Bean1, Module2Bean1 module2Bean1) {
return new Module3Bean1(module1Bean1, module2Bean1);
}
}
This seems fine to me, but if anyone would care to comment on it, that would be welcome as well.
I think you came across same problem I just had. In my case problem was invalid xml configuration. In my module B I had config like :
<beans>
<context:component-scan base-package="com.moduleB"/>
<import resource="classpath:applicationContext-moduleA.xml"/>
</beans>
In moduleA context I placed "context:annotation-config" annotation.
When I change import/context order to :
<beans>
<import resource="classpath:applicationContext-moduleA.xml"/>
<context:component-scan base-package="com.moduleB"/>
</beans>
Autowiring for configuration class properties started to work.
We had the same issue and came to the conclusion that the error arose because we had a circular dependency where a BeanPostProcessor was involved.
A PropertyPlaceholderConfigurer (a BeanPostProcessor) has been configured to set its propertiesArray property with the help of another bean:
<bean id="globalPropertyPlaceholderConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
lazy-init="false" depends-on="javaLoggingConfigurer">
<property name="locations">
<list>
<value>classpath:config/host/${env.instance}.properties</value>
<value>WEB-INF/config/host/${env.instance}.properties</value>
</list>
</property>
<property name="ignoreResourceNotFound" value="true" />
<property name="propertiesArray" value="#{springPropertyFinder.findProperties()}" />
</bean>
The used springPropertyFinder bean to set the propertiesArray is not a BeanPostProcessor but a "normal" bean that gathers all Properties instances with:
public Properties[] findProperties() {
Map<String, Properties> propertiesMap = applicationContext.getBeansOfType(Properties.class);
for (String title : propertiesMap.keySet()) {
PropertiesLoggerUtil.logPropertiesContent(logger, "Springcontext Properties ("+title+")", propertiesMap.get(title));
}
return propertiesMap.values().toArray(new Properties[propertiesMap.size()]);
}
The #Configuration class contained a bean of type Properties
So our assumption is that the #Configuration class has been created without being processed by the ConfigurationClassPostProcessor (also a BeanPostProcessor), because the PropertyPlaceholderConfigurer depends on the springPropertyFinder, which depends on the properties bean in the #Configuration class. The order of the BeanPostProcessors is probably not setup right under these circumstances.
This described setup worked in XML, but not with Java config.