I'm using the Spring ApplicationContextInitializer to add PropertySources to the Enviroment. I have verified that that part is working as in it's loading the properties into the PropertySources. The part that I'm struggling with is figuring out how to access the Property values that were loaded in the ApplicationContextInitializer within the Spring XML configuration as well as the Java code itself.
Here is a snippit of my ApplicationContextInitializer:
public class ExternalPropertiesApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(ConfigurableApplicationContext applicationContext) {
Properties properties = load_properties_from_external_source();
PropertiesPropertySource propertySource = new PropertiesPropertySource("external", properties);
applicationContext.getEnvironment().getPropertySources().addFirst(propertySource);
}
}
So this loads up my Properties which has key,value pairs like:
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mydb
jdbc.username=user
jdbc.password=passwd
My Spring configuration file looks like this:
<context:component-scan base-package="com.example"/>
<context:property-placeholder ignore-resource-not-found="true" system-properties-mode="OVERRIDE"/>
<bean id="myds" class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
This is failing because it doesn't recognize ${jdbc.driverClassName}. I also tried #{external.jdbc.driverClassName} which fails too. The first fails because I'm not explicitly loading jdbc.properties in my Spring configuration file (which is the intent). The latter fails because it can't find the external bean. What step am I missing? How do I expose the property values that were loaded externally?
I also have a Java class like so:
package com.example;
#Component
public class MyClass {
#Value("${jdbc.url}")
private String jdbcUrl;
}
Similar to the Spring configuration, how would I access the jdbc.url property values that were loaded upon initialization?
Oh and here is a snippit of the web.xml:
<context-param>
<param-name>contextInitializerClasses</param-name>
<param-value>com.example.ExternalPropertiesApplicationContextInitializer</param-value>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
There were a couple of issues with the Spring configuration file that was the culprit.
Specifically, in the heading, I had:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
despite being on Spring 3.1. That caused Spring to revert to the old PropertyPlaceholderConfigurer rather than the new PropertySourcesPlaceholderConfigurer.
Once I changed the 3.0.xsd reference to 3.1.xsd, it started to resolve the issue. The new header looks like so:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
The second issue was this line:
<context:property-placeholder ignore-resource-not-found="true" system-properties-mode="OVERRIDE"/>
Because of the system-properties-mode attribute, Spring again reverted back to the old PropertyPlaceholderConfigurer and not the new PropertySourcesPlaceholderConfigurer.
When using the old PropertyPlaceholderConfigurer, it doesn't pick up the values from the new Environment which was why the property values weren't being picked up in the Spring Context configuration.
Related
I have a properties file in src/main/resources.
Spring 4.0 and Java 1.8 project.
mock.properties
key=test
WEB-INF/web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/mvc-dispatcher-servlet.xml</param-value>
</context-param>
WEB-INF/mvc-dispatcher-servlet.xml
<context:component-scan base-package="com.x.y.z.*">
</context:component-scan>
<context:annotation-config />
<context:spring-configured />
<context:property-placeholder location="classpath:mock.properties"
order="-1" ignore-resource-not-found="true" ignore-unresolvable="true" />
<mvc:annotation-driven />
Controller.java ... I can access the value of property key in controller.
#Controller
#RequestMapping("/xyz")
public class EC2Controller {
#Value("${key}")
String v;
}
Helper.java ... Can't access any property.Always gets "null"
#Component
public class Helper {
#Value("${key}")
String v;
}
I also tried various options suggested on forums and spring docs but helper is just not able to read the prop.I tried following as well
#Configuration
#PropertySource("classpath:mock.properties")
public class Helper {
#Value("${key}")
String v;
#Bean
public static PropertySourcesPlaceholderConfigurer
propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
and
#Configuration
#PropertySource("classpath:mock.properties")
public class Helper {
#Autowired
Environment env;
String v = env.getProperty("key");
}
nothing works.
The answer depends on whether you are using the springonfig.xml mvc-dispatcher-servlet.xml that you show above. Based on your code samples, it looks like you are not (unless there is something missing).
Your mvc-dispatcher-servlet.xml includes context:property-placeholder, so it should be sufficient. But if you are not using it, or want to use an #Configuration (Java config) class instead (you can mix xml and Java config in the same app) then you need a PropertySourcesPlaceHolderConfigurer to resolve ${} in #Value, such as:
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
See documentation at https://docs.spring.io/spring/docs/4.2.4.RELEASE/javadoc-api/org/springframework/context/annotation/PropertySource.html, which states:
In order to resolve ${...} placeholders in definitions or #Value annotations using properties from a PropertySource, one must register a PropertySourcesPlaceholderConfigurer...
Note that the documentation states this happens automatically when using context:property-placeholder in XML - but as noted it's not clear from your code samples whether you are using that.
You could try this code snippet, and use ${key} to inject value:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:/demo.properties</value>
</list>
</property>
</bean>
or this, and use #{app.key} to inject value:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/context/spring-util-3.2.xsd" profile="dev">
...
<util:properties id="app" location="app.properties" />
...
</beans>
Hope to help you.
When I access a bean from spring bean configuration file using BeanFactory like this:
public class Person {
private String id,address;
#Autowired
private Customer customer;
//setters & getters
}
and bean configuration file
<bean name="person" class="com.ram.spring.model.Person"></bean>
<bean class="com.ram.spring.model.Customer">
<property name="email" value="ram#adp.com"></property>
<property name="name" value="Ram"></property>
</bean>
here is the executor class
public class PersonExecutor {
public static void main(String[] args) {
BeanFactory context = new XmlBeanFactory(new ClassPathResource("Spring.xml"));
Person person = (Person)context.getBean("person");
System.out.println(person.getCustomer());
}
}
when I execute this, I got null.is BeanFactory not supported for annotations?? any ideas??
Approach 1: Include below code in your xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config />
<!-- Remaining bean declaration -->
</beans>
Approach 2: Remove #Autowired and inject customer in your xml file only.
<bean name="person" class="com.ram.spring.model.Person">
<property name="customer" ref="customer"></property>
</bean>
<bean name="customer" class="com.ram.spring.model.Customer">
<property name="email" value="ram#adp.com"></property>
<property name="name" value="Ram"></property>
</bean>
You have to use AnnotationConfigApplicationContext or
you have to add to yor Spring.xml to activate the annotation scan.
As #jens suggested
you should active annotation scan
<context:component-scan base-package="package_path">
</context:component-scan>
<context:annotation-config />
hope that helped
Why doesn't it work?
When using Spring with an XML context, using annotations is not activated by default. This means #Autowired, #Transactional, #PostConstruct and any other annotation you will use will simply not be exploited.
How do I fix it?
To make Spring aware of annotations, you need to add the following line:
<context:annotation-config />
Thus, Spring will search for annotations in the beans it creates and process them accordingly.
This requires activating the context namespace. At the top of your context, make sure you have all context related links and arguments1:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config />
<!-- Your context -->
</beans>
You do not need <context:component-scan /> in your case. This would be useful if you used full-annotation context (e.g. classes annotated with #Component). See the difference between <context:annotation-config /> and <context:component-scan /> in this answer.
Alternate solution
As Naman Gala suggested, you could also drop #Autowired completely and inject all dependencies in XML. See the related answer for more details.
1 This includes the xmlns:context attribute (xmlns = XML NameSpace) and two URLs in xsi:schemaLocation.
I'm still new to spring and I'm trying to get ehcache spring annotations setup correctly. I'm using Spring 3.2.3 ehCache 2.4 and ehcache-spring-annotations-1.2.
When I try to access the reference to the cacheManager, it is always null. All the jars are on the build path, ehcache.xml is in the classpath and there are no xml errors. I've tried also including the classes in the component scan and using #Resource instead of Autowired. I'm stuck!
Application context:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:ehcache="http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring
ehcache-spring-1.1.xsd">
<context:component-scan base-package="org.springframework.cache.ehcache.EhCacheManagerFactoryBean,com .defaultPackage,net.sf.ehcache.CacheManager" />
<!-- ehCache Annotation settings -->
<ehcache:annotation-driven cache-manager="ehCacheManager" />
<bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:ehcache.xml" />
<property name="shared" value="true"/>
</bean>
Wrapper
#Component
public final class MyCache implements Serializable {
#Autowired
private CacheManager ehCacheManager;
private getCacheManager() {
return ehCacheManger; // this is always null
}...}
It seems you are trying to use the EhCacheManagerFactoryBean as your cache manager.
Looking at Spring caching documentation, you need to declare another bean to be your CacheManager created from the factory.
I have a Spring 3.1 application, and I try to use a system variable in the context files. The variable "JAVA_MY_ENV" is defined on my System (on Windows, it is in the "System variables", from the control panel).
In web.xml, I can use it as a variable and it works, it is successfully replaced by the actual value of the variable (let's say "electrotype") :
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>classpath:log/${JAVA_MY_ENV}.log4j.properties</param-value>
</context-param>
I can also use it in my main "bean" context, to do an import, and it also works :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<!-- (...) -->
<import resource="classpath:spring/app-config.xml" />
<import resource="classpath:spring/env/context-env-${JAVA_MY_ENV}.xml" />
</beans>
But In "app-config.xml", one of my other context file, I try this and it doesn't work :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<bean id="appConfiguration" class="com.xxx.app.AppConfiguration">
<constructor-arg value="${JAVA_MY_ENV}" />
</bean>
</beans>
The com.xxx.app.AppConfiguration receives the String "${JAVA_MY_ENV}" as the constructor parameter, not the interpreted value of it!
I'm not sure to understand where the ${} variables are interpreted and where they are not.
Is there a way I can pass the interpreted ${JAVA_MY_ENV} value to my com.xxx.app.AppConfiguration constructor?
As of 3.0 in Spring you should be to inject values into properties
#Value("#{ systemProperties['JAVA_MY_ENV'] }")
private String myVar;
or
<property name ="myVar" value="#{systemProperties['JAVA_MY_ENV']}"/>
Alternatively you can look into using the PropertySourcesPlaceholderConfigurer or similar class. Creating this will tell spring how to look for variables. Often i make a number of property files as well so that environment and internal property file values are available to the app. e.g.
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:someprops.properties</value>
</list>
</property>
<property name="ignoreResourceNotFound" value="true" />
<property name="searchSystemEnvironment" value="true" />
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
A key element in the above example is the "searchSystemEnvironment" being set to true. This tells spring to use env variables (which is what you want)
Possible duplicate of <context:property-placeholder> properties not accessible to the child (web) context
As I understand this is expected behavior. You should inject beans only in servlet context or to include configuration bean into servlet-context.xml
To use a PropertyPlaceholderConfigurer, suggested by BruceLowe, works. Another way (maybe more complicated) I found to resolve a specific property is using :
<bean id="JAVA_MY_ENV" class="org.springframework.util.SystemPropertyUtils" factory-method="resolvePlaceholders">
<constructor-arg value="${JAVA_MY_ENV}" />
</bean>
This creates a String bean containing the resolved value of ${JAVA_MY_ENV}!
Then I can use this bean anywhere a bean ref can be used. For example as a constructor-arg:
<bean id="appConfiguration" class="com.xxx.app.AppConfiguration">
<constructor-arg ref="JAVA_MY_ENV" />
</bean>
So now I use ${JAVA_MY_ENV} in places where it is interpreted, without adding PropertyPlaceholderConfigurer, and the JAVA_MY_ENV bean otherwise.
Is there a special way for doing this?
What i got is:
config.properties with param.key=value
web.xml with ContextLoaderListener that reads the configuration
pages-servlet.xml that defines servlet beans.
What I want is to configure one of the beans in pages-servlet.xml with param.key.
I'm using <property name="myField" value="${param.key}"/> in the xml but I see that the field is configured with ${param.key} instead of 'value'.
What is the right way to configure the bean?
Ok, I solved it by importing application context file that defines configuration bean into pages-servlet.xml.
It works, but seems very wrong.
Property placeholder is what you want.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:property-placeholder location="classpath:/config.properties" />
<bean id="mybean" class="...">
<property name="xxx" value="${prop.value}" />
</bean>
</beans>