Spring @Value annotation in @Controller class not evaluating to value inside properties file

SpringProperties

Spring Problem Overview


I am new to Spring and trying to inject a string with a value using the @Value("${loginpage.message}") annotation inside of a controller annotated with the @Controller annotation and the value of my string is being evaluated as the string "${loginpage.message}" and not what is inside my properties file.

Below is my controller with the string 'message' that I want to inject.

@Controller
public class LoginController extends BaseController {
	@Value("${loginpage.message}")
	private String message;
	
	@RequestMapping("/")
	public String goToLoginPage(Model model) {
		model.addAttribute("message", message);
		
		return "/login";
	}
}

My application context looks like this:

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

<context:annotation-config />

<context:component-scan base-package="com.me.application" />

My properties file has the line:

loginpage.message=this is a test message

Spring must be picking up the value at some point because whenever I change @Value("${loginpage.message}") to a value not in the properties file like @Value("${notInPropertiesFile}"), I get an exception.

Spring Solutions


Solution 1 - Spring

It seems that the question has been already asked https://stackoverflow.com/questions/5275724/spring-3-0-5-doesnt-evaluate-value-annotation-from-properties

The difference between web app root and servlet application contexts is one of the top sources of confusion in Spring, see https://stackoverflow.com/questions/3652090/difference-between-applicationcontext-and-spring-servlet-xml-in-spring

From @Value javadoc :

> Note that actual processing of the @Value annotation is performed by a > BeanPostProcessor

From Spring documentation:

> BeanPostProcessor interfaces are scoped per-container. This is only relevant if you are using container hierarchies. If you define a BeanPostProcessor in one container, it will only do its work on the beans in that container. Beans that are defined in one container are not post-processed by a BeanPostProcessor in another container, even if both containers are part of the same hierarchy.

Solution 2 - Spring

Yea I am having same issue with Spring 3. It doesn't seem to work inside Controllers. To fix the issue I created a another bean with @Service and injected that into the controller. It did work for me. Hope this would be helpful to someone as I spent all day to figure it out.

Solution 3 - Spring

You can @Autowire Environment and then environment.getProperty("name"). See https://stackoverflow.com/a/15562319/632293

Solution 4 - Spring

You need to use PropertySourcePlaceHolder if you are using @Value annotation because it can extract the value from a properties file. If you are using java config base you need to create a bean like this

@Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
    return new PropertySourcesPlaceholderConfigurer();
}

Or if you are using xml based then declare the bean accordingly.

Solution 5 - Spring

I had similar issue in my spring project, but specifically spring BATCH one. I built initially my config as below

@Configuration
public class BatchConfig   
{
  @Bean
  public Job job(@Autowired Step stepMulti, @Autowired Step stepMultiDiff,  @Autowired Step stepMultiPolling
		){
    
	Job job	= jobBuilders.get("job")
		    	.start(init0())
		            .on("POLLING").to(stepMultiPolling)
                .from(init0()).on("*").to(stepMulti).next(stepMultiDiff).end()
		        .build();
	return job;
  }

  @Bean
  public Step init0(){
    return stepBuilders.get("init0")
            .tasklet(new MyDecider())
            .build();
  }

  ...
}

with MyDecider shortly as below

public class MyDecider implements StepExecutionListener , Tasklet{ 

  @Autowired ThreadPoolTaskScheduler taskScheduler;
  @Value("${read.chunk.size}") private Integer pagesize;

@Override
public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
	return RepeatStatus.FINISHED;
}

@Override
public ExitStatus afterStep(StepExecution exe) {
	String type	= exe.getJobParameters().getString("mode");
	
	log.info("SPRING BATCH props:");
	log.info("    READ chunk size:  {}", pagesize);
	
	
	if (StringUtils.equals(type, "send")) {
		log.info("MODE batch SENDING...");
		
		if (taskScheduler !=null) taskScheduler.shutdown();
		else log.info("      Not able to stop scheduler (is null)");
		
		return new ExitStatus("SEND");
	} else  {
		log.info("MODE batch POLLING...");
		return new ExitStatus("POLLING");
	} 
    
}

But in this way neither taskScheduler was wired nor pagesize was injected; both null. Thanks to Boris answer, after some try, I changed BatchConfig as below perfectly working

...

@Bean
public Step init0(){
    return stepBuilders.get("init0")
            .tasklet(decider())
            .build();
}

@Bean
public Tasklet decider() {
	return new MyDecider();
}

...

Reason: having MyDecider construction closer to a Bean annotation in BatchConfig (the one of decider()), make spring understand that MyDecider has to be injected properly, with values found in application.property values, and wired with TaskScheduler used (because I tried also to have SpringScheduler activation, but I wanted to swith it off if jar starting option was 'send').

NOTE: with option mode="send" spring batch job takes the way towards stepMulti and not stepMultiPolling, because MyDecider exit status was SEND and not POLLING; but that is just an explanation out of this topic, so I skip further details.

Hope this spring batch case can be helpful for someone!

Solution 6 - Spring

I'm sorry to ask the obvious, but how do you know that the @Value annotation is not working? One of the problems with the way Spring works is that the Bean pre-processing is carried out after construction of the Bean.

So if you were inspecting your Bean in the constructor with a debugger, you will not see the fields being set. You can add a method in your Bean called, say, audit() and annotate it with @PostConstruct and if you put a log statement in there, put a breakpoint on it, you should see the fields with their @Value values.

If you do that and you still do not see your @Value fields, then you might not even has scanned the Bean. A class that you think implements a Bean is still a Java class, which can be instantiated and will have its fields assigned null, if it is not being pre-processed.

To make sure your Beans are being scanned, the classes should have at least the @Component and you need to add the package of the classes to the @ComponentScan.

@ComponentScan(basePackages = { "com.example.springboot", "org.bilbo.baggins" })

If you don't have the source to the main() method is, which is where you can usually find @ComponentScan, then you can add a @Configuration class in the same package and add a @ComponentScan to that.

In this example, I have the @ComponentScan as a commented-out line in the wrong place (it should replace @ImportResources).

package com.example.springboot;

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

// @ComponentScan(basePackages = { "com.example.springboot", "org.bilbo.baggins" })
@Configuration
@ImportResource({"classpath*:applicationContext.xml"})
public class Configurer {
}

I did that, to show how to use an XML file: applicationContext.xml. This contains a component-scan and creates a Bean.

(Note: only one package is stated to be scanned, component-scan seems to accumulate.)

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/sc
hema/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.xsd">

    <context:annotation-config />

    <context:component-scan base-package="org.bilbo.baggins" />
          <bean id="applicationProperties"
                      class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
                    <property name="location" value="classpath:application.properties" />
          </bean>
</beans>

It is useful to build a bean in the XML file, so that you list it and demonstrate that you have loaded the XML file. You can list the beans using the method String[] beanNames = ctx.getBeanDefinitionNames();

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionChrisView Question on Stackoverflow
Solution 1 - SpringBoris TreukhovView Answer on Stackoverflow
Solution 2 - Springtn.stackoverflowView Answer on Stackoverflow
Solution 3 - SpringjedizView Answer on Stackoverflow
Solution 4 - SpringAbdusSalamView Answer on Stackoverflow
Solution 5 - SpringStefano ScarpantiView Answer on Stackoverflow
Solution 6 - SpringBonaparteView Answer on Stackoverflow