简体   繁体   English

通过依赖注入访问Spring bean *而不是*

[英]Accessing Spring bean *not* by dependency injection

We have some domain objects that are created at runtime - not by Spring. 我们有一些在运行时创建的域对象 - 而不是Spring。 These domain objects need access to some service type beans that are being managed by Spring. 这些域对象需要访问由Spring管理的某些服务类型bean。 How can the domain objects that are created at runtime access Spring beans dynamically (not by DI)? 如何在运行时创建的域对象动态访问Spring bean(而不是通过DI)?

@duffymo's answer is the most common solution to this problem, and the one you should probably follow. @ duffymo的答案是这个问题最常见的解决方案,也是你应该遵循的问题。

However, if you're feeling saucy, and if your situation supports it, then you could consider using Spring's AspectJ support to autowire your non-spring-managed domain objects with spring beans: 但是,如果您感觉很好,并且如果您的情况支持它,那么您可以考虑使用Spring的AspectJ支持来使用spring bean自动装配非Spring管理的域对象

[...] contains an annotation-driven aspect that exploits this capability to allow dependency injection of any object. [...]包含一个注释驱动的方面,利用此功能允许依赖注入任何对象。 The support is intended to be used for objects created outside of the control of any container. 该支持旨在用于在任何容器控制之外创建的对象。 Domain objects often fall into this category because they are often created programmatically using the new operator, or by an ORM tool as a result of a database query. 域对象通常属于此类别,因为它们通常使用new运算符以编程方式创建,或者由于数据库查询而由ORM工具创建。

It's verging on voodoo, this stuff, and it only works on certain appservers, but it might be the tool for you. 它正在接近伏都教,这个东西,它只适用于某些appservers,但它可能是你的工具。

您必须向他们提供对ApplicationContext或BeanFactory的引用,以便他们可以获得Spring管理的bean。

Spring has a mechanism called the SingletonBeanFactoryLocator that you can use in places, such as EJB 2.0 applications, to get the bean factory/application context in places where you can't use dependency injection. Spring有一个名为SingletonBeanFactoryLocator的机制,您可以在某些地方使用它,例如EJB 2.0应用程序,以便在不能使用依赖项注入的地方获取bean工厂/应用程序上下文。 There's a hook in the existing Spring ContextLoader that you're already using to take advantage of this functionality, though it's somewhat tricky to setup. 现有的Spring ContextLoader中有一个钩子,你已经用它来利用这个功能,虽然设置起来有些棘手。

You'll need to separate out your application contexts into a parent/child relationship. 您需要将应用程序上下文分离为父/子关系。 The parent will contain the service layer objects, while the child is composed of the web-layer specific stuff. 父级将包含服务层对象,而子级由Web层特定的东西组成。

Then you'll have to add a couple of context parameters to your web.xml (like you do for the config location) to tell it to initialize the parent: 然后,您必须向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>

The locatorFactorySelector is a reference to an xml file, BUT (this is where I always get confused) this isn't going to point to the xml that defines your services. locatorFactorySelector是对xml文件的引用,BUT(这是我总是感到困惑的地方),这并不是指向定义服务的xml。 It's a bean definition xml that creates an application context bean. 它是一个bean定义xml,它创建一个应用程序上下文bean。 That you then reference this bean using the parentContextKey attribute. 然后,您使用parentContextKey属性引用此bean。

So for example, beanRefFactory.xml would then contain: 例如,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>

In your non-DIed domain objects, you could then get to the application context with this code: 在非DIed域对象中,您可以使用以下代码访问应用程序上下文:

   BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);
   BeanFactoryReference contextRef= locator.useBeanFactory(parentContextKey);
   ApplicatonContext context = (ApplicationContext) contextRef.getFactory();

You can find more information on ContextSingletonBeanFactoryLocator in this blog post . 您可以在此博客文章中找到有关ContextSingletonBeanFactoryLocator更多信息。 There's also a good description of using this approach in the chapter on EJBs in Java Development with the Spring Framework . 在使用Spring Framework的Java开发中的 EJB章节中,还有一个很好的描述。

Create a Factory and register it with spring, use the factory to create domain object rather than using 'new' 创建一个工厂并用spring注册它,使用工厂创建域对象而不是使用'new'

in this case, you have all the goodies available to your DomainObjFactory 在这种情况下,您拥有DomainObjFactory可用的所有好东西

Slightly related question 有点相关的问题

You could adopt a similar strategy to Hibernate, creating facilities for interceptors in you domain instance factory. 您可以采用与Hibernate类似的策略,为您的域实例工厂中的拦截器创建工具。 You can inject the required services into spring managed interceptors, which are injected into your domain factories 您可以将所需的服务注入到Spring管理的拦截器中,这些拦截器将注入您的域工厂

This would untie your application from Spring specific interfaces. 这将从Spring特定接口解开您的应用程序。 The below example could be simplified with generics, but you should get the idea. 下面的例子可以用泛型简化,但你应该明白这个想法。

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);
  }
}

Then you would configure it something like 然后你会配置它像

<bean id="myServiceInjector" class="MyServiceInjector">
  <property name="myService" ref="someServiceBean" />
</bean>

<bean class="DomainFactory">
  <property name="interceptors">
    <list>
      <ref bean="myServiceInjector"/>
    </list>
  </property>
</bean>

You could use the approach that @duffymo suggested but in the event that you are not running Spring as a web app you should look at this SO entry . 您可以使用@duffymo建议的方法,但如果您没有将Spring作为Web应用程序运行,则应该查看此SO 条目 See that the utility class is made thread safe in the most popular answer. 在最流行的答案中看到实用程序类是线程安全的。 Btw the OP and the answer you should get all you need and then you can use this utility class to get a reference to the Spring managed beans. 通过OP和答案,你应该得到所有你需要的,然后你可以使用这个实用程序类来获得对Spring托管bean的引用。

One solution is to use a global static method that returns the Spring application contest (see BeanLocator .) 一种解决方案是使用返回Spring应用程序竞赛的全局static方法(请参阅BeanLocator 。)

Other options could be to have your business objects implement the ApplicationContextAware interface. 其他选项可能是让您的业务对象实现ApplicationContextAware接口。 At instantiation, your "integration" code would have to inject the Spring ApplicationContext into the dynamically created bean (maybe by checking if the class implements ApplicationContextAware .) This of course would tie your business code with Spring, but the first option would be the same. 在实例化时,您的“集成”代码必须将Spring ApplicationContext注入到动态创建的bean中(可能通过检查类是否实现了ApplicationContextAware 。)这当然会将您的业务代码与Spring绑定,但第一个选项将是相同的。

A variation would be to not inject the ApplicationContext directly but reuse the Spring @Autowired annotation. 一种变化是不直接注入ApplicationContext但重用Spring @Autowired注释。 The "integration" code would then inject only the annotated fields. 然后,“集成”代码将仅注入带注释的字段。

You could make the dependent object a singleton with a static getInstance() method that could be used by the non-Spring managed domain objects. 您可以使依赖对象成为具有静态getInstance()方法的单例,该方法可由非Spring托管域对象使用。 You would then make it available to Spring via org.springframework.beans.factory.config.MethodInvokingFactoryBean like: 然后,您可以通过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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM