Come utilizzare Spring FactoryBean?

1. Panoramica

Ci sono due tipi di fagioli nel contenitore Spring bean: fagioli normali e fagioli di fabbrica. Spring utilizza il primo direttamente, mentre il secondo può produrre essi stessi oggetti, che sono gestiti dal framework.

E, in poche parole, possiamo creare un factory bean implementando l' interfaccia org.springframework.beans.factory.FactoryBean .

2. Le basi dei fagioli di fabbrica

2.1. Implementa un FactoryBean

Diamo prima un'occhiata all'interfaccia FactoryBean :

public interface FactoryBean { T getObject() throws Exception; Class getObjectType(); boolean isSingleton(); }

Discutiamo i tre metodi:

  • getObject () - restituisce un oggetto prodotto dalla fabbrica, e questo è l'oggetto che verrà utilizzato dal contenitore Spring
  • getObjectType () - restituisce il tipo di oggetto che questo FactoryBean produce
  • isSingleton () - denota se l'oggetto prodotto da questo FactoryBean è un singleton

Ora implementiamo un esempio FactoryBean . Implementeremo una ToolFactory che produce oggetti del tipo Tool :

public class Tool { private int id; // standard constructors, getters and setters }

La ToolFactory stessa:

public class ToolFactory implements FactoryBean { private int factoryId; private int toolId; @Override public Tool getObject() throws Exception { return new Tool(toolId); } @Override public Class getObjectType() { return Tool.class; } @Override public boolean isSingleton() { return false; } // standard setters and getters }

Come possiamo vedere, ToolFactory è un FactoryBean , che può produrre oggetti Tool .

2.2. Usa FactoryBean con configurazione basata su XML

Diamo ora un'occhiata a come utilizzare la nostra ToolFactory .

Inizieremo a costruire uno strumento con una configurazione basata su XML - factorybean-spring-ctx.xml :

Successivamente, possiamo verificare se l' oggetto Tool è stato iniettato correttamente:

@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "classpath:factorybean-spring-ctx.xml" }) public class FactoryBeanXmlConfigTest { @Autowired private Tool tool; @Test public void testConstructWorkerByXml() { assertThat(tool.getId(), equalTo(1)); } }

Il risultato del test mostra che siamo riusciti a iniettare l'oggetto strumento prodotto da ToolFactory con le proprietà che abbiamo configurato in factorybean-spring-ctx.xml .

Il risultato del test mostra anche che il contenitore Spring utilizza l'oggetto prodotto da FactoryBean invece di se stesso per l' inserimento delle dipendenze.

Anche se il contenitore Spring usa il FactoryBean s' getObject () il valore di ritorno il metodo di come il fagiolo, è anche possibile utilizzare il FactoryBean stesso.

Per accedere a FactoryBean , è sufficiente aggiungere una "&" prima del nome del bean.

Proviamo a ottenere il bean di fabbrica e la sua proprietà factoryId :

@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "classpath:factorybean-spring-ctx.xml" }) public class FactoryBeanXmlConfigTest { @Resource(name = "&tool") private ToolFactory toolFactory; @Test public void testConstructWorkerByXml() { assertThat(toolFactory.getFactoryId(), equalTo(9090)); } }

2.3. Usa FactoryBean con configurazione basata su Java

Utilizzare FactoryBean con la configurazione basata su Java è un po 'diverso con la configurazione basato su XML, è necessario chiamare il FactoryBean s' getObject () il metodo in modo esplicito.

Convertiamo l'esempio nella sottosezione precedente in un esempio di configurazione basato su Java:

@Configuration public class FactoryBeanAppConfig { @Bean(name = "tool") public ToolFactory toolFactory() { ToolFactory factory = new ToolFactory(); factory.setFactoryId(7070); factory.setToolId(2); return factory; } @Bean public Tool tool() throws Exception { return toolFactory().getObject(); } }

Quindi, testiamo se l' oggetto Tool è stato iniettato correttamente:

@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = FactoryBeanAppConfig.class) public class FactoryBeanJavaConfigTest { @Autowired private Tool tool; @Resource(name = "&tool") private ToolFactory toolFactory; @Test public void testConstructWorkerByJava() { assertThat(tool.getId(), equalTo(2)); assertThat(toolFactory.getFactoryId(), equalTo(7070)); } }

Il risultato del test mostra l'effetto simile al precedente test di configurazione basato su XML.

3. Modi per inizializzare

Sometimes you need to perform some operations after the FactoryBean has been set but before the getObject() method is called, like properties check.

You can achieve this by implementing the InitializingBean interface or using @PostConstruct annotation.

More details about using these two solutions have been introduced in another article: Guide To Running Logic on Startup in Spring .

4. AbstractFactoryBean

Spring provides the AbstractFactoryBean as a simple template superclass for FactoryBean implementations. With this base class, we can now more conveniently implement a factory bean which creates a singleton or a prototype object.

Let's implement a SingleToolFactory and a NonSingleToolFactory to show how to use AbstractFactoryBean for both singleton and prototype type:

public class SingleToolFactory extends AbstractFactoryBean { private int factoryId; private int toolId; @Override public Class getObjectType() { return Tool.class; } @Override protected Tool createInstance() throws Exception { return new Tool(toolId); } // standard setters and getters }

And now the nonsingleton implementation:

public class NonSingleToolFactory extends AbstractFactoryBean { private int factoryId; private int toolId; public NonSingleToolFactory() { setSingleton(false); } @Override public Class getObjectType() { return Tool.class; } @Override protected Tool createInstance() throws Exception { return new Tool(toolId); } // standard setters and getters }

Also, the XML config for these factory beans:

Now we can test if the Worker objects' properties are injected as we expect:

@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "classpath:factorybean-abstract-spring-ctx.xml" }) public class AbstractFactoryBeanTest { @Resource(name = "singleTool") private Tool tool1; @Resource(name = "singleTool") private Tool tool2; @Resource(name = "nonSingleTool") private Tool tool3; @Resource(name = "nonSingleTool") private Tool tool4; @Test public void testSingleToolFactory() { assertThat(tool1.getId(), equalTo(1)); assertTrue(tool1 == tool2); } @Test public void testNonSingleToolFactory() { assertThat(tool3.getId(), equalTo(2)); assertThat(tool4.getId(), equalTo(2)); assertTrue(tool3 != tool4); } }

As we can see from the tests, the SingleToolFactory produces singleton object, and the NonSingleToolFactory produces prototype object.

Note that there's no need to set singleton property in SingleToolFactory because, in AbstractFactory, singleton property's default value is true.

5. Conclusion

L'utilizzo di un FactoryBean può essere una buona pratica per incapsulare una logica di costruzione complessa o semplificare la configurazione di oggetti altamente configurabili in Spring.

Quindi, in questo articolo, abbiamo introdotto le basi su come implementare il nostro FactoryBean , come usarlo sia nella configurazione basata su XML che in quella basata su Java e alcuni altri aspetti vari di FactoryBean , come l'inizializzazione di FactoryBean e AbstractFactoryBean .

Come sempre, il sorgente completo è finito su GitHub.