Comment utiliser le Spring FactoryBean?

Comment utiliser le Spring FactoryBean?

1. Vue d'ensemble

Le conteneur de haricots de printemps contient deux types de haricots: les haricots ordinaires et les haricots d’usine. Spring utilise les premiers directement, alors que ces derniers peuvent produire eux-mêmes des objets, qui sont gérés par le framework.

Et, simplement, nous pouvons construire un bean usine en implémentant l'interfaceorg.springframework.beans.factory.FactoryBean.

2. Les bases des haricots d'usine __

2.1. Implémenter unFactoryBean

Examinons d'abord l'interface deFactoryBean:

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

Discutons des trois méthodes:

  • getObject() - renvoie un objet produit par l'usine, et c'est l'objet qui sera utilisé par le conteneur Spring

  • getObjectType() - renvoie le type d'objet produit par ceFactoryBean

  • isSingleton() - indique si l'objet produit par ceFactoryBean est un singleton

Maintenant, implémentons un exempleFactoryBean. Nous allons implémenter unToolFactory qui produit des objets de typeTool:

public class Tool {

    private int id;

    // standard constructors, getters and setters
}

LeToolFactory lui-même:

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
}

Comme nous pouvons le voir, leToolFactory est unFactoryBean, qui peut produire des objetsTool.

2.2. UtiliserFactoryBean avec une configuration basée sur XML

Voyons maintenant comment utiliser nosToolFactory.

Nous allons commencer à construire un outil avec une configuration basée sur XML -factorybean-spring-ctx.xml:



    
        
        
    

Ensuite, nous pouvons tester si l'objetTool est correctement injecté:

@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));
    }
}

Le résultat du test montre que nous parvenons à injecter l'objet outil produit par lesToolFactory avec les propriétés que nous avons configurées dans lesfactorybean-spring-ctx.xml.

Le résultat du test montre également que le conteneur Spring utilise l'objet produit par lesFactoryBean au lieu de lui-même pour l'injection de dépendances.

Bien que le conteneur Spring utilise la valeur de retour de la méthodeFactoryBeangetObject() comme bean, vous pouvez également utiliser leFactoryBean lui-même.

Pour accéder auxFactoryBean, il vous suffit d'ajouter un «&» avant le nom du bean.

Essayons d'obtenir le bean factory et sa propriété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. UtiliserFactoryBean avec une configuration basée sur Java

L'utilisation deFactoryBean avec une configuration basée sur Java est un peu différente avec une configuration basée sur XML, vous devez appeler la méthodegetObject() deFactoryBean explicitement.

Convertissons l'exemple de la sous-section précédente en un exemple de configuration 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();
    }
}

Ensuite, nous testons si l'objetTool est correctement injecté:

@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));
    }
}

Le résultat du test montre un effet similaire à celui du test de configuration précédent basé sur XML.

3. Façons d'initialiser

Parfois, vous devez effectuer certaines opérations après que leFactoryBean a été défini mais avant que la méthodegetObject() soit appelée, comme la vérification des propriétés.

Vous pouvez y parvenir en implémentant l'interfaceInitializingBean ou en utilisant l'annotation@PostConstruct.

Plus de détails sur l'utilisation de ces deux solutions ont été introduits dans un autre article:Guide To Running Logic on Startup in Spring.

4. AbstractFactoryBean

Spring fournit lesAbstractFactoryBean comme une simple superclasse de modèle pour les implémentations deFactoryBean. Avec cette classe de base, nous pouvons maintenant plus facilement implémenter un bean factory qui crée un singleton ou un objet prototype.

Implémentons unSingleToolFactory et unNonSingleToolFactory pour montrer comment utiliserAbstractFactoryBean pour les types singleton et prototype:

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
}

Et maintenant, l'implémentation nonsingleton:

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
}

En outre, la configuration XML pour ces beans d'usine:



    
        
        
    

    
        
        
    

Nous pouvons maintenant tester si les propriétés des objetsWorker sont injectées comme prévu:

@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);
    }
}

Comme nous pouvons le voir d'après les tests, leSingleToolFactory produit un objet singleton et leNonSingleToolFactory produit un objet prototype.

Notez qu'il n'est pas nécessaire de définir la propriété singleton dansSingleToolFactory car, dansAbstractFactory, la valeur par défaut de la propriété singleton esttrue.

5. Conclusion

L'utilisation d'unFactoryBean peut être une bonne pratique pour encapsuler une logique de construction complexe ou faciliter la configuration d'objets hautement configurables dans Spring.

Donc, dans cet article, nous avons présenté les bases de la façon d'implémenter nosFactoryBean, comment l'utiliser à la fois dans une configuration XML et une configuration basée sur Java, et d'autres aspects divers deFactoryBean, tels que initialisation deFactoryBean etAbstractFactoryBean.

Comme toujours, la source complète estover on GitHub.