Para que servem os qualifiers do Spring?

Atualmente na Caelum, estou participando de um projeto pequeno, bem focado em tirar relatórios de um dos nossos sistemas. Para ficar um pouco mais simples manipular os dados, pelo menos para mim, a gente criou uma aplicação que trata as informações das tabelas do sistema original e cria views ou o que for necessário em outro banco de dados, específico para os relatórios. Para completar a brincadeira, usamos um pouco do R para fazer as consultas nesse novo banco e brincar de gerar saídas para esses relatórios. Apesar do R ser legal, a utilização dele foi mais para preencher aquela vontade de brincar com algo ainda desconhecido para mim.

Como já é de praxe, a aplicação foi escrita usando o Spring Boot, mesmo ela não sendo uma aplicação web. Lembre-se disso, você pode usar o Spring para qualquer tipo de projeto, não só web :). Como ela tem que falar com dois bancos de dados, foi necessário configurar dois datasources.

	@Bean
	public DataSource dataSourceSistemaAntigo() {
		//codigo aqui
	}
	
	@Bean	
	public DataSource dataSourceRelatorios() {
		//codigo aqui
	}

Só que na hora de subir a aplicação eu tomei a seguinte exception:

	Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException:
	No qualifying bean of type [javax.sql.DataSource] is defined: expected single
	matching bean but found 2: ds_relatorios,ds_cweb

Talvez você já tenha visto a exception NoUniqueBeanDefinitionException. O nome dela não podia ser mais expressivo, o Spring encontrou dois candidatos para injetar em certo ponto do sistema.

	@Autowired
	public GeraDadosAnaliseComentarios(GeracaoRelatorioDao dao,DataSource dataSource) {
		super();
		this.dao = dao;
		this.template = new JdbcTemplate(dataSource);
	}

Como ele vai saber qual DataSource usar aqui? Para resolver essa questão, é necessário que você ensine para ele qual dos dois você quer.

	@Autowired
	public GeraDadosAnaliseComentarios(GeracaoRelatorioDao dao,
			@Qualifier("dataSourceSistemaAntigo") DataSource dataSource) {
		super();
		this.dao = dao;
		this.template = new JdbcTemplate(dataSource);
	}

Esse é o objetivo da annotation @Qualifier, ela permite que você referencie pelo nome o objeto que está injetado. Quando você anota um método com @Bean,  o Spring vai registrar o objeto retornado pelo método no container de IoC usando o mesmo nome do seu método. No caso o nome seria dataSourceSistemaAntigo. Nada também impede de você solicitar um registro com um nome diferente, basta que seja utilizado o atributo name da annotation @Bean. 

	@Bean(name="ds_cweb")	
	public DataSource dataSourceSistemaAntigo(Environment environment) {
		//codigo aqui
	}
	@Autowired
	public GeraDadosAnaliseComentarios(GeracaoRelatorioDao dao,
			@Qualifier("ds_cweb") DataSource dataSource) {
		super();
		this.dao = dao;
		this.template = new JdbcTemplate(dataSource);
	}

Mesmo com essas alterações, nossa aplicação ainda continua dando a mesma exception. O problema aqui é que a classe de auto configuração do DataSource, do Spring Boot, espera que apenas um objeto deste tipo esteja configurado(até onde sei). O trecho de código abaixo, da última versão estável do Spring Boot, mostra exatamente isso.

	if (this.applicationContext.getBeanNamesForType(DataSource.class, false,
			false).length > 0) {
		this.dataSource = this.applicationContext.getBean(DataSource.class);
	}

Precisamos agora ensinar ao Spring que ele sempre deve preferir um dos objetos, quando o lookup for feito sem o uso dos qualifiers.

	@Bean(name="ds_relatorios")	
	@Primary
	public DataSource dataSourceRelatorios(Environment environment) {
		//codigo aqui
	}

É justamente para isso que serve a annotation @Primary. Ela indica qual dos métodos produtores de objetos deve ser priorizado.

Esse post talvez não tenha sido muito avançado, mas é um erro que chega bastante nos fóruns e que muitas vezes as pessoas ficam em dúvida. Espero que ela tenha sido útil para você 🙂

 

2 thoughts on “Para que servem os qualifiers do Spring?

  1. Fala Alberto, Beleza?

    Recurso extremamente interessante do Qualifiers, utilizei ele em um projeto recentemente para injetar implementações em uma interface. Porém fiquei um pouco temeroso por qualificarmos a injeção através de uma String. Saberia dizer se existe alguma forma, se tratando de classes de injetar uma referencia para a classe ao invés de uma string com o nome da mesma?

    Obrigado,
    Abç!

    Like

  2. Fico contente que um post do início de 2016 tirou minha dúvida no final de 2020!
    Muito boa a explicação de como foi mostrado para que serve (quais problemas resolvem) e como utilizar.

    Like

Leave a comment