[RESOLVIDO] Como executar outro construtor se um @Component não for encontrado no VRaptor

19 respostas
M

É possível executar outro construtor ou outro código se um @Component não for encontrado?

Por exemplo

@Component
public class ClasseA{
    int numero;

    public ClasseA(){
        numero = 0;
    }
    public ClasseA(ClasseB b){
        numero = b.getNumero;
    }
}

Aí se a class “ClasseB” não for encontrada, executa o primeiro construtor.

19 Respostas

Rafael_Guerreiro

Se ele não fosse encontrado ele seria nulo, não?

M

Ero o que eu queria, mas é jogado uma Exception informando que não foi encontrada.

M

As classes reais são:

@Component
@SessionScoped
public class UserAccessDefinitions implements Serializable {

	private static final long serialVersionUID = 5301380832545035596L;
	
	
	private List<Method> methodDenials;
	private List<Method> methodPermissions;
	private List<Class<?>> resourceDenials;
	private List<Class<?>> resourcePermissions;
	private List<String> roles;

	/**
	 * @param valuesProvider
	 */
	public UserAccessDefinitions(
			EAccessConInitialUserAccessDefinitionProvider valuesProvider) {
		if (valuesProvider.getMethodDenials() != null) {
			this.methodDenials = valuesProvider.getMethodDenials();
		} else {
			this.methodDenials = new ArrayList<Method>();
		}

		if (valuesProvider.getMethodPermissions() != null) {
			this.methodPermissions = valuesProvider.getMethodPermissions();
		} else {
			this.methodPermissions = new ArrayList<Method>();
		}

		if (valuesProvider.getResourceDenials() != null) {
			this.resourceDenials = valuesProvider.getResourceDenials();
		} else {
			this.resourceDenials = new ArrayList<Class<?>>();
		}

		if (valuesProvider.getResourcePermissions() != null) {
			this.resourcePermissions = valuesProvider.getResourcePermissions();
		} else {
			this.resourcePermissions = new ArrayList<Class<?>>();
		}

		if (valuesProvider.getRoles() != null) {
			this.roles = valuesProvider.getRoles();
		} else {
			this.roles = new ArrayList<String>();
		}

	}
        //Setters and getters
}
A variável no construtor é um objeto de uma classe que deve implementar minha interface, acho que é por isso que lança a Exception.
public interface EAccessConInitialUserAccessDefinitionProvider {

	public List<String> getRoles();

	public List<Method> getMethodDenials();

	public List<Method> getMethodPermissions();

	public List<Class<?>> getResourceDenials();

	public List<Class<?>> getResourcePermissions();

}

Então deve ser isso, já que não encontra uma implementação, não coloca nem null, pois é uma interface.

Então a unica solução que eu penso seria executar o outro construtor, mas nem sei se é possível fazer.

Rafael_Guerreiro

Para poder fazer isso, você deve criar um component factory da sua interface e nela você instancia a implementação e devolve no getInstance.

M

O problema é que a implementação não vai ser feita por mim e não vai ter como eu saber o nome dela, então não tem como eu instanciar uma coisa que eu nem sei se está lá.

Eu queria o seguinte:

Se tiver alguma classe que implementa minha interface, então me  um objeto daquela classe para eu iniciar os atributos da minha outra classe
Se não tiver, executa um novo construtor que inicia os atributos da minha  outra classe com valores padrões.
M

Eu já até consegui solucionar o problema, mas aí o usuário vai ter que criar um CustomProvider para o VRaptor, e registrar a implementação dele para a minha interface, e é o que eu não queria fazer.

Só quero saber mesmo se tem como, se não existir uma implementação, executar outro construtor que não precise daquela dependência.

Rafael_Guerreiro

Isso dá para ser feito… Já que o próprio VRaptor faz algo assim: Se você criar uma implementação de uma das interfaces internas dele e anotar com @Component, ele vai usar a implementação “nova” se não, ele usa a padrão… Se você perceber, a classe Result é uma interface, ou seja, dá para sobrescrever ela.

