我们有一些在运行时创建的域对象 - 而不是Spring。这些域对象需要访问由Spring管理的某些服务类型bean。如何在运行时创建的域对象动态访问Spring bean(而不是通过DI)?
答案 0 :(得分:8)
@ duffymo的回答是这个问题的最常见解决方案,也是你应该遵循的问题。
但是,如果你感觉很好,如果你的情况支持它,那么你可以考虑使用Spring的AspectJ支持autowire your non-spring-managed domain objects使用spring bean:
[...]包含注释驱动 利用此功能的方面 允许依赖注入任何 宾语。该支持旨在 用于外部创建的对象 控制任何容器。域 对象通常属于这一类 因为它们经常被创造出来 以编程方式使用新的 运算符,或作为一个ORM工具 数据库查询的结果。
它正在接近伏都教,这个东西,它只适用于某些appservers,但它可能是你的工具。
答案 1 :(得分:7)
您必须向他们提供对ApplicationContext或BeanFactory的引用,以便他们可以获得Spring管理的bean。
答案 2 :(得分:3)
Spring有一个名为SingletonBeanFactoryLocator
的机制,您可以在某些地方使用它,例如EJB 2.0应用程序,以便在不能使用依赖项注入的地方获取bean工厂/应用程序上下文。现有的Spring ContextLoader
中有一个钩子,你已经用它来利用这个功能,虽然设置起来有些棘手。
您需要将应用程序上下文分离为父/子关系。父级将包含服务层对象,而子级由特定于Web层的东西组成。
然后你必须向你的web.xml添加几个上下文参数(就像你对配置位置所做的那样)来告诉它初始化父文件:
<context-param>
<param-name>locatorFactorySelector</param-name>
<param-value>classpath:beanRefFactory.xml</param-value>
</context-param>
<context-param>
<param-name>parentContextKey</param-name>
<param-value>beanRefFactory</param-value>
</context-param>
locatorFactorySelector
是对xml文件的引用,但是(这是我总是感到困惑的地方)这并不是指向定义服务的xml。它是一个bean定义xml,它创建一个应用程序上下文bean。然后,您使用parentContextKey
属性引用此bean。
因此,例如,beanRefFactory.xml将包含:
<beans>
<bean id="beanRefFactory"
class="org.springframework.context.support.ClassPathXmlApplicationContext">
<constructor-arg>
<list>
<value>service-context.xml</value>
</list>
</constructor-arg>
</bean>
</beans>
在非DIed域对象中,您可以使用以下代码访问应用程序上下文:
BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);
BeanFactoryReference contextRef= locator.useBeanFactory(parentContextKey);
ApplicatonContext context = (ApplicationContext) contextRef.getFactory();
您可以在this blog post中找到有关ContextSingletonBeanFactoryLocator
的更多信息。在Java Development with the Spring Framework中的EJB章节中也有一个很好的描述使用这种方法。
答案 3 :(得分:3)
创建工厂并使用spring注册它,使用工厂创建域对象而不是使用'new'
在这种情况下,您拥有DomainObjFactory
可用的所有好东西答案 4 :(得分:1)
略有关联question
您可以采用与Hibernate类似的策略,为您的域实例工厂中的拦截器创建工具。您可以将所需的服务注入到弹簧管理的拦截器中,这些拦截器将注入您的域工厂
这将从Spring特定接口解开您的应用程序。以下示例可以使用泛型进行简化,但您应该明白这一点。
public interface Interceptor {
public void onCreate(Object entity);
}
public class DomainFactory {
public void setInterceptors(List<Interceptor> interceptors) { ... }
public Object createInstance() {
// iterate interceptors, call onCreate
}
}
public interface MyServiceAware {
public void setMyService(MyService service);
}
public class MyServiceInjector implements Interceptor {
private MyService myService;
public void onCreate(Object entity) {
if (entity instanceof MyServiceAware)
((MyServiceAware) entity).setMyService(myService);
}
}
然后你会配置它像
<bean id="myServiceInjector" class="MyServiceInjector">
<property name="myService" ref="someServiceBean" />
</bean>
<bean class="DomainFactory">
<property name="interceptors">
<list>
<ref bean="myServiceInjector"/>
</list>
</property>
</bean>
答案 5 :(得分:0)
您可以使用@duffymo建议的方法,但如果您没有将Spring作为Web应用程序运行,则应该查看此SO entry。在最流行的答案中看到实用程序类是线程安全的。通过OP和答案,您应该得到所需的一切,然后您可以使用此实用程序类来获取对Spring托管bean的引用。
答案 6 :(得分:0)
一种解决方案是使用全局static
方法返回Spring应用程序竞赛(请参阅BeanLocator。)
其他选项可能是让您的业务对象实现ApplicationContextAware
接口。在实例化时,您的“集成”代码必须将Spring ApplicationContext
注入到动态创建的bean中(可能通过检查类是否实现ApplicationContextAware
。)这当然会将您的业务代码与Spring绑定,但第一种选择是相同的。
变体是不直接注入ApplicationContext
,而是重用Spring @Autowired
注释。然后,“集成”代码将仅注入带注释的字段。
答案 7 :(得分:0)
您可以使依赖对象成为具有静态getInstance()方法的单例,该方法可由非Spring托管域对象使用。然后,您可以通过org.springframework.beans.factory.config.MethodInvokingFactoryBean将其提供给Spring,如:
<bean id="myObject"
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="staticMethod">
<value>com.example.MyObject.getInstance</value>
</property>
</bean>