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 @Autowired
MyRepository
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
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
ouListableBeanFactory
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.
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.
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:
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.
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);
}
}
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.
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.