【问题标题】:Accessing Spring bean *not* by dependency injection通过依赖注入访问 Spring bean *not*
【发布时间】:2009-11-28 22:40:06
【问题描述】:

我们有一些在运行时创建的域对象——不是由 Spring 创建的。这些域对象需要访问一些由 Spring 管理的服务类型 bean。在运行时创建的域对象如何动态访问 Spring bean(而不是通过 DI)?

【问题讨论】:

    标签: java spring dependency-injection


    【解决方案1】:

    @duffymo 的回答是这个问题最常见的解决方案,也是你应该遵循的。

    但是,如果你觉得很有趣,并且你的情况支持它,那么你可以考虑使用 Spring 的 AspectJ 支持到 autowire your non-spring-managed domain objects 和 spring bean:

    [...] 包含一个注解驱动的 利用这种能力的方面 允许任何依赖注入 目的。该支持旨在 用于在外部创建的对象 任何容器的控制。领域 对象通常属于这一类 因为它们经常被创建 以编程方式使用新的 运算符,或通过 ORM 工具作为 数据库查询的结果。

    它正在接近巫毒,这种东西,它只适用于某些应用服务器,但它可能是适合你的工具。

    【讨论】:

      【解决方案2】:

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

      【讨论】:

      • 在网络应用程序中执行此操作的最佳做​​法是什么?动态创建的域对象需要以这种方式访问​​ Spring bean 是否常见?
      • 我建议查看 WebApplicationContextUtils:static.springsource.org/spring/docs/3.0.0.RC1/javadoc-api。我认为让 Spring 管理 bean 更为常见。只有方法调用上下文中的本地 bean 才能使用“new”创建。
      【解决方案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 的 bean 定义 xml。然后使用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 章节中也对使用这种方法进行了很好的描述。

      【讨论】:

        【解决方案4】:

        创建工厂并注册到spring,使用工厂创建域对象而不是使用'new'

        在这种情况下,您的 DomainObjFactory 拥有所有可用的东西

        【讨论】:

          【解决方案5】:

          略相关question

          您可以采用与 Hibernate 类似的策略,在您的域实例工厂中为拦截器创建设施。您可以将所需的服务注入到 Spring 托管的拦截器中,这些拦截器将注入到您的域工厂中

          这将使您的应用程序与 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>
          

          【讨论】:

            【解决方案6】:

            您可以使用@duffymo 建议的方法,但如果您没有将 Spring 作为 Web 应用程序运行,您应该查看此 SO entry。看到实用程序类在最流行的答案中是线程安全的。顺便说一句,你应该得到所有你需要的 OP 和答案,然后你可以使用这个实用程序类来获取对 Spring 托管 bean 的引用。

            【讨论】:

              【解决方案7】:

              一种解决方案是使用返回 Spring 应用程序竞赛的全局 static 方法(请参阅 BeanLocator。)

              其他选项可能是让您的业务对象实现ApplicationContextAware 接口。在实例化时,您的“集成”代码必须将 Spring ApplicationContext 注入动态创建的 bean(可能通过检查该类是否实现 ApplicationContextAware。)这当然会将您的业务代码与 Spring 绑定,但第一个选项会是一样的。

              一种变体是不直接注入ApplicationContext,而是重用Spring @Autowired 注解。然后,“集成”代码将只注入带注释的字段。

              【讨论】:

                【解决方案8】:

                您可以使用静态 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>
                

                【讨论】:

                  猜你喜欢
                  • 2020-04-14
                  • 2012-08-02
                  • 1970-01-01
                  • 2013-06-19
                  • 2014-06-28
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2021-03-12
                  相关资源
                  最近更新 更多