Como usar o Spring FactoryBean?

Como usar o Spring FactoryBean?

1. Visão geral

Existem dois tipos de feijões no contêiner Spring: feijões comuns e feijões de fábrica. O Spring usa o primeiro diretamente, enquanto o último pode produzir objetos, que são gerenciados pela estrutura.

E, simplesmente, podemos construir um bean de fábrica implementando a interfaceorg.springframework.beans.factory.FactoryBean.

2. Noções básicas de feijão de fábrica __

2.1. Implementar umFactoryBean

Vejamos a interfaceFactoryBean primeiro:

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

Vamos discutir os três métodos:

  • getObject() - retorna um objeto produzido pela fábrica, e este é o objeto que será usado pelo container Spring

  • getObjectType() - retorna o tipo de objeto que esteFactoryBean produz

  • isSingleton() - denota se o objeto produzido por esteFactoryBean é um singleton

Agora, vamos implementar um exemploFactoryBean. Vamos implementar umToolFactory que produz objetos do tipoTool:

public class Tool {

    private int id;

    // standard constructors, getters and setters
}

O próprioToolFactory:

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
}

Como podemos ver, oToolFactory é umFactoryBean, que pode produzir objetosTool.

2.2. UseFactoryBean com configuração baseada em XML

Vamos agora dar uma olhada em como usar nossoToolFactory.

Vamos começar a construir uma ferramenta com configuração baseada em XML -factorybean-spring-ctx.xml:



    
        
        
    

A seguir, podemos testar se o objetoTool foi injetado corretamente:

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

O resultado do teste mostra que conseguimos injetar o objeto ferramenta produzido porToolFactory com as propriedades que configuramos emfactorybean-spring-ctx.xml.

O resultado do teste também mostra que o contêiner Spring usa o objeto produzido porFactoryBean em vez de si mesmo para injeção de dependência.

Embora o contêiner Spring use o valor de retorno do métodoFactoryBean'sgetObject() como o bean, você também pode usar o próprioFactoryBean.

Para acessar oFactoryBean, você só precisa adicionar um “&” antes do nome do bean.

Vamos tentar obter o bean de fábrica e sua propriedadefactoryId:

@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. UseFactoryBean com configuração baseada em Java

UsarFactoryBean com configuração baseada em Java é um pouco diferente com configuração baseada em XML, você deve chamar o métodoFactoryBeangetObject() explicitamente.

Vamos converter o exemplo da subseção anterior em um exemplo de configuração baseada em 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();
    }
}

Em seguida, testamos se o objetoTool foi injetado corretamente:

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

O resultado do teste mostra o efeito semelhante ao teste de configuração anterior baseado em XML.

3. Maneiras de inicializar

Às vezes, você precisa realizar algumas operações depois queFactoryBean foi definido, mas antes que o métodogetObject() seja chamado, como a verificação de propriedades.

Você pode conseguir isso implementando a interfaceInitializingBean ou usando a anotação@PostConstruct.

Mais detalhes sobre o uso dessas duas soluções foram apresentados em outro artigo:Guide To Running Logic on Startup in Spring.

4. AbstractFactoryBean

Spring forneceAbstractFactoryBean como uma superclasse de template simples para implementações deFactoryBean. Com essa classe base, agora podemos implementar de maneira mais conveniente um bean de fábrica que cria um objeto singleton ou protótipo.

Vamos implementar umSingleToolFactory e umNonSingleToolFactory para mostrar como usarAbstractFactoryBean para o tipo singleton e protótipo:

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
}

E agora a implementação não-singleton:

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
}

Além disso, a configuração XML para esses beans de fábrica:



    
        
        
    

    
        
        
    

Agora podemos testar se as propriedades dos objetosWorker são injetadas como esperamos:

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

Como podemos ver nos testes, oSingleToolFactory produz o objeto singleton e oNonSingleToolFactory produz o objeto protótipo.

Observe que não há necessidade de definir a propriedade singleton emSingleToolFactory porque, emAbstractFactory, o valor padrão da propriedade singleton étrue.

5. Conclusão

Usar umFactoryBean pode ser uma boa prática para encapsular lógicas de construção complexas ou tornar a configuração de objetos altamente configuráveis ​​mais fácil no Spring.

Portanto, neste artigo, apresentamos os princípios básicos de como implementar nossoFactoryBean, como usá-lo na configuração baseada em XML e na configuração baseada em Java, e alguns outros aspectos diversos deFactoryBean, como inicialização deFactoryBeaneAbstractFactoryBean.

Como sempre, a fonte completa éover on GitHub.