Spring @autowired不适用于非单件容器吗?

时间:2014-01-01 21:22:09

标签: spring singleton spring-aop autowired

我有一个实现MyTask的{​​{1}}类,并且可以在任何给定时刻实例化许多此类对象。我希望将某些属性自动装入Runnable类。

但我认为如果我用MyTask标记MyTask那么它将成为一个由春季管理的单身人士正确吗?这不是我想要的,我需要由TaskExecutor运行这个类的许多独立实例。

所以我的问题:

  • a)我对@Component注释的理解基本上是错误的吗?是不是将@Component变为弹簧管理的单身人士?
  • b)我应该使用其他注释,以便spring检测到MyTask并注入属性吗?
  • c)弹簧自动装配不适用于非单件容器/类@Autowired吗?

更新#1 - 这些不起作用:

MyTask

更新#2 - 在等待更多关于如何做到这一点的建议时,我正在研究:public class MyTask implements Runnable { // I want this class to be non-singleton @Autowired public SomeSpecialSpringConfiguredConnectionClass blah; // this is the singleton bean that should be injected @Override public void run() { // BLAH IS NULL, this shouldn't be NULL, that is not what I want // which makes sense considering Spring never knew it had to work // on this class } } @Component public class MyTask implements Runnable { // I want this class to be non-singleton @Autowired public SomeSpecialSpringConfiguredConnectionClass blah; // this is the singleton bean that should be injected @Override public void run() { // this works BUT now MyTask is singleton :( } } @Component @Scope("prototype") public class MyTask implements Runnable { // I want this class to be non-singleton @Autowired public SomeSpecialSpringConfiguredConnectionClass blah; // this is the singleton bean that should be injected @Override public void run() { // BLAH IS NULL, again ... this shouldn't be NULL, that is not what I want } } 作为替代方案。

4 个答案:

答案 0 :(得分:8)

@Component注释允许它们在使用context:component scan进行类路径扫描时进行自动检测 这就是它的作用。 @Service和@Component之间有一条细线,在这种情况下它无论如何都不会影响。

可以为原型和单例范围完成Spring自动装配。在原型范围的情况下,虽然没有调用bean的销毁的生命周期回调。

在Spring文档页面上对此进行了很好的解释。 http://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch04s04.html

我没有看到为什么你提到的任何东西不应该工作的原因。

他是我试图更好地解释它的工作样本。

public class SpringContainerStartClass {

   public static void main(final String[] args) {
      final ClassPathXmlApplicationContext bf = new ClassPathXmlApplicationContext("beans.xml");
      final MainApplication1 bean = (MainApplication1) bf.getBean("mainApplication1");
      bean.getMyTask().printSomething();

   }
}

这是该应用的起点。

这是你的myTask类

@Component(value = "myTask")
@Scope(value = "prototype")
public class MyTask
      implements Runnable {

   @Autowired
   private SomeSpecialSpringConfiguredConnectionClass someSpringObject;

   @Override
   public void run() {
      System.out.println("running now");

   }

   public void printSomething() {
      System.out.println(someSpringObject.getValue());
   }

   public SomeSpecialSpringConfiguredConnectionClass getSomeSpringObject() {
      return someSpringObject;
   }

   public void setSomeSpringObject(final SomeSpecialSpringConfiguredConnectionClass someSpringObject) {
      this.someSpringObject = someSpringObject;
   }

}

显示原型范围如何工作的另外两个类

@Component
public class MainApplication1 {

   @Autowired
   private MyTask myTask;

   public MyTask getMyTask() {
      return myTask;
   }

   public void setMyTask(final MyTask myTask) {
      this.myTask = myTask;
   }

}

@Component
public class MainApplication2 {

   @Autowired
   private MyTask myTask;

   public MyTask getMyTask() {
      return myTask;
   }

   public void setMyTask(final MyTask myTask) {
      this.myTask = myTask;
   }

}

BeanPostprocessor,它将向您展示如何创建对象

public class InstantiationTracingBeanPostProcessor
      implements BeanPostProcessor {

   @Override
   public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException {
      return bean;
   }

   @Override
   public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
      System.out.println("Bean '" + beanName + "' created : " + bean.toString());
      return bean;
   }
}

你的SomeSpringConfig类

@Service
public class SomeSpecialSpringConfiguredConnectionClass {

   private String value = "someValue";

   public String getValue() {
      return value;
   }

   public void setValue(final String value) {
      this.value = value;
   }

}

运行此示例时,您会注意到控制台上的输出是

