@Controller 类中的 Spring @Value 注释未评估属性文件中的值
Posted
技术标签:
【中文标题】@Controller 类中的 Spring @Value 注释未评估属性文件中的值【英文标题】:Spring @Value annotation in @Controller class not evaluating to value inside properties file 【发布时间】:2012-08-07 02:23:02 【问题描述】:我是 Spring 的新手,并尝试使用带有 @Controller
注释注释的控制器内部的 @Value("$loginpage.message")
注释注入一个带有值的字符串,并且我的字符串的值被评估为字符串 "$loginpage.message"
和不是我的属性文件中的内容。
下面是我的控制器,带有我要注入的字符串“消息”。
@Controller
public class LoginController extends BaseController
@Value("$loginpage.message")
private String message;
@RequestMapping("/")
public String goToLoginPage(Model model)
model.addAttribute("message", message);
return "/login";
我的应用程序上下文如下所示:
<context:property-placeholder location="classpath:properties/application.properties" />
<context:annotation-config />
<context:component-scan base-package="com.me.application" />
我的属性文件有一行:
loginpage.message=this is a test message
Spring 必须在某个时候获取该值,因为每当我将 @Value("$loginpage.message")
更改为不在属性文件中的值(如 @Value("$notInPropertiesFile")
)时,都会出现异常。
【问题讨论】:
Chris 的回答应该可以确保属性文件在项目类路径中,如果它要在外部战争中,那么应该在应用程序上下文中使用属性占位符。它应该可以工作。 【参考方案1】:我很抱歉问这个显而易见的问题,但是你怎么知道@Value 注释不起作用? Spring工作方式的问题之一是Bean的预处理是在Bean构建完成之后进行的。
因此,如果您使用调试器在构造函数中检查 Bean,您将看不到正在设置的字段。您可以在 Bean 中添加一个名为 audit() 的方法,并使用 @PostConstruct 对其进行注释,如果您在其中放置日志语句,在其上放置断点,您应该会看到带有 @Value 值的字段。
如果您这样做了,但仍然看不到 @Value 字段,那么您甚至可能还没有扫描 Bean。一个你认为实现 Bean 的类仍然是一个 Java 类,它可以被实例化,并且如果它没有被预处理,它的字段将被分配为 null。
为了确保您的 Bean 正在被扫描,类应该至少具有 @Component,并且您需要将类的包添加到 @ComponentScan。
@ComponentScan(basePackages = "com.example.springboot", "org.bilbo.baggins" )
如果您没有 main() 方法的源代码,通常可以在其中找到@ComponentScan,那么您可以在同一个包中添加一个@Configuration 类,然后在其中添加一个@ComponentScan。
在此示例中,我将 @ComponentScan 作为注释掉的行放在错误的位置(它应该替换 @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
我这样做是为了展示如何使用 XML 文件:applicationContext.xml。这包含一个组件扫描并创建一个 Bean。
(注意:只扫描一个包,component-scan 好像是累积的。)
<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>
在 XML 文件中构建一个 bean 很有用,这样您就可以列出它并证明您已经加载了 XML 文件。你可以使用String[] beanNames = ctx.getBeanDefinitionNames();
方法列出beans
【讨论】:
【参考方案2】:我在我的 spring 项目中遇到了类似的问题,但特别是 spring BATCH one。我最初构建我的配置如下
@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();
...
MyDecider 如下所示
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");
但是这样既没有连接taskScheduler,也没有注入pagesize;两者都为空。感谢鲍里斯的回答,经过一番尝试,我将 BatchConfig 更改为以下完美工作
...
@Bean
public Step init0()
return stepBuilders.get("init0")
.tasklet(decider())
.build();
@Bean
public Tasklet decider()
return new MyDecider();
...
原因:让 MyDecider 构造更接近 BatchConfig 中的 Bean 注释(decider() 之一),让 spring 明白必须正确注入 MyDecider,其值在 application.property 值中找到,并与使用的 TaskScheduler 连接(因为我也尝试过激活 SpringScheduler,但如果 jar 启动选项是“发送”,我想关闭它)。
注意:使用选项 mode="send" 春季批处理作业采用 stepMulti 而不是 stepMultiPolling,因为 MyDecider 退出状态是 SEND 而不是 POLLING;但这只是本主题之外的解释,所以我跳过更多细节。
希望这个春季批次案例可以对某人有所帮助!
【讨论】:
【参考方案3】:如果您使用 @Value 注释,则需要使用 PropertySourcePlaceHolder,因为它可以从属性文件中提取值。如果您使用的是 java config base,则需要创建一个像这样的 bean
@Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigInDev()
return new PropertySourcesPlaceholderConfigurer();
或者,如果您使用基于 xml,则相应地声明 bean。
【讨论】:
【参考方案4】:您可以先@Autowire Environment
,然后再environment.getProperty("name")
。
见https://***.com/a/15562319/632293
【讨论】:
【参考方案5】:是的,我在 Spring 3 中遇到了同样的问题。它似乎在控制器中不起作用。 为了解决这个问题,我使用 @Service 创建了另一个 bean 并将其注入到控制器中。 它确实对我有用。希望这对我花了一整天时间弄清楚的人有所帮助。
【讨论】:
您好,我尝试了您的解决方案,但我的服务遇到了同样的问题,该服务需要什么特别的吗?【参考方案6】:问题好像已经问过了Spring 3.0.5 doesn't evaluate @Value annotation from properties
Web 应用程序根目录和 servlet 应用程序上下文之间的区别是 Spring 中最容易混淆的来源之一,请参阅 Difference between applicationContext.xml and spring-servlet.xml in Spring Framework
来自@Value
javadoc:
请注意,@Value 注释的实际处理是由一个 豆后处理器
来自Spring documentation:
BeanPostProcessor 接口的范围是每个容器。这仅在您使用容器层次结构时才相关。如果您在一个容器中定义一个 BeanPostProcessor,它只会在该容器中的 bean 上工作。在一个容器中定义的 Bean 不会由另一个容器中的 BeanPostProcessor 进行后处理,即使两个容器属于同一层次结构。
【讨论】:
谢谢,成功了。我必须将<context:property-placeholder location="classpath:properties/application.properties" />
添加到dispatcher-servlet.xml 并且它现在能够从属性文件中读取。
如果有人试图在类构造函数中访问这些属性,请记住你不能,因为它们还没有被连接。您应该改为实现 InitializingBean::afterPropertiesSet
这也适用于将属性值注入 Spring MVC 拦截器,例如 HandlerInterceptorAdaptor 的子类。
@Chris 感谢您的提示。一直把我的头发拉到那个上面。
谢谢!我希望我能更快地找到这个答案。重复一遍,如果你的实例成员用@Value("$key")
注释在运行时被设置为未解析的“$key”,那么问题可能是你有一个应用程序上下文的层次结构,因此你需要属性占位符在多个级别配置。以上是关于@Controller 类中的 Spring @Value 注释未评估属性文件中的值的主要内容,如果未能解决你的问题,请参考以下文章