2011-03-07 44 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的依赖关系。

相关问题