web-dev-qa-db-fra.com

Spring @autowired ne concerne-t-il pas les conteneurs non-singleton?

J'ai une classe MyTask qui implémente Runnable et il peut y avoir beaucoup d'objets de ce type instanciés à un moment donné. Il y a certaines propriétés que j'aimerais transférer automatiquement dans la classe MyTask.

Mais je pense que si je marque MyTask avec @Component, alors il deviendra un singleton géré par un ressort correct? Ce n'est pas ce que je veux, j'ai besoin que plusieurs instances indépendantes de cette classe soient exécutées par un TaskExecutor.

Donc ma question (s):

  • a) Suis-je fondamentalement erroné dans ma compréhension de l'annotation @Component? Ne fait-il pas que MyTask devienne un singleton géré par un ressort?
  • b) Y a-t-il une autre annotation que je devrais utiliser pour que spring détecte @Autowired et injecte la propriété?
  • c) Le câblage automatique de printemps n'est-il pas destiné à des conteneurs/classes non singleton tels que MyTask?

Mise à jour # 1 - Ceux-ci ne fonctionnent pas:

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

Mise à jour # 2 - En attendant d'autres suggestions sur la façon de le faire facilement, je cherche: Using AspectJ to dependency inject domain objects with Spring comme alternative.

9
pulkitsinghal

d’abord, les haricots déclarés avec @Component et ramassés par le balayage des composants de printemps deviendront un singleton à gestion de printemps par défaut

Je n'ai aucune idée de la façon dont vous utilisez MyTask, mais il est excessif d'utiliser AspectJ dans votre situation et il n'a pas beaucoup de sens de déclarer MyTask comme un haricot géré par un ressort. une autre façon de faire sera:

  1. définir MyTask en tant que classe Java simple et ajouter un constructeur pour initialiser la dépendance blah

  2. autowire blah dans lequel vous utilisez MyTask et instanciez un objet MyTask chaque fois que vous souhaitez exécuter une tâche comme suit:

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

L'annotation @Component leur permettrait de se supprimer automatiquement lors de l'analyse du chemin d'accès aux classes à l'aide du contexte: analyse du composant C'est ce qu'il fait. la ligne est mince entre @Service et @Component et, dans ce cas, cela n’a aucune incidence. 

Le câblage automatique à ressort peut être effectué pour les prototypes ainsi que pour les portées uniques. Dans le cas de la portée du prototype, les rappels de cycle de vie pour la destruction du bean ne sont pas appelés. 

Cela s’explique très bien sur la page de documentation de Spring. http://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch04s04.html

Je ne vois pas une raison pour laquelle tout ce que vous avez mentionné ne devrait pas fonctionner. 

C'est un échantillon de ce que j'ai essayé de faire pour mieux l'expliquer. 

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

   }
}

C'est le point de départ de l'application. 

Voici votre classe 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;
   }

}

Deux autres classes pour montrer comment fonctionne le prototype

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

}

Un processeur de haricot qui vous montrera comment les objets sont créés

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

votre classe SomeSpringConfig

@Service
public class SomeSpecialSpringConfiguredConnectionClass {

   private String value = "someValue";

   public String getValue() {
      return value;
   }

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

}

Lorsque vous exécutez cet exemple, vous remarquerez que la sortie de la console est

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

Si vous remarquez attentivement, il y a 2 objets de myTask avec des codes de hachage différents. 

Si vous modifiez la portée de la tâche myTask en "Singleton", le résultat sera affiché. 

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

Dans ce cas, un objet est créé pour "ma tâche".

Est-ce que cela aide?

8
Hrishikesh

Ajouter typiquement @Scope ("prototype") ne devrait pas causer d'erreur nulle pour le bean blown autowired, vous devriez vérifier comment vous instanciez le bean MyTask. Je pense que le problème est que vous instanciez manuellement MyTask comme :

   MyTask task = new MyTask();

et par conséquent il échappe au contrôle de Spring, c’est pourquoi sa dépendance, blah bean, est nulle, au lieu de l’instanciation manuelle, vous devez autoriser le transfert automatique et laisser Spring prendre en charge ses dépendances, alors blah ne sera pas nul. Mais il y a un autre problème. Le câblage automatique d'un haricot prototype, MyTask, dans un autre objet singleton est erroné. Le conteneur Spring crée un bean singleton une seule fois et ne définit donc le bean prototype qu'une seule fois, ce qui empêche la portée du prototype de fonctionner. Comme ci-dessous, Myactivity est un singleton autowires MyTask, j'ai également ajouté un constructeur pour que MyTask imprime quelque chose nouvelle instance de MyTask est créée. Dans le cas ci-dessous, il n’imprime qu’une fois. Le prototype ne fonctionne donc pas.

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

Basé sur Spring AOP Scoped proxies J'ai remplacé @Scope ("prototype") en @Scope (proxyMode = ScopedProxyMode.TARGET_CLASS, value = "prototype") De sorte que le proxy détecté injecte une nouvelle instance de MyTask. chaque fois que le bean singleton MyActivity est appelé.

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

et maintenant le prototype fonctionne bien et voici le résultat imprimé en console:

New Instance of MyTask
New Instance of MyTask
New Instance of MyTask
New Instance of MyTask
New Instance of MyTask
1
ShayneR

Au lieu de @Autowire, utilisez @Inject et voyez la magie. J'ai la même situation, où, dans, une classe Validator est une classe Java Singleton et non un bean spring. Je dois injecter un haricot de printemps du client UAA fourni par une autre équipe. Donc, @Autowire n'a pas fonctionné, mais @Inject a fonctionné.

0
Yoga Gowda