Infelizmente eu não sei como ele faz isso… Talvez se você escaneasse o projeto procurando pela classe que implementa a interface e está anotada com @Component.
Mas acho que é trabalho desnecessário. O VRaptor deve ter algo já pronto para isso.

Talvez essa parte de injeção de dependências da documentação do VRaptor possa te ajudar melhor:
http://vraptor.caelum.com.br/documentacao/injecao-de-dependencias/

Rafael_Guerreiro

Olha só, nesse post, o Lucas afirma que se você tiver várias classes que implementam a sua interface e que estão anotadas com @Component, todas serão injetadas em uma lista.

http://www.guj.com.br/java/264058-polimorfismo-e-injecao-de-dependencia---vraptor

M

Rafael Guerreiro:
Olha só, nesse post, o Lucas afirma que se você tiver várias classes que implementam a sua interface e que estão anotadas com @Component, todas serão injetadas em uma lista.

http://www.guj.com.br/java/264058-polimorfismo-e-injecao-de-dependencia---vraptor

Dei uma olhada, mas tá falando que você tem que receber todas as implementações no construtor. Só que eu não sei quais implementações serão feitas :frowning:

Rafael_Guerreiro

Eu estive pensando no seguinte:
Você cria um component factory da sua interface e no construtor dele você recebe uma lista de da interface, verifica qual você quer devolver e coloca no getInstance…

O problema é que ela recebe no construtor o que ela mesma vai devolver… Será que isso dá certo?

M

Rafael Guerreiro:
Eu estive pensando no seguinte:
Você cria um component factory da sua interface e no construtor dele você recebe uma lista de da interface, verifica qual você quer devolver e coloca no getInstance…

O problema é que ela recebe no construtor o que ela mesma vai devolver… Será que isso dá certo?

Vlw Rafael Guerreiro. Funcionou fazendo receber uma lista.

Solução:

@Component
@SessionScoped
public class UserAccessDefinitions implements Serializable {

	private static final long serialVersionUID = 5301380832545035596L;

	private List&lt;Method&gt; methodDenials;
	private List&lt;Method&gt; methodPermissions;
	private List&lt;Class&gt;&lt;?&gt;&gt; resourceDenials;
	private List&lt;Class&gt;&lt;?&gt;&gt; resourcePermissions;
	private List&lt;String&gt; roles;

	/**
	 * @param valuesProvider
	 */
	public UserAccessDefinitions(
			List&lt;EAccessConInitialUserAccessDefinitionProvider&gt; valuesProviderList) {

		EAccessConInitialUserAccessDefinitionProvider valuesProvider = valuesProviderList
				.get(0);

		if (valuesProviderList.size() &gt; 1) {
			for (EAccessConInitialUserAccessDefinitionProvider provider : valuesProviderList) {
				if (!DefaulEAccessInitialDefaultValueProvider.class
						.isInstance(provider)) {
					valuesProvider = provider;
					break;
				}
			}
		}
        /* Resto do código - Inicializando as variáveis*/
    }
}

Aí eu tenho uma classe padrão que implementa minha interface, que retorna os valores padrões

@Component
public class DefaulEAccessInitialDefaultValueProvider implements EAccessConInitialUserAccessDefinitionProvider {/*...*/}

E a classe que alguém vai implementar

@Component
public class MyEAccessInitialDefaultValueProvider implements EAccessConInitialUserAccessDefinitionProvider {/*...*/}

Minha lógica foi: se o objeto que recebi na lista não for da minha classe padrão, então eu uso este objeto.

Rafael_Guerreiro

Eu estava pensando em uma forma diferente…

Tipo, cria um component factory de EAccessConInitialUserAccessDefinitionProvider.

No construtor dele, recebe a List<EAccessConInitialUserAccessDefinitionProvider> e define a instância que será retornada no método getInstance.

Assim, quando você precisar receber EAccessConInitialUserAccessDefinitionProvider em algum construtor, você não precisaria receber uma lista e verificar se é a instancia esperada.

