68

I have following configuration class:

@Configuration
@PropertySource(name = "props", value = "classpath:/app-config.properties")
@ComponentScan("service")
public class AppConfig {

and I have service with property:

@Component 
public class SomeService {
    @Value("#{props['some.property']}") private String someProperty;

I receive error when I want to test the AppConfig configuration class with

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'someService': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private java.lang.String service.SomeService.someProperty; nested exception is org.springframework.beans.factory.BeanExpressionException: Expression parsing failed; nested exception is org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 0): Field or property 'props' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext' 

The issue is documented in SPR-8539

but anyway I cannot figure out how to configure PropertySourcesPlaceholderConfigurer to get it work.

Edit 1

This approach works well with xml configuration

<util:properties id="props" location="classpath:/app-config.properties" />

but I want to use java for configuration.

matus
  • 1,483
  • 4
  • 16
  • 19

13 Answers13

138

as @cwash said;

@Configuration
@PropertySource("classpath:/test-config.properties")
public class TestConfig {

     @Value("${name}")
     public String name;


     //You need this
     @Bean
     public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
     }

}
Unmitigated
  • 76,500
  • 11
  • 62
  • 80
bora.oren
  • 3,439
  • 3
  • 33
  • 31
  • 1
    This is also the way suggested in the java docs. See "Using the @Value annotation" in https://docs.spring.io/spring/docs/4.2.4.RELEASE/javadoc-api/org/springframework/context/annotation/PropertySource.html – Martin Becker Jan 28 '16 at 07:42
  • This works perfectly, we do not need to use xml config files. Just PropertySource annotation and declaring the PropertySourcesPlaceholderConfigurer with Bean annotation is enough. – Binita Bharati Nov 29 '17 at 13:35
  • I just put `@Component` on my custom implementation of PropertySourcesPlaceholderConfigurer and it registered fine without needing to declare a dedicated bean – takanuva15 Aug 15 '23 at 19:03
36

If you use @PropertySource, properties have to be retrieved with:

@Autowired
Environment env;
// ...
String subject = env.getProperty("mail.subject");

If you want to retrieve with @Value("${mail.subject}"), you have to register the prop placeholder by xml.

Reason: https://jira.springsource.org/browse/SPR-8539

jediz
  • 4,459
  • 5
  • 36
  • 41
laffuste
  • 16,287
  • 8
  • 84
  • 91
  • 8
    You can also add the static PropertySourcesPlaceholderConfigurer bean in java to allow for the @Value to work.. Here is a sample public static PropertySourcesPlaceholderConfigurer properties() { PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer(); configurer.setIgnoreUnresolvablePlaceholders(true); configurer.setIgnoreResourceNotFound(true); return configurer; } – Gowtham Apr 21 '16 at 20:15
16

I found the reason @value was not working for me is, @value requires PropertySourcesPlaceholderConfigurer instead of a PropertyPlaceholderConfigurer. I did the same changes and it worked for me, I am using spring 4.0.3 release. I configured this using below code in my configuration file.

@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
   return new PropertySourcesPlaceholderConfigurer();
}
slavoo
  • 5,798
  • 64
  • 37
  • 39
Sachchidanand Singh
  • 1,456
  • 1
  • 15
  • 15
13

Don't you need a method on your @Configuration class that returns PropertySourcesPlaceholderConfigurer, annotated @Bean and is static, to register any @PropertySource with Spring?

http://www.baeldung.com/2012/02/06/properties-with-spring/#java

https://jira.springsource.org/browse/SPR-8539

cwash
  • 4,185
  • 5
  • 43
  • 53
6

I had the very same problem. @PropertySource is not playing well with @Value. A quick workaround is to have an XML configuration which you'll refer to it from your Spring Java Configuration using @ImportResource as usual and that XML configuration file will include a single entry: <context:property-placeholder /> (of course with the needed namespace ceremony). Without any other change @Value will inject properties in your @Configuration pojo.

dimitrisli
  • 20,895
  • 12
  • 59
  • 63
5

This can also be configured in java this way

@Bean
public static PropertySourcesPlaceholderConfigurer properties() {
    PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
    configurer.setIgnoreUnresolvablePlaceholders(true);
    configurer.setIgnoreResourceNotFound(true);
    return configurer;
}
Gowtham
  • 1,437
  • 2
  • 17
  • 25
4

That looks mighty complicated, can't you just do

 <context:property-placeholder location="classpath:some.properties" ignore-unresolvable="true"/>

