Est-il possible de définir l'ordre d'instanciation au printemps?
Je ne veux pas utiliser @DependsOn
et je ne veux pas utiliser l'interface Ordered
. J'ai juste besoin d'un ordre d'instanciation.
L'utilisation suivante de l'annotation @Order
ne fonctionne pas:
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
/**
* Order does not work here
*/
public class OrderingOfInstantiation {
public static class MyBean1 {{
System.out.println(getClass().getSimpleName());
}}
public static class MyBean2 {{
System.out.println(getClass().getSimpleName());
}}
@Configuration
public static class Config {
@Bean
@Order(2)
public MyBean1 bean1() {
return new MyBean1();
}
@Bean
@Order(1)
public MyBean2 bean2() {
return new MyBean2();
}
}
public static void main(String[] args) {
new AnnotationConfigApplicationContext(Config.class);
}
}
Les haricots sont toujours instanciés dans l'ordre lexicographique.
Pourquoi ça ne marche pas ici?
Puis-je quand même compter sur l'ordre lexicographique?
METTRE À JOUR
Je souhaite toute solution permettant de fournir un ordre de création.
L'objectif est de renseigner les collections au niveau de la configuration dans le bon ordre. Depends on
- ne correspond pas à la tâche. Toute "explication" sur la raison pour laquelle Spring n'aime pas avoir d'instanciations ordonnées - ne correspond pas non plus à la tâche.
Commande signifie commande :)
De @Order
javadoc
REMARQUE: les commandes basées sur des annotations sont prises en charge pour des types de composants spécifiques uniquement - par exemple, pour les aspects AspectJ basés sur des annotations. Les stratégies de commande dans le conteneur Spring, en revanche, sont généralement basées sur l'interface Ordered afin de permettre la commande configurable par programme de chaque instance.
Donc, je suppose que le printemps ne suit tout simplement pas @Order()
lors de la création de haricots.
Mais si vous souhaitez simplement renseigner des collections, cela vous suffit peut-être:
import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;
import org.Apache.commons.lang3.Tuple.Pair;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import Java.lang.annotation.*;
import Java.util.ArrayList;
import Java.util.Collection;
import Java.util.List;
@Configuration
public class OrderingOfInstantiation {
public static void main(String[] args) {
new AnnotationConfigApplicationContext(OrderingOfInstantiation.class);
}
@Component
@CollectionOrder(collection = "myBeans", order = 1)
public static class MyBean1 {{
System.out.println(getClass().getSimpleName());
}}
@Component
@CollectionOrder(collection = "myBeans", order = 2)
public static class MyBean2 {{
System.out.println(getClass().getSimpleName());
}}
@Configuration
public static class CollectionsConfig {
@Bean
List<Object> myBeans() {
return new ArrayList<>();
}
}
// PopulateConfig will populate all collections beans
@Configuration
public static class PopulateConfig implements ApplicationContextAware {
@SuppressWarnings("unchecked")
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Multimap<String, Object> beansMap = MultimapBuilder.hashKeys().arrayListValues().build();
// get all beans
applicationContext.getBeansWithAnnotation(CollectionOrder.class)
.values().stream()
// get CollectionOrder annotation
.map(bean -> Pair.of(bean, bean.getClass().getAnnotation(CollectionOrder.class)))
// sort by order
.sorted((p1, p2) -> p1.getRight().order() - p2.getRight().order())
// add to multimap
.forEach(pair -> beansMap.put(pair.getRight().collection(), pair.getLeft()));
// and add beans to collections
beansMap.asMap().entrySet().forEach(entry -> {
Collection collection = applicationContext.getBean(entry.getKey(), Collection.class);
collection.addAll(entry.getValue());
// debug
System.out.println(entry.getKey() + ":");
collection.stream()
.map(bean -> bean.getClass().getSimpleName())
.forEach(System.out::println);
});
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
public @interface CollectionOrder {
int order() default 0;
String collection();
}
}
Cela ne changera pas l'ordre d'instanciation, mais vous obtiendrez des collections ordonnées.
Si vous voulez vous assurer qu'un bean spécifique est créé avant un autre, vous pouvez utiliser l'annotation @DependsOn.
@Configuration
public class Configuration {
@Bean
public Foo foo() {
...
}
@Bean
@DependsOn("foo")
public Bar bar() {
...
}
}
Gardez à l'esprit que cela ne définit pas l'ordre, il garantit uniquement que le bean "foo" est créé avant "bar". JavaDoc pour @DependsOn
Vous pouvez imposer un ordre dans votre exemple en éliminant d’abord la statique dans les classes MyBean1
et MyBean2
, ce qui n’est pas nécessaire lorsque vous utilisez Spring, dans la mesure où Spring utilise par défaut une instance unique de chaque bean (semblable à Singleton). .
L'astuce consiste à déclarer MyBean1
et MyBean2
en tant que @Bean
et à appliquer l'ordre, vous créez une dépendance implicite de bean1 à bean2 en appelant la méthode d'initialisation de bean2 à partir de la méthode d'initialisation de bean1.
Par exemple:
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
/**
* Order does not work here
*/
public class OrderingOfInstantiation {
interface MyBean{
default String printSimpleName(){
System.out.println(getClass().getSimpleName());
}
}
public class MyBean1 implments MyBean{
public MyBean1(){ pintSimpleName(); }
}
public class MyBean2 implments MyBean{
public MyBean2(){ pintSimpleName(); }
}
public class MyBean3 implments MyBean{
public MyBean3(){ pintSimpleName(); }
}
public class MyBean4 implments MyBean{
public MyBean4(){ pintSimpleName(); }
}
public class MyBean5 implments MyBean{
public MyBean5(){ pintSimpleName(); }
}
@Configuration
public class Config {
@Bean
MyBean1 bean1() {
//This will cause a a dependency on bean2
//forcing it to be created before bean1
bean2();
return addToAllBeans(new MyBean1());
}
@Bean
MyBean2 bean2() {
//This will cause a a dependency on bean3
//forcing it to be created before bean2
bean3();
//Note: This is added just to explain another point
//Calling the bean3() method a second time will not create
//Another instance of MyBean3. Spring only creates 1 by default
//And will instead look up the existing bean2 and return that.
bean3();
return addToAllBeans(new MyBean2());
}
@Bean
MyBean3 bean3(){ return addToAllBeans(new MyBean3()); }
@Bean
MyBean4 bean4(){ return addToAllBeans(new MyBean4()); }
@Bean
MyBean5 bean5(){ return addToAllBeans(new MyBean5()); }
/** If you want each bean to add itself to the allBeans list **/
@Bean
List<MyBean> allBeans(){
return new ArrayList<MyBean>();
}
private <T extends MyBean> T addToAllBeans(T aBean){
allBeans().add(aBean);
return aBean;
}
}
public static void main(String[] args) {
new AnnotationConfigApplicationContext(Config.class);
}
}