2

我知道可以在 Spring 中将请求范围的 bean 注入到单例 bean 中,所以我知道我正在尝试做的事情会起作用,我只是想知道是否有一种方法可以更简洁地表达它,而无需那么多额外的不必要的类定义。我是 Spring 注释的新手,所以也许有一个我不知道的注释。

我有一个抽象类,它将在我的应用程序中作为不同的单例 spring bean 扩展 100 次。以这个类定义为例:

/** The abstract class with a field that needs to be request-specific **/
public abstract class AbstractSingletonBean {

    private SampleState state;
    public SampleState getState() { return state; }
    public void setState(SampleState state) { this.state = state; }

    // Other fields that are just singleton here
}

还有一个 bean 定义可能是什么样子的示例:

@Component
public class SampleSingletonBean extends AbstractSingletonBean {

    @Resource(name="sampleState")
    public void setState(SampleState state) { super.setState(state); }
}

现在我们当然需要一个名为sampleState. 所以我必须创建两个类:一个定义字段的基类,SampleState然后是一个请求范围的 bean 定义。这是因为每个扩展AbstractSingletonBean都需要它自己的请求范围的状态字段实例。

这里可能是基类:

public class SampleState {
    private String fieldOne;
    public String getFieldOne() { return fieldOne }
    public void setFieldOne() { this.fieldOne = fieldOne }
}

这是这个愚蠢的 bean 定义:

@Component ("sampleState")
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class SampleStateBean extends SampleState {}

困扰我的事情是,如果我有 100 个扩展AbstractSingletonBean,我将需要 100 个扩展,SampleStateBean仅使用样板代码才能使其具有请求范围。有没有办法setState()在扩展中覆盖AbstractSingletonBean并向spring指示它应该动态创建一个新的请求范围bean并将其注入这里?所以我的SampleSingletonBean可能看起来像这样:

@Component
public class SampleSingletonBean extends AbstractSingletonBean {

    @Resource
    @Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
    public void setState(SampleState state) { super.setState(state); }
}

当然这不起作用,因为@Resource需要引用一个已经存在的bean。是否有另一个注释可以在不为每个SampleStatebean 创建新类的情况下完成此操作?

4

3 回答 3

1

Spring 也可以注入抽象类。因此,如果每个后代只需要一个(如您的示例中所示),您可以将注入移至SampleState抽象类。AbstractSingletonBeanSampleState

于 2013-07-04T03:05:51.280 回答
1

看起来这不是开箱即用的,所以我创建了一个我称为 @AnonymousRequest 的注释,我将它放在我想要的字段上,并创建一个 BeanDefinitionRegistryPostProcessor 来完成创建 bean 的工作。它基本上是这样的:

for each bean in the BeanFactory
  if bean class has AnonymousRequest annotation
    create request scoped bean from field class
    create singleton bean to be request scoped bean wrapper
    set the annotated property value to the singleton wrapper

这需要大量工作才能弄清楚 Spring 如何注册请求范围的 bean。您创建所需的 bean 定义作为请求范围的 bean。然后,您创建一个 RootBeanDefinition 类型的单例 bean,它充当请求范围 bean 的包装器,并在包装​​器上设置一个名为“targetBeanName”的属性,将其命名为您命名的请求范围 bean(“scopedTarget”+按照约定的单例 bean 名称) )。

因此,真正了解这些东西的人可能会改进这一点,但这就是我想出的:

  public void createRequestBeanFromSetterMethod(String containingBeanName, BeanDefinition containingBean, Method method, BeanDefinitionRegistry registry)
  {
    String fieldName = ReflectionUtil.getFieldNameFromSetter(method.getName());
    String singletonBeanName = containingBeanName+"_"+fieldName;
    String requestBeanName = "scopedTarget."+singletonBeanName;

    BeanDefinition requestBean = createAnonymousRequestBean(ReflectionUtil.getFieldTypeFromSetter(method), containingBean);

    RootBeanDefinition singletonBean = new RootBeanDefinition();
    singletonBean.setBeanClass(ScopedProxyFactoryBean.class);
    singletonBean.getPropertyValues().addPropertyValue("targetBeanName", requestBeanName);

    registry.registerBeanDefinition(singletonBeanName, singletonBean);
    registry.registerBeanDefinition(requestBeanName, requestBean);

    beanDefinition.getPropertyValues().addPropertyValue(fieldName, new RuntimeBeanReference(singletonBeanName));

  }

  private BeanDefinition createAnonymousRequestBean(Class<?> beanType, BeanDefinition parentBean)
  {
    BeanDefinition newBean = null;
    if (parentBean != null)
    {
      newBean = new GenericBeanDefinition(parentBean);
    }
    else
    {
      newBean = new GenericBeanDefinition();
    }

    if (beanType != null)
    {
      newBean.setBeanClassName(beanType.getName());
    }

    newBean.setScope("request");
    newBean.setAutowireCandidate(false);

    // This would have come from the Proxy annotation...could add support for different values
    String proxyValue = "org.springframework.aop.framework.autoproxy.AutoProxyUtils.preserveTargetClass";
    BeanMetadataAttribute attr = new BeanMetadataAttribute(proxyValue, true);
    newBean.setAttribute(proxyValue, attr);

    return newBean;
  }

它似乎工作!我现在有效地在上下文初始化之前创建了一个请求范围的 bean,该 bean 本地化到这个包含 bean。它或多或少是一个请求范围的属性。

于 2013-07-19T18:43:48.897 回答
0

您可以尝试定义单个 SampleState 请求范围 bean,然后使用 spring 的查找方法将这个 bean 注入到您想要的任何位置。这对于原型范围 bean 来说效果很好。手指交叉它也适用于请求范围。

AFAIK,到目前为止还没有对查找方法的注释支持,所以要么使用它的 xml vis-a-vis,要么在这里查看 javax.inject.Provider 相关问题

于 2013-07-04T01:11:11.060 回答