【问题标题】:Intentionally setting a Spring bean to null故意将 Spring bean 设置为 null
【发布时间】:2010-01-29 16:04:27
【问题描述】:

我正在使用 Spring 将 JMS 连接工厂注入到我的 Java 应用程序中。由于这个工厂只需要在生产环境中,而不是在我开发时,我将 bean 定义放入一个单独的 XML 中,我将它包含在我的主 applicationContext.xml 中。在生产环境中,这个额外的文件包含常规的 bean 定义。在我的本地开发环境中,我希望这个 bean 为空。当 Spring 遇到它不知道的引用 ID 时,尝试简单地删除 bean 定义显然会导致错误。

所以我尝试创建一个只会返回 null 的工厂 bean。如果我这样做,Spring (2.5.x) 会抱怨工厂返回 null,尽管基于 FactoryBean 接口的 Spring API 文档,我希望这可以工作(请参阅Spring API doc)。

XML 看起来像这样:

<bean id="jmsConnectionFactoryFactory" class="de.airlinesim.jms.NullJmsConnectionFactoryFactory" />

<bean id="jmsConnectionFactory" factory-bean="jmsConnectionFactoryFactory" factory-method="getObject"/>

这样做的“正确”方法是什么?

【问题讨论】:

    标签: java spring


    【解决方案1】:

    对于任何提出这个问题的人,请记住,只需将 @Autowired 注释设置为可选就可以解决问题(即,如果没有找到符合条件的 bean,Spring 会将引用保留为空)。

    @Autowired(required = false)
    private SomeClass someBean
    

    请注意,您必须在引用 bean 的任何地方都这样做,这可能比创建上面提到的空工厂更麻烦。

    【讨论】:

      【解决方案2】:

      对于遇到此问题的其他人:如果您使用 Java 8,另一种方法是使用 Supplier 功能接口来包装可能为空的 bean:

      @Bean
      @Scope("singleton")
      public Supplier<SomeBean> getSomeBean() {
        SomeBean myBean = null;  // or can set to a SomeBean instance
        return () -> myBean;
      }
      

      使用 @Autowired 构造函数注入使用这个看起来像:

      private SomeBean someBean;
      
      @Autowired
      SomeService(Supplier<SomeBean> someBeanSupplier) {
          this.someBean = someBeanSupplier.get();
      }
      

      那么SomeService中的someBean字段可以为null也可以为非null。

      【讨论】:

        【解决方案3】:

        我很确定 Spring 不允许您将 null 与 bean id 或别名相关联。您可以通过将属性设置为 null 来处理此问题。

        这是您在 Spring 2.5 中的做法

        <bean class="ExampleBean">
            <property name="email"><null/></property>
        </bean>
        

        在 Spring 3.0 中,您应该还可以使用Spring expression language (SpEL);例如

        <bean class="ExampleBean">
            <property name="email" value="#{ null }"/>
        </bean>
        

        或任何计算结果为 null 的 SpEL 表达式。

        如果您使用的是占位符配置器,您甚至可以这样做:

        <bean class="ExampleBean">
            <property name="email" value="#{ ${some.prop} }`"/>
        </bean>
        

        some.prop 可以在属性文件中定义为:

        some.prop=null
        

        some.prop=some.bean.id
        

        【讨论】:

        • 我更喜欢这个解决方案;它不需要创建自定义工厂 bean
        • 伟大的收获!是否有确认 Spring 不将“null”视为有效 bean 值的源/文档链接?我有一个 Spring @Configuration 类,其中包含一个返回 @Bean 的方法。但是该方法在返回实例之前会进行一些检查。当检查失败时,它返回 null 并导致UnsatisfiedDependencyException
        • 当您共享引用具有名称的 bean 的定义但您希望该 bean 为空时,这不是一个好的解决方案
        【解决方案4】:

        factory-bean/factory-method 不适用于 null,但自定义 FactoryBean 实现可以正常工作:

        public class NullFactoryBean implements FactoryBean<Void> {
        
            public Void getObject() throws Exception {
                return null;
            }
        
            public Class<? extends Void> getObjectType() {
                return null;
            }
        
            public boolean isSingleton() {
                return true;
            }
        }
        
        <bean id="jmsConnectionFactory" class = "com.sample.NullFactoryBean" />
        

        【讨论】:

        • 有一个开放的功能请求,但已经坐了 2 年...投票可能会有所帮助:jira.springframework.org/browse/SPR-5320
        • 如果我在 @Autowired 上下文中使用 bean,这对我不起作用。
        • 如果我返回 null,我会得到 BeanCreationException。原因:org.springframework.beans.factory.BeanCreationException:创建类路径资源[XXX.xml]中定义的名称为'nullObject'的bean时出错:factory-bean'NullFactoryBean'返回null
        【解决方案5】:

        你能利用特殊的&lt;null&gt; bean 元素吗?例如

        <bean class="ExampleBean">
        <property name="email"><null/></property>
        </bean>
        

        来自the doc,第 3.3.2.5 节

        【讨论】:

        • 这在设置最终应用程序 bean 的属性时可能会起作用。但在这一点上,我只提供参考 id “jmsConnectionFactory”,然后它应该引用实际的 bean 或 null(如果可能的话)。
        【解决方案6】:

        在测试中也可以像这样注入空 bean:

        @RunWith(SpringJUnit4ClassRunner.class)
        @ContextConfiguration(classes = NullTest.class)
        @Configuration
        public class NullTest {
        
            @Bean(name = "em")
            public IEntityManager em() { return null; }
            @Bean
            public PlatformTransactionManager tm() { return null; }
        
            @Resource
            private SomeBean someBean; // this would get em and tm fields autowired to nulls
        

        【讨论】:

          【解决方案7】:

          上面提到的一些,axtact 的答案在自动装配上下文中不起作用,其中 Spring 将依赖来自 getObjectType() 方法的正确信息。所以你最终可能会遇到如下错误:

          原因:org.springframework.beans.factory.NoSuchBeanDefinitionException:没有为依赖找到[xxxxxxxxxxxxx]类型的匹配bean:预计至少有1个bean有资格作为此依赖的自动装配候选者。依赖注解:{@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value=yyyyyyyyyyyyyyyy)} 在 org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:920) 在 org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:789) 在 org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:703) 在 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotatio

          所以这里有一个小的变化,它涉及允许用户在构造时强制对象类型。使用属性而不是构造函数参数不起作用,因为 Spring 并未在此上下文中完全初始化 bean。

          public class NullFactoryBean implements FactoryBean {
              private final Class<?> objectType;
          
              public NullFactoryBean(Class<?> objectType) {
                  this.objectType = objectType;
              }
          
              @Override
              public Object getObject() throws Exception {
                  return null;
              }
          
              @Override
              public Class<?> getObjectType() {
                  return objectType;
              }
          
              @Override
              public boolean isSingleton() {
                  return false;
              }
          }
          

          【讨论】:

            猜你喜欢
            • 2017-09-21
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-08-23
            • 2017-11-28
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多