2011-03-07 45 views
2

我想從應用程序上下文中提取bean。應用程序上下文bean

所以我定義的類:

public class ApplicationContextProvider implements ApplicationContextAware { 

    private static ApplicationContext applicationContext; 

    public static ApplicationContext getApplicationContext() { 
     return applicationContext; 
    } 
    public void setApplicationContext(ApplicationContext _applicationContext) throws BeansException { 
     applicationContext = _applicationContext; 

    } 

} 

,並在我的applicationContext.xml

<bean id="workflowService" class="com.mycompany.util.WorkflowService"> 
    <bean id="applicationContextProvider" class="com.mycompany.util.ApplicationContextProvider"></bean> 
    <context:annotation-config /> 

但是在我的代碼,當我嘗試:

WorkflowService service = (WorkflowService) ApplicationContextProvider.getApplicationContext().getBean("workflowService"); 

我得到:

java.lang.ClassCastException:$ Proxy40不能轉換到com.mycompany.util.WorkflowService

EDITED

WorkflowService代碼:

public class WorkflowService implements Serializable { 
    ... 
    @PostConstruct 
     public void init() { 
    } 
    ... 
    @Transactional(readOnly = true, propagation = Propagation.SUPPORTS) 
     public Collection<lData> findData(Integer contractId) { 
    }  
} 
+0

這一項的相對 - http://stackoverflow.com/questions/5133291/applicationcontextprovider-is-not-being-called - 我猜你是混在一起的界面v具體類在bean。定義/引用。請發佈WorkflowService的代碼。 – 2011-03-07 21:00:34

回答

3

我猜WorkflowService是類實現至少一個接口(你沒有提供足夠的代碼)。你試圖從Spring中查找確切的類,而你應該要求其中的一個接口。

這是因爲Spring大部分時間都將bean包裝在若干代理(例如事務性代理)中。如果類實現了至少一個接口,則生成的代理實現所有這些接口,但不能將其轉換爲原始類。如果該類沒有實現任何接口(通常被認爲是重量級服務的糟糕做法,儘管如此),Spring將使用原始類的CGLIB子類。在這種情況下,你的代碼是有效的。

+0

我懷疑你是在正確的領域,但可能有一個更簡單的答案來解決錯誤 - 建議WorkflowService上的註釋 - 將按照您的說法進行代理。 @Odelya - 請發佈WorkflowService的代碼以及您正在使用的註釋。有人可能會猜測@Service? – 2011-03-07 21:31:10

+0

另請參見:在應用程序上下文中xml - 不是applicationContextProvider內部bean - 是否可以「看到」外部bean? – 2011-03-07 21:35:23

+0

@David,Tomasz - 我用workflowservice代碼編輯了我的問題。 – Dejell 2011-03-07 21:54:01

4

你的問題是這樣的位:

WorkflowService implements Serializable 

是彈簧產生將實現所有的類做接口的任何代理 - 在這種情況下,Serializable,這是幾乎可以肯定不是你想要的。

你應該做的是從WorkflowService提取一個新的接口,其中包括findData方法(我們稱之爲WorkflowOperations)。通過實現該界面,你就可以投射到該界面,例如

public interface WorkflowOperations { 
    Collection<lData> findData(Integer contractId); 
} 


public class WorkflowService implements WorkflowOperations { 

    ... 
    @PostConstruct 
     public void init() { 
    } 
    ... 
    @Transactional(readOnly = true, propagation = Propagation.SUPPORTS) 
     public Collection<lData> findData(Integer contractId) { 
    } 

} 

然後:

WorkflowOperations service = (WorkflowOperations) ApplicationContextProvider.getApplicationContext().getBean("workflowService"); 

你或許應該也WorkflowService刪除Serializable。你幾乎肯定不需要這個,像這樣序列化Spring bean是沒有意義的。如果你只是增加了Serializable出於習慣,然後將其刪除(並擺脫那種特殊的習慣)。

+0

+1。是的 - 避免Serializable,除非你絕對需要它。閱讀Bloch的「Effective Java」 - v。詳細討論Serializable的缺點。 – 2011-03-08 07:09:20

2

您註釋的服務使用@Transactional,所以Spring使用事務性JDK動態代理來封裝您的服務bean,該代理實現與您的bean相同的接口,但不是WorkflowService。這就是爲什麼當您嘗試將其分配給WorkflowService變量時,您會得到ClassCastException。我看到了兩個可能的解決方案:

  • 指定的接口WorkflowService與您的業務方法和一個WorkflowServiceImpl類實現它。然後在Spring上下文中將bean定義從WorkflowService更改爲WorkflowServiceImpl。這就是我所推薦的,既是一個通用的設計原則,也是專門爲Spring環境工作的:Spring喜歡接口。

  • 在您的Spring上下文中,將proxy-target-class="true"添加到您的<tx:annotation-driven/>元素中,以強制Spring通過繼承實現代理,以便proxy instanceof WorkFlowService爲真。我覺得這個解決方案更骯髒。另外請注意,您以這種方式添加對CGLIB的依賴關係。

相關問題