In questo articolo mi segno come ho fatto, in Spring 4, a generare dei bean in base a dei parametri. Vale la pena di scrivere questo promemoria dato che c’è bisogno di mettere insieme alcune parti della documentazione ufficiale o di altri blog, evitare quelle pagine che si riferiscono a versioni vecchie di Spring e infine fare qualche tentativo per vedere come i vari tasselli formano il puzzle.

Obiettivo: istanziare dei bean, il cui comportamento vari a seconda di uno o più parametri passati via codice al momento della creazione.

Questo è il bean di test che ho usato.

public interface MyBean {
	public void work();
}
public class SimpleMyBean implements MyBean {
	private Integer num;
	public SimpleMyBean(Integer num) {
		super();
		this.num = num;
	}
	@Override
	public void work() {
		System.out.println(num + " -> " + toString());
	}
}

Ecco subito la soluzione, implementata con Java 6 + Spring 4.1, usando l’approccio Java Config.
Questa è la classe di configurazione che ho sviluppato:

@Configuration
public class AppConf {
	@Bean(name = "myBean")
	@Scope("prototype")
	public MyBean getMyBean(Integer num) {
		return new SimpleMyBean(num);
	}
}

Si noti che:

  • lo scope DEVE essere prototype, perché logicamente potranno venir istanziati più bean con questo metodo e perché altrimenti Spring lancia un bello stack trace di errori di inizializzazione del bean;
  • ogni chiamata a getMyBean genera una nuova istanza, anche nel caso di chiamate con parametri uguali, per cui eventuali politiche di caching vanno esplicitamente implementate in questo metodo (o metodi da esso utilizzati).

Per testare il tutto, un bel main:

public class Main {
	private ApplicationContext context;
	public static void main(String[] args) {
		Main instance = new Main();
		instance.execute();
	}
	private void execute() {
		context = new AnnotationConfigApplicationContext(AppConf.class);
		MyBean bean1 = context.getBean(MyBean.class, Integer.valueOf(10));
		bean1.work();
		MyBean bean2 = context.getBean(MyBean.class, Integer.valueOf(11));
		bean2.work();
		MyBean bean3 = context.getBean(MyBean.class, Integer.valueOf(10));
		bean3.work();
	}
}

che produce il seguente output (a parte qualche taglio):

10 -> SimpleMyBean@38ee6681
11 -> SimpleMyBean@2b8bbc5a
10 -> SimpleMyBean@62facf0b

L’output appunto dimostra come vengano generate 3 istanze del bean.

Note finali

  • Ritengo che un eventuale caching delle istanze al fine di ritornare la stessa istanza per parametri uguali debba essere fatto internamente al metodo @Bean.
    Quanto meno, non ho trovato altri sistemi: l’implementazione di uno Scope personalizzato non risolve questo problema.
  • Cercando in rete, si trovano molte pagine che propongono lo sviluppo di un proprio BeanFactory, ma questo approccio è sconsigliato in Spring 4.
  • Il metodo di richiesta dei bean da parte del main accetta gli argomenti come array di Object, i metodo @Bean nella classe @Configuration possono specificare le classi che intendono effettivamente usare (come l’Integer dell’esempio).
Annunci