为什么Spring @Value与@Controller不兼容?
我在寻找对这个问题的更好的理解。一个解决方法非常简单,即将配置数据移动到另一个没有代理/建议的类中,但是我认为更好地理解这将帮助我避免将来出现其他相关问题,所以我希望不管任何解释可以提供。为什么Spring @Value与@Controller不兼容?
我在Spring STS和vFabric tc服务器上使用Spring 3.1.0.RELEASE。使用@Controller类实现一个基本的小型REST服务器。这非常棒(实际上是这样),但@Controller也是@Transactional,并且在加载时间编织和vFabric tc服务器之间,它打破了@Value。
@Controller
@RequestMapping("/hello")
public class MyAPI {
@Value("${my.property}")
private String prop;
...
@Transactional
handleRequest(...) ...
}
而且属性文件app.properties:
my.property = SUCCESS
这JUnit的下正常工作,与测试越来越有支撑设置为“成功”一MyAPI对象。但是当应用程序被加载到vFabric中时,我猜测它会加载时间编织和代理。无论发生什么事情,都会创建两个MyAPI实例,其中一个具有prop ==“SUCCESS”,另一个(不幸是处理http请求的那个)具有prop ==“$ {my.prop}”。
总而言之,我称之为魔法失败,这是我最喜欢使用AOP之类的东西。即使使用STS,我也不知道如何找出问题背后的原因,或者找出这是一个严重的错误。如果它是一个bug,我不知道它是否是Spring,AspectJ,加载时织布工或vFabric中的一个bug,所以我甚至不知道在哪里提交错误报告。
因此,任何帮助理解这一点,将不胜感激。谢谢。
我想通了。实际上,这太神奇了。
我在STS中使用了Spring Roo来生成基本的应用程序框架,然后使用STS将Roo分解出来,因为我们不想坚持使用它。
Roo作为“最佳实践”所做的一件事是创建两个Spring上下文,一个用于整个应用程序,另一个仅限于调度程序servlet。到底为什么,我还没有做到,但我想他们想让表示层的东西,比如控制器,不会进入共享的服务层。这很好地由axtavt here解释。这一切都是由STS隐藏的。
在带有Roo的STS中,WEB-INF源不在我期望的/ src/main/resources(这是META-INF目录所在的位置)的位置,而是在/ src/main/webapp下面不是Java源代码目录,因此完全分开显示,仅位于/ target目录之上,因此我将它误认为是输出文件夹。
在applicationContext.xml中,Roo插入了过滤器,以防止应用程序上下文构造控制器,正如axtavt的文章中所解释的,但它也会放入另一个过滤器来消除Roo生成的类的扫描。我同时把两个过滤器都拿出来,并不真正知道他们在那里,但认为他们只是Roo的剩菜。
所以现在我已经得到了如前所述的Controllers being created twice的问题。并且应用程序上下文中的一个获取属性,因为它使用applicationContext.xml并查找属性文件。但为什么他们都没有获得物业?
这让我回到了隐藏的webapps文件夹。在WEB-INF文件夹内部,Roo放置了web.xml(自然地)和一个包含webmvc-config.xml文件的spring文件夹。这个配置文件被设置为扫描,创建和设置控制器。 web.xml文件将web应用程序设置为使用applicationContext.xml和dispatcherServlet来使用webmvc-config.xml,所以我应该将过滤器留在applicationContext.xml中以避免重复创建。
拼图的最后一部分是这个webmvc-config.xml文件。由于这是控制器设置的上下文,所以即文件需要配置<上下文:property-placeholder/>以便它可以找到属性文件。
首先,使用$符号是正确的,而不是#
现在关于原始的海报,我认为你有2个注释(@Controller和@Transactional)之间冲突的行为。
使用@Transactional注释将代入您的实现(我想检测错误并启动回滚)。
现在你在说你看到了你的控制器的两个实例。这不应该发生。 通常情况下,应该只有一个控制器的实例加载到内存中。
可能与您的配置文件有关或者由于@Transactional及其代理性质的存在?作为一个方面说明,我从来没有在控制器本身中使用@Transactional,而是使用服务或dao的方法。由于可能失败并需要回滚的实际进程在那里,并且可以从不同的控制器/源访问。
问候。
我了解将@Transactional从“Controller”移出到服务层的愿望。但是,我相信精益程序设计不需要做额外的工作,而这些工作永远都不需要。在这种情况下,实现一个REST服务API,控制器基本上没有做任何事情,但实现该服务,并且没有人会以任何其他方式调用该服务。如果有一天他们这样做,那么重构会很容易,但是前面我已经创建了一个完整的图层。通过在@Transactional中使用AspectJ,我不必担心代理。 – 2012-04-12 03:01:40
精益编程很好,但可以根据上下文采取不同的表示形式。在休息服务的情况下,你必须考虑到:1.他们经常发展(新的需求/需求)2.你希望让开发人员易于阅读控制器,而不是让他们与业务规则“杂乱无章”。它在开始时确实需要做更多的工作(创建服务),但从长远来看,它给你一个真正的自由。 – Farid 2012-04-12 12:17:01
法里德,我的“精益”愿景是避免“在一开始就做更多的工作”,它不会使未来的增长变得更加困难。 Controller逻辑基本上是:读取请求,执行请求的动作,并将响应写入try/catch块中的“请求的动作”。稍后,如果需要单独的Service对象,则可以从控制器中将try/catch块的内部剪切并将其粘贴到服务方法中。真正的长期自由没有限制,但肯定更快到达一个有效的产品。 – 2012-04-12 15:59:55
在我来说,我解决了这种方式: 在spring-servlet.xml
<context:component-scan ... />
之前,我把这个:
<context:property-placeholder location="classpath:strings.properties"/>
虽然strings.properties
文件我放到src/main/resources/
。
注意:context:property-placeholder
标签具有有限的可见性,因此我已阅读某处推荐将其复制到每个上下文文件中,并在其中使用字符串。
我有同样的问题,这是我需要双方
要获得这两个senarios工作
- 注入(通过@Value)属性到控制器 事实
- 向控制器注入具有属性(通过@Value)的托管bean(通过@Inject)
我希望这有助于。
这里是web.xml。
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:/spring-config/spring-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
这里是spring-servlet。xml:关键是要有BOTH util:properties和context:property-placeholder。虽然他们找到相同的文件,但它们有不同的用途。
<util:properties id="propSource" location="classpath:/properties/prop1.properties" />
<context:property-placeholder location="classpath:/properties/prop1.properties" />
<context:component-scan base-package="com.concurrent.controller" />
<context:annotation-config/>
<mvc:annotation-driven/>
这里是我的控制器类的一个片段:
@Controller
@RequestMapping("/fooController")
public class MyController {
@Value("${message1}")
public String message1;
@Inject
public ConfigProperties configProperties;
最后,这里是我的课,我注入性质分为:
@Service
public class ConfigProperties {
@Value("${message1}")
public String message1;
}
这为我工作将工作为你。祝你好运!
你确定它是由AOP引起的吗?如果删除'@ Transactional'会怎么样? – axtavt 2012-04-11 08:50:17
@axtavt你指出我的解决方案[这里](http://stackoverflow.com/a/4335438/712765)。在下面全部看到我的答案,但简短的答案是'Controller's单独在我的(错误)配置中创建两次。 '@ Transactional'不是问题,因为它使用AspectJ而不是代理。 – 2012-04-12 02:55:38