M

Rafael Guerreiro:
Eu estava pensando em uma forma diferente…

Tipo, cria um component factory de EAccessConInitialUserAccessDefinitionProvider.

No construtor dele, recebe a List<EAccessConInitialUserAccessDefinitionProvider> e define a instância que será retornada no método getInstance.

Assim, quando você precisar receber EAccessConInitialUserAccessDefinitionProvider em algum construtor, você não precisaria receber uma lista e verificar se é a instancia esperada.

Eu tentei fazer isso, mas deu uma exceção lá de referência circular. Não entendi direito.

Rafael_Guerreiro

Era o que eu estava imaginando. Pois o component factory recebe o que ele devolve, então vira bagunça…

Eu te garanto que existe uma forma de fazer isso. Só estamos olhando para o caminho errado.

M

Olhando o exemplo do Lucas no outro Post, eu acho que eu tenho que criar uma classe.

UserAccessDefinitionProviders anotar com @Component, e criar um método getDefaultProvider(). Nesse método eu vou escolher qual vai ser o padrão. Se o programador não implementar minha interface, então a minha implementação vai ser a padrão. Se ele implementar, então a dele vai ser a padrão.

Vou fazer isso agora para ver se funciona e já respondo.

M

É isso mesmo.

Olha como ficou agora:

Classe que escolhe o AcceesDefinitionProvider padrão:

@Component
public class EAccessConInitialUserAccessDefinitionProviders {
	private final List<EAccessConInitialUserAccessDefinitionProvider> providersList;

	/**
	 * @param providersList
	 */
	public EAccessConInitialUserAccessDefinitionProviders(
			List<EAccessConInitialUserAccessDefinitionProvider> providersList) {
		this.providersList = providersList;
	}

	public EAccessConInitialUserAccessDefinitionProvider getDefaultProvider() {
		EAccessConInitialUserAccessDefinitionProvider defaultProvider = this.providersList
				.get(0);

		if (this.providersList.size() > 1) {
			for (EAccessConInitialUserAccessDefinitionProvider provider : this.providersList) {
				if (!DefaultEAccessInitialDefaultValueProvider.class
						.isInstance(provider)) {
					defaultProvider = provider;
					break;
				}
			}
		}

		return defaultProvider;
	}

}

Aí o UserAccessDefinitions recebe essa classe no construtor e pega o provider padrão, chamando o método getDefaultProvider.

Rafael_Guerreiro

Eu sugiro você mudar um pouco essa classe:

@Component  
public class EAccessConInitialUserAccessDefinitionProviders {  
    private final EAccessConInitialUserAccessDefinitionProvider defaultProvider;  
  
    /** 
     * @param providersList 
     */  
    public EAccessConInitialUserAccessDefinitionProviders(  
            List&lt;EAccessConInitialUserAccessDefinitionProvider&gt; providersList) {  
       defaultProvider = providersList  
                .get(0);  
  
        if (providersList.size() &gt; 1) {  
            for (EAccessConInitialUserAccessDefinitionProvider provider : providersList) {  
                if (!DefaultEAccessInitialDefaultValueProvider.class  
                        .isInstance(provider)) {  
                    defaultProvider = provider;  
                    break;  
                }  
            }  
        }  
    }  
  
    public EAccessConInitialUserAccessDefinitionProvider getDefaultProvider() {  
        return defaultProvider;  
    }  
  
}

Assim você evita ficar fazendo a mesma coisa toda vez que precisar do DefaultProvider…

M

Realmente, acho que vou colocar como @ApplicationScoped também. Aí só vai executar uma vez.

Rafael_Guerreiro

Isso ai! :smiley:
Depois edita o primeiro post e no assunto coloca um “[Resolvido]” assim quem precisar de algo parecido, consegue resolver mais rapidamente.

Criado 7 de abril de 2012
Ultima resposta 8 de abr. de 2012
Respostas 19
Participantes 2