then in code reference:

@Value("${myProperty}")
private String myString;

@Value("${myProperty.two}")
private String myStringTwo;

where some.properties looks something like this

myProperty = whatever
myProperty.two = something else\
that consists of multiline string

For java based config you can do this

@Configuration
@PropertySource(value="classpath:some.properties")
public class SomeService {

And then just inject using @value as before

NimChimpsky
  • 46,453
  • 60
  • 198
  • 311
  • No properties are in text file: `@PropertySource(name = "props", value = "classpath:/app-config.properties")` I just want to replace .xml with java config which is available in Spring 3 – matus Dec 06 '12 at 14:30
  • 3
    it doesn't solve my problem. As I said it works with spring xml configuration. But I need solution for Java config with Value annotation as I wrote. I'm not looking for alternative solution. – matus Dec 06 '12 at 15:12
  • I don't think I should annotate service with _@Configuration_. I tried to move **@PropertySource** directly to service but the error is the same – matus Dec 06 '12 at 16:12
  • 2
    @coffy I'm looking for the exactly same thing. Do you remember what the solution was to this? – Ben Mar 13 '13 at 15:18
  • @matus, nope, it doesn't work like that (Spring 4.3.1). – franta kocourek Aug 03 '16 at 10:37
3

Since Spring 4.3 RC2 using PropertySourcesPlaceholderConfigurer or <context:property-placeholder> is not needed anymore. We can use directly @PropertySource with @Value. See this Spring framework ticket

I have created a test application with Spring 5.1.3.RELEASE. The application.properties contains two pairs:

app.name=My application
app.version=1.1

The AppConfig loads the properties via @PropertySource.

package com.zetcode.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration
@PropertySource(value = "application.properties", ignoreResourceNotFound = true)
public class AppConfig {

}

The Application injects the properties via @Value and uses them.

package com.zetcode;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;

@ComponentScan(basePackages = "com.zetcode")
public class Application {

    private static final Logger logger = LoggerFactory.getLogger(Application.class);

    @Value("${app.name}")
    private String appName;

    @Value("${app.version}")
    private String appVersion;

    public static void main(String[] args) {

        var ctx = new AnnotationConfigApplicationContext(Application.class);
        var app = ctx.getBean(Application.class);

        app.run();

        ctx.close();
    }

    public void run() {

        logger.info("Application name: {}", appName);
        logger.info("Application version: {}", appVersion);
    }
}

The output is:

$ mvn -q exec:java
22:20:10.894 [com.zetcode.Application.main()] INFO  com.zetcode.Application - Application name: My application
22:20:10.894 [com.zetcode.Application.main()] INFO  com.zetcode.Application - Application version: 1.1
Jan Bodnar
  • 10,969
  • 6
  • 68
  • 77
2

The thing is: as far as I get it, <util:propertes id="id" location="loc"/>, is just a shorthand for

<bean id="id" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
  <property name="location" value="loc"/>
</bean>

(see documentation of util:properties). Thus, when you use util:properties, a standalone bean is created.

@PropertySource, on the other hand, as documentation says is an

annotation providing a convenient and declarative mechanism for adding a PropertySource to Spring's Environment'.

(see @PropertySource doc). So it doesn't create any bean.

Then "#{a['something']}" is a SpEL expression (see SpEL), that means "get something from bean 'a'". When util:properties is used, the bean exists and the expression is meaningful, but when @PropertySource is used, there is no actual bean and the expression is meaningless.

You can workaround this either by using XML (which is the best way, I think) or by issuing a PropertiesFactoryBean by yourself, declaring it as a normal @Bean.

Artem Shitov
  • 2,889
  • 1
  • 15
  • 8
0

Another thing that may be happening: ensure your @Value annotated values are not static.

davo
  • 380
  • 2
  • 11
0

In my case, depends-on="bean1" was within property-placeholder was causing the issue. I removed that dependency and used @PostConstruct to achieve the same original functionality and was able to read the new values too.

Smart Coder
  • 1,435
  • 19
  • 19
0

If you are configuring with xml, after adding

<context:property-placeholder location="..."/>

Make sure your annotations are activated. In my case properties were not fetched for this reason:

<context:annotation-config/>

leonaugust
  • 186
  • 1
  • 4
  • 15
0

for me it worked

spring.cloud.config.server.git.default-label=main spring.cloud.config.server.native.searchLocations=file:///Users/${your username}/Projects/git-local-repositories/ spring.profiles.active=native