2012-04-11 286 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; 
} 

這爲我工作將工作爲你。祝你好運!

相關問題