2012-04-11 148 views
4

我在寻找对这个问题的更好的理解。一个解决方法非常简单,即将配置数据移动到另一个没有代理/建议的类中,但是我认为更好地理解这将帮助我避免将来出现其他相关问题,所以我希望不管任何解释可以提供。为什么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,所以我甚至不知道在哪里提交错误报告。

因此,任何帮助理解这一点,将不胜感激。谢谢。

+2

你确定它是由AOP引起的吗?如果删除'@ Transactional'会怎么样? – axtavt 2012-04-11 08:50:17

+0

@axtavt你指出我的解决方案[这里](http://stackoverflow.com/a/4335438/712765)。在下面全部看到我的答案,但简短的答案是'Controller's单独在我的(错误)配置中创建两次。 '@ Transactional'不是问题,因为它使用AspectJ而不是代理。 – 2012-04-12 02:55:38

回答

6

我想通了。实际上,这太神奇了。

我在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/>以便它可以找到属性文件。

1

首先,使用$符号是正确的,而不是#

现在关于原始的海报,我认为你有2个注释(@Controller和@Transactional)之间冲突的行为。

使用@Transactional注释将代入您的实现(我想检测错误并启动回滚)。

现在你在说你看到了你的控制器的两个实例。这不应该发生。 通常情况下,应该只有一个控制器的实例加载到内存中。

可能与您的配置文件有关或者由于@Transactional及其代理性质的存在?作为一个方面说明,我从来没有在控制器本身中使用@Transactional,而是使用服务或dao的方法。由于可能失败并需要回滚的实际进程在那里,并且可以从不同的控制器/源访问。

问候。

+0

我了解将@Transactional从“Controller”移出到服务层的愿望。但是,我相信精益程序设计不需要做额外的工作,而这些工作永远都不需要。在这种情况下,实现一个REST服务API,控制器基本上没有做任何事情,但实现该服务,并且没有人会以任何其他方式调用该服务。如果有一天他们这样做,那么重构会很容易,但是前面我已经创建了一个完整的图层。通过在@Transactional中使用AspectJ,我不必担心代理。 – 2012-04-12 03:01:40

+0

精益编程很好,但可以根据上下文采取不同的表示形式。在休息服务的情况下,你必须考虑到:1.他们经常发展(新的需求/需求)2.你希望让开发人员易于阅读控制器,而不是让他们与业务规则“杂乱无章”。它在开始时确实需要做更多的工作(创建服务),但从长远来看,它给你一个真正的自由。 – Farid 2012-04-12 12:17:01

+0

法里德,我的“精益”愿景是避免“在一开始就做更多的工作”,它不会使未来的增长变得更加困难。 Controller逻辑基本上是:读取请求,执行请求的动作,并将响应写入try/catch块中的“请求的动作”。稍后,如果需要单独的Service对象,则可以从控制器中将try/catch块的内部剪切并将其粘贴到服务方法中。真正的长期自由没有限制,但肯定更快到达一个有效的产品。 – 2012-04-12 15:59:55

0

在我来说,我解决了这种方式: 在spring-servlet.xml<context:component-scan ... />之前,我把这个:

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

虽然strings.properties文件我放到src/main/resources/

注意:context:property-placeholder标签具有有限的可见性,因此我已阅读某处推荐将其复制到每个上下文文件中,并在其中使用字符串。

1

我有同样的问题,这是我需要双方

要获得这两个senarios工作

  1. 注入(通过@Value)属性到控制器
  2. 事实
  3. 向控制器注入具有属性(通过@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; 
} 

这为我工作将工作为你。祝你好运!

相关问题