web-dev-qa-db-fra.com

Spring @Autowired sur une nouvelle instance de classe

Je ne suis pas si familier avec Spring et j'ai la situation suivante:

Une classe de référentiel:

@Repository
public class MyRepository {
    // ...
}

Une classe qui utilise la classe repository:

public class MyClass extends AbstractClass {

    @Autowired
    private MyRepository myRepository;

    //...
}

Je sais que si j'annote ma MyClass avec @Component et que je l'utilise avec un @Autowired, le @AutowiredMyRepository est alors résolu correctement. Le problème est que je suis dans une situation où je dois créer de nouvelles instances de MyClass avec réflexion. Donc, MyRepository n'est jamais résolu et est nul tout le temps.

Est-il possible d'utiliser @Autowired dans cette situation?

Expliquant mieux ma situation: J'ai quelques implémentations de AbstractClass. Dans une phase d’installation de mon application, je crée un HashMap de ces implémentations. Fondamentalement:

{"MyClass", MyClass.class}
//...

Ensuite, j'ai un Controller générique qui correspond à l'url /{class}?options=... À l'aide du {class}@PathVariable, du HashMap ci-dessus et de la réflexion, je peux créer une instance d'une classe basée sur le options (cette partie est importante). Pensez-vous qu'il existe un meilleur moyen de procéder?

Merci d'avance

6
João Menighin

Spring lui-même offre certaines fonctionnalités pour l'auto-câblage dans vos objets Que vous avez créées avec new ou newInstance() ou autre.

Pour l'utiliser, vous avez besoin d'une AutowireCapableBeanFactory Que vous obtenez par l'injection de dépendance normale de Spring avec @Autowired.

@Autowired
private  AutowireCapableBeanFactory autowireCapableBeanFactory;

Ensuite, vous utilisez sa autowireBean(Object) method pour injecter les propriétés @Autowired dans votre bean.

Object myBean = map.get(className).newInstance();
autowireCapableBeanFactory.autowireBean(myBean);

Note de conception:

Réfléchissez bien si vous avez vraiment besoin de l'approche ci-dessus . Le javadoc de AutowireCapableBeanFactory déconseille l'utilisation de cette interface pour la plupart des cas d'utilisation:

Cette sous-interface de BeanFactory n'est pas destinée à être utilisée dans le code d'application normal: utilisez BeanFactory ou ListableBeanFactory pour les cas d'utilisation typiques.

Le code d'intégration pour d'autres infrastructures peut tirer parti de cette interface pour câbler et peupler des instances de bean existantes dont Spring ne contrôle pas le cycle de vie. Ceci est particulièrement utile pour les objets WebWork Actions et Tapestry Page, par exemple.

5
Thomas Fritsch

Une solution consiste à lier MyClass à la table de hachage pour lier une classe Factory. MyClassFactory. De cette façon, vous déléguerez la construction à une usine à béton qui sera chargée d'instancier la classe correcte et d'initialiser le référentiel approprié.

Voici un exemple:

{"MyClass", MyClassFactory.class}

La fabrique peut également être Composant, vous devez alors lier la table de hachage à l'instance de fabrique au lieu de la classe d'usine. Mais disons que ce n'est pas un composant:

//@Component   this is optional
    public MyClassFactory {
        //@Autowired optional
        ApplicationContext ctx;


       public MyClass createInstance() {
            MyRepository repo = ctx.getBean("")
            MyClass myclass = new MyClass(repo)
            return myclass;
       }
    }

Si vous le marquez comme composant, vous pouvez également utiliser l'interface ApplicationContextAware si vous souhaitez autoriser le transfert de ApplicationContext.

2
Alexandar Petrov

Vous pouvez utiliser Factory Design Pattern ici.

Cela peut sembler un peu compliqué au début, mais je suis sûr que vous allez l'adorer après l'avoir mis en œuvre.

Pas:

  1. Ajoutez @Component à toutes les implémentations de AbstractClass.
  2. Créer une classe d'usine en tant que:

    @Component
    public class MyFactory {
    
        private final Map<String, AbstractClass> impletationMap = new HashMap<>();
    
        @Autowired
        ApplicationContext context;
    
        @PostConstruct
        public void initialize() {
            populateDataMapperMap(context.getBeansOfType(AbstractClass.class).values().iterator());
        }
    
        private void populateDataMapperMap(final Iterator<AbstractClass> classIterator) {
            while (classIterator.hasNext()) {
                AbstractClass abstractClassImpl = (AbstractClass) classIterator.next();
                impletationMap.put(abstractClassImpl.getClass().getName(), abstractClassImpl);
    
            }
        }
    }
    

Lorsque le bean de cette classe MyFactory est initialisé, il recherche tous les beans de type AbstractClass et les met dans HashMap (implementationMap). 

Maintenant, à partir de cette usine, vous pouvez obtenir le HashMap, puis les implémentations selon vos besoins. Ce sera très facile lorsque vous ajouterez une nouvelle implémentation de AbstractClass, car l’usine en prendra soin.

1
Rishabh Agarwal

Essaye ça

@Component    
public class SomeClass extends AbstractClass {

  private static ApplicationContext applicationContext;

  public MyClass getMyClass(){
      // Now @Autowired MyRepository will work
      return applicationContext.getBean(MyClass.class);
  }

}
1
Maruthi Adithya

Une approche consiste à déclarer @Component au-dessus de MyClass

Ensuite, dans la phase d’installation, vous pouvez transmettre le instance de MyClass au lieu de MyClass.class lui-même, dans HashMap. Il n'y aura pas besoin de créer des instances via la réflexion.

Remarque: Vous pouvez récupérer l'instance de MyClass à partir de votre ApplicationContext lors de la phase d'installation.

1
Pankaj Singhal

Oui, vous pouvez annoter tous vos beans d'implémentation AbstractClass avec @Component et utiliser la déclaration suivante.

@Autowired
private List<AbstractClass> beans;

Vous pouvez ensuite convertir cela en une carte dans une méthode @PostConstruct.

Spring ne se plaindra pas des définitions en double si vous liez automatiquement des listes.

0
user10367961