Spring @autowired 是否不适用于非单例容器?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20873404/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 06:26:25  来源:igfitidea点击:

Is Spring @autowired not meant for non-singleton containers?

springsingletonspring-aopautowired

提问by pulkitsinghal

I have a MyTaskclass which implements Runnableand there can be many such objects instantiated at any given moment. There are certain properties that I would like to autowire into MyTaskclass.

我有一个MyTask实现类,Runnable并且可以在任何给定时刻实例化许多这样的对象。我想将某些属性自动装配到MyTask类中。

But I thinkthat if I mark MyTaskwith @Componentthen it will become a spring-managed singleton correct? That's not what I want, I need many independent instances of this class to be run by a TaskExecutor.

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

So my question(s):

所以我的问题:

  • a) Am I fundamentally wrong in my understanding of @Componentannotation? Does it NOT make MyTaskinto a spring-managed singleton?
  • b) Is there some other annotation I should use so that spring detects @Autowiredand injects the property?
  • c) Is spring autowiring not meant for non-singleton containers/classes like MyTask?
  • a) 我对@Component注释的理解从根本上是错误的吗?它不会MyTask成为弹簧管理的单身人士吗?
  • b) 是否还有其他一些我应该使用的注释,以便 spring 检测@Autowired并注入该属性?
  • c) spring 自动装配不适用于非单例容器/类MyTask吗?


Update # 1 - These don't work:

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

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
    }
}


Update # 2 - While waiting for some more suggestions on how to do it the easy way, I'm looking into: Using AspectJ to dependency inject domain objects with Springas an alternative.

更新#2 - 在等待更多关于如何以简单的方式做到这一点的建议时,我正在研究:Using AspectJ to dependency inject domain objects with Spring作为替代方案。

采纳答案by Septem

first, beans declared with @Component and picked up by spring component scan will become a spring-managed singleton by default.

首先,使用@Component 声明并由 spring 组件扫描拾取的 bean 将默认成为 spring 管理的单例

I have no idea how you use MyTask, but it is overkilled to use AspectJ in your situation, and it does not make much sense to declare MyTask as a spring-managed bean. another way of doing this will be:

我不知道您如何使用 MyTask,但是在您的情况下使用 AspectJ 是过度的,并且将 MyTask 声明为 spring 管理的 bean 没有多大意义。另一种方法是:

  1. define MyTask as a plain java classand add a constructor to initialize the dependency blah

  2. autowire blahin where you use MyTask, and instantiate a MyTask object every time you want to execute a task as follow:

    //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))
    
  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))
    

回答by Hrishikesh

The @Component annotation would allow them for auto detetion while classpath scanning using the context:component scan That is what it does. there is a fine line between @Service and @Component, and in this case it does not affect in anyway.

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

Spring autowiring can be done for prototype as well as singleton scopes. In case of the prototype scope though the lifecycle callbacks for the destruction of the bean isnt called.

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

It is explained very well on the Spring documentation page. http://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch04s04.html

它在 Spring 文档页面上有很好的解释。 http://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch04s04.html

I dont see a reason why whatever mentioned by you shouldnt work.

我看不出您提到的任何内容都不起作用的原因。

He is a working sample of what i tried to do to explain it better.

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

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();

   }
}

This is the starting point of the app.

这是应用程序的起点。

Here is your myTask class

这是你的 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;
   }

}

Two other classes to show how the prototype scope is working

另外两个类来展示原型作用域是如何工作的

@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;
   }

}

A BeanPostprocessor which will show you how the objects are getting created

一个 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;
   }
}

your SomeSpringConfig class

你的 SomeSpringConfig 类

@Service
public class SomeSpecialSpringConfiguredConnectionClass {

   private String value = "someValue";

   public String getValue() {
      return value;
   }

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

}

When you run this sample, you will notice that the output on the console is

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

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

If you notice carefulyy there are 2 objects of myTaskwith different hashcodes.

如果您仔细注意到myTask 的2 个对象具有不同的哈希码。

If you change the scope of the myTask to "Singleton" here would be the output.

如果您将 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

In this case there is one object created for "myTask"

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

Does this help?

这有帮助吗?

回答by ShayneR

Typically adding @Scope("prototype")should not cause null error for autowired blah bean,you should check how you instantiate MyTask bean. I think the problem is that you manually instantiate MyTask like:

通常,添加@Scope("prototype")不应导致自动装配的 blah bean 出现空错误,您应该检查如何实例化 MyTask bean。我认为问题在于您手动实例化 MyTask ,例如:

   MyTask task = new MyTask();

and therefor it goes out of Spring's control that is why its dependency, blah bean, is null , instead of manual instantiation, you need to autowire it and let Spring takes care of its dependencies then blah willnot be null. But then there is another issue. Autowiring a prototype bean, MyTask, whithin another singleton object is wrong. Spring container creates a singleton bean only once, and thus only sets the prototype bean once and this causes the prototype scope not to work.Like below, Myactivity is a singleton which autowires MyTask, I also added a constructor for MyTask to print something once a new instance of MyTask gets created. In below case it only prints once, therefore prototype is not working.

因此它超出了 Spring 的控制,这就是为什么它的依赖项 blah bean 为 null ,而不是手动实例化,您需要自动装配它并让 Spring 处理其依赖项,那么 blah 就不会为 null。但是还有另一个问题。在另一个单例对象中自动装配原型 bean MyTask 是错误的。Spring 容器只创建一次单例 bean,因此只设置一次原型 bean,这会导致原型作用域不起作用。 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();
    }
  }
 }

Based on Spring AOP Scoped proxiesI changed @Scope("prototype") to @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS, value = "prototype") so the scoped proxy injects a new instance of MyTask every time singleton MyActivity bean gets called.

基于Spring AOP Scoped 代理,我将 @Scope("prototype") 更改为 @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS, value = "prototype") 所以每次调用单例 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);
  }
}

and now the prototype is working fine and this was the result printed in console:

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

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

回答by Yoga Gowda

Instead of @Autowire, use @Inject and see the magic. I have the same situation, where in, a Validator class is Java Singleton class and not spring scoped bean. I need to inject a UAA Client spring bean provided by another team. So @Autowire didn't work, but @Inject did work.

而不是@Autowire,使用@Inject 并看到魔法。我有同样的情况,其中 Validator 类是 Java Singleton 类而不是 spring 范围的 bean。我需要注入另一个团队提供的 UAA Client spring bean。所以@Autowire 不起作用,但@Inject 起作用了。