如何防止Spring在上下文刷新时实例化原型范围的bean?

时间:2011-12-17 18:33:25

标签: spring

我在Spring XML描述符中使用原型范围的bean定义来配置默认属性(这些bean有许多属性),然后用getBean(beanName, ctorArgs[])调用应用程序上下文来创建实例。

bean定义需要2-3个构造函数参数,这些参数提供逻辑上唯一的键,用于JMX ObjectName的键属性等。另外,构造函数参数写入的变量是final。

我所看到的是,当应用程序上下文刷新时,它会尝试实例化这些原型,这看起来与原型要做的完全相反。它们是模板,而不是实际的实例。为了解决这个问题,我一直在使用伪造的ctor值配置原型,以便实例化这些伪造的bean实例,我只是在代码中过滤掉创建的MBean。

我的问题是,在我进行getBean调用之前,如何将应用程序上下文配置为注册这些原型bean定义,而不是实例化它们?

2 个答案:

答案 0 :(得分:7)

UPDATE:

问题比我最初想的要复杂一点。事实上,lazy是原型范围bean的默认行为。我挖了一点,我设法重现你的问题,找到解决方案。那么问题是什么?

您可能已启用<aop:scoped-proxy/>或(@ComponentScan(scopedProxy=...)等效)。在上下文刷新期间,Spring使用作用域代理包装原型bean(ClosedMetricSubscriberFeed)。它使用类代理,因为(a)选择类代理或(b)类没有接口。

基于类的代理基本上是bean的CGLIB子类,必须调用(由于JVM规则)基类的构造函数。 CGLIB生成的类总是调用no-arg构造函数。

我知道这听起来很复杂,这就是你能做的事情:

  1. 禁用<aop:scoped-proxy/>。就像那样。

  2. 提供一个虚拟的无参数构造函数并弃用它以防万一。不幸的是,你将不得不发现这样的虚假实例。请注意,在这种情况下,类的类型为:``。

  3. 从类中提取接口并使用接口作为作用域代理:

  4. @Scope(
      value = ConfigurableBeanFactory.SCOPE_PROTOTYPE,
      proxyMode = ScopedProxyMode.INTERFACES)
    

    旧答案:

    使用@Lazy注释或lazy-init="true"(参见参考文档中的4.4.4 Lazy-initialized beans)配置属性进行延迟初始化。

    <bean id="proto" class="MyPrototype" scope="prototype" lazy-init="true"/>
    

    或:

    @Service
    @Scope("prototype")
    @Lazy
    public class MyPrototype {/*...*/}
    

答案 1 :(得分:0)

我使用抛出IllegalStateException的私有的,不推荐的,无参数的构造函数。上下文加载正常,getBean()与构造函数args工作正常,而没有args的getBean()抛出异常。

package a;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component("myCommand")
@Scope("prototype")
public class Command {
  final protected String name;

  @Deprecated
  private Command() {throw new IllegalStateException("Only for Spring"); }

  public Command(String name) {
    super();
    this.name = name;
  }

  @Override
  public String toString() {
    return "Command [name=" + name + "]";
  }
}