INFO: Loading XML bean definitions from class path resource [beans.xml]
Jan 02, 2014 12:07:15 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@26efabf8: defining beans [mainApplication1,mainApplication2,myTask,someSpecialSpringConfiguredConnectionClass,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,com.stackoverflow.DIQuestion.InstantiationTracingBeanPostProcessor#0,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root of factory hierarchy
Bean 'someSpecialSpringConfiguredConnectionClass' created : com.stackoverflow.DIQuestion.SomeSpecialSpringConfiguredConnectionClass@1e20d04b
Bean 'myTask' created : com.stackoverflow.DIQuestion.MyTask@175d6331
Bean 'mainApplication1' created : com.stackoverflow.DIQuestion.MainApplication1@741b31f2
Bean 'myTask' created : com.stackoverflow.DIQuestion.MyTask@2c2815d3
Bean 'mainApplication2' created : com.stackoverflow.DIQuestion.MainApplication2@7bb0e64a

如果您注意到carefulyy,则有两个 myTask 对象具有不同的哈希码。

如果你将myTask的范围更改为“Singleton”,那么这将是输出。

INFO: Loading XML bean definitions from class path resource [beans.xml]
Jan 02, 2014 12:08:35 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@26efabf8: defining beans [mainApplication1,mainApplication2,myTask,someSpecialSpringConfiguredConnectionClass,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,com.stackoverflow.DIQuestion.InstantiationTracingBeanPostProcessor#0,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root of factory hierarchy
Bean 'someSpecialSpringConfiguredConnectionClass' created : com.stackoverflow.DIQuestion.SomeSpecialSpringConfiguredConnectionClass@1e20d04b
Bean 'myTask' created : com.stackoverflow.DIQuestion.MyTask@175d6331
Bean 'mainApplication1' created : com.stackoverflow.DIQuestion.MainApplication1@741b31f2
Bean 'mainApplication2' created : com.stackoverflow.DIQuestion.MainApplication2@2c2815d3

在这种情况下,为“myTask”创建了一个对象

这有帮助吗?

答案 1 :(得分:6)

首先,使用@Component声明并通过spring组件扫描获取的bean将默认成为弹簧管理的单例

我不知道你如何使用MyTask,但在你的情况下使用AspectJ是过分的,将MyTask声明为一个弹簧管理的bean没有多大意义。另一种方法是:

  1. 将MyTask定义为普通java类并添加构造函数以初始化依赖项blah

  2. autowire blah 在您使用MyTask的地方,并在每次要执行任务时实例化MyTask对象,如下所示:

    //autowire the dependency of MyTask in another spring bean with default singleton scope
    @Autowired private SomeSpecialSpringConfiguredConnectionClass blah
    //create task and wire the blah yourself
    executor.submit(new MyTask(blah))
    

答案 2 :(得分:1)

通常添加 @Scope(" prototype")不应该对自动装配的blah bean造成null错误,你应该检查你如何实例化MyTask bean。 我认为问题是您手动实例化MyTask,如:

   MyTask task = new MyTask();

因此它不受Spring控件的影响,这就是为什么它的依赖,blah bean为null,而不是手动实例化,你需要自动装配它,让Spring处理它的依赖关系然后blah将不会为null 。 但是还有另一个问题。在另一个单例对象中自动装配原型bean MyTask是错误的。 Spring容器只创建一个singleton bean,因此只设置一次原型bean,这会导致原型范围无法工作。如下所示,Myactivity是一个自动装配MyTask的单例,我还为MyTask添加了一个构造函数来打印一次创建了MyTask的新实例。在下面的情况下,它只打印一次,因此原型不起作用。

@Component
@Scope("prototype")
public class MyTask implements Runnable {

  @Autowired
  public SomeSpecialSpringConfiguredConnectionClass blah;

  public MyTask(){
    System.out.println("New Instance of MyTask");
  }

  @Override
  public void run() {
    assert(blah != null);
  }
}


@Component
public class MyActivity {

  @Autowired
  private MyTask task;

  public MyTask start() {
    return task;
  }
}

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = {MyActivity.class, MyTask.class, 
   SomeSpecialSpringConfiguredConnectionClass.class})
public class MyTaskTest {

  @Autowired
  private MyActivity activity;

  @Test
  public void testActivity() {
    for (int i = 0; i < 5; i++) {
        MyTask task = activity.start();
        task.run();
    }
  }
 }

基于Spring AOP Scoped proxies我将@Scope(&#34;原型&#34;)更改为@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS,value =&#34; prototype&#34;) 因此,每次调用单例MyActivity bean时,作用域代理都会注入一个新的MyTask实例。

@Component
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS, value = "prototype")
public class MyTask implements Runnable {

  @Autowired
  public SomeSpecialSpringConfiguredConnectionClass blah;

  public MyTask(){
    System.out.println("New Instance of MyTask");
  }

  @Override
  public void run() {
    assert(blah != null);
  }
}

现在原型工作正常,这是在控制台中打印的结果:

New Instance of MyTask
New Instance of MyTask
New Instance of MyTask
New Instance of MyTask
New Instance of MyTask

答案 3 :(得分:0)

使用@Inject而不是@Autowire,看看魔术。 我有相同的情况,其中,Validator类是Java Singleton类而不是spring scoped bean。我需要注入另一个团队提供的UAA Client spring bean。所以@Autowire没有工作,但@Inject确实有效。