0
votes

i did a singleton class named AcessoCliente

public class AcessoCliente {

    private HashMap<String, Cliente> clientes;
    private new HashMap<String, Date> clientesNaoEncontrados;

    private static AcessoCliente instance;

    static {
        instance = new AcessoCliente();
    }

    public static AcessoCliente get() {
        return instance;
    }

    private AcessoCliente() {
        clientes = new HashMap<String, Cliente>();
        clientesNaoEncontrados = new HashMap<String, Date>();
    }

    /*business*/
}

But i need to do a dependency injection of a class named ValidadorNivelDeAcessoBusiness on my singleton class

@Component
public class ValidadorNivelDeAcessoBusiness {

    @Autowired
    QuerysNiveisDeAcesso querysNiveisDeAcesso;
    /*business*/
}

I'm trying do this dependency injection but isn't working, This is what I did:

public class AcessoCliente {

    @Autowired
    ValidadorNivelDeAcessoBusiness validadorNivelDeAcessoBusiness;

    private HashMap<String, Cliente> clientes;
    private new HashMap<String, Date> clientesNaoEncontrados;

    private static AcessoCliente instance;

    static {
        ApplicationContext context = new AnnotationConfigApplicationContext(AcessoCliente.class);
        instance = context.getBean(AcessoCliente.class);
    }

    public static AcessoCliente get() {
        return instance;
    }

    private AcessoCliente() {
        clientes = new HashMap<String, Cliente>();
        clientesNaoEncontrados = new HashMap<String, Date>();
    }

    /*business*/
}

but the dependency injection isn't working and I get this error:

Error creating bean with name 'acessoCliente': Unsatisfied dependency expressed through field 'validadorNivelDeAcessoBusiness'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'my.project.business.interceptorBusiness.ValidadorNivelDeAcessoBusiness' available: expected at least 1 bean which qualifies as autowire candidate.
Dependency annotations:
{@org.springframework.beans.factory.annotation.Autowired(required=true)}

Edit1. That's the QuerysNiveisDeAcesso class

@Component
public class QuerysNiveisDeAcesso extends QuerysClientes {

    public QueryIntegratorBuilder queryBuscaNiveisDeAcesso(String[] condicoesQuery) throws Exception {
        return super.executaQuery("BUSCA_NIVEIS_DE_ACESSO", condicoesQuery);
    }

    public QueryIntegratorBuilder queryBuscaNiveisDeAcesso() throws Exception {
        return super.executaQuery("BUSCA_NIVEIS_DE_ACESSO");
    }

    public QueryIntegratorBuilder queryBuscaNiveisDeAcesso(String sqlWhere, String[] condicoesQuery) throws Exception {
        return super.executaQuery("BUSCA_NIVEIS_DE_ACESSO", condicoesQuery, sqlWhere);
    }

}
3
What is this class: QuerysNiveisDeAcesso ?Ismail
@Ismail QueryNiveisDeAcesso added at the postCapybara

3 Answers

1
votes

You are trying to mix Java Singleton and Spring singleton.

To make it compatible with plain java and spring both, you should make static factory method with your injected service in parameter and make a singleton's bean in @Configuration file.

public class AcessoCliente {

  ValidadorNivelDeAcessoBusiness validadorNivelDeAcessoBusiness;

  private HashMap<String, Cliente> clients;
  private HashMap<String, Date> clientesNaoEncontrados;

  private static AcessoCliente instance;

  public static AcessoCliente getInstance(ValidadorNivelDeAcessoBusiness validadorNivelDeAcessoBusiness) {
    if(instance == null) {
      instance = new AcessoCliente(validadorNivelDeAcessoBusiness);
    }
    return instance;
  }

  private AcessoCliente(ValidadorNivelDeAcessoBusiness validadorNivelDeAcessoBusiness) {
    clientes = new HashMap<String, Cliente>();
    clientesNaoEncontrados = new HashMap<String, Date>();
    this.validadorNivelDeAcessoBusiness = validadorNivelDeAcessoBusiness;
  }
}

@Configuration
public class AcessoClienteConfiguration
{    
  @Bean
  @Scope("singleton")
  public AcessoCliente acessoCliente(ValidadorNivelDeAcessoBusiness validadorNivelDeAcessoBusiness)
  {
    return AcessoCliente.getInstance(validadorNivelDeAcessoBusiness);
  }
}
0
votes

I've tried to recreate the same issue you are having, it would seem that your dependencies in ValidadorNivelDeAcessoBusiness class is trying to load, however one of the fields in this class might be missing an @Component annotation, for the Spring application context to load.

@Component public class AcessoCliente {
    @Autowired
    ValidadorNivelDeAcessoBusiness validadorNivelDeAcessoBusiness;
}

@Component
public class ValidadorNivelDeAcessoBusiness {

   @Autowired
   QuerysNiveisDeAcesso querysNiveisDeAcesso;
}

public class QuerysNiveisDeAcesso {
   // some code
}

Would produce the above error: 'acessoCliente': Unsatisfied dependency expressed through field 'validadorNivelDeAcessoBusiness'

Ensuring that all the fields in ValidadorNivelDeAcessoBusiness have an @Component got the spring application context to work i.e.:

@Component 
public class QuerysNiveisDeAcesso {
      // some code
}
-1
votes

You've completely misused the purpose of spring.

First off, there is no point in creating an application context inside the AcessoCliente class.

Application context is a "global" spring's registry objects that usually exists once in a whole application.

If you're using Plain Spring - you can create the application context right in the public static void main method. And then get the beans from there.

Next, when you create the application context, you should pass to it the configuration objects and not a single class. You can also work with component scanning, there are many techniques. But all-in-all it should accept the "rules" to configure - read find and load the beans.

Now lets return to the AccessCliente class. You've defined it as a singleton with static methods and private constructor, that's ok. But It doesn't work with spring - in fact if you're using spring, you can make this class a Singleton in a sense that there will be a single bean in the whole application context. This is much more manageable and clear (+ no boilerplate code). In fact all beans by default are singletons in spring universe.

The next thing to mentions is when you make is a singleton bean (a class whose instance is managed by spring) then the whole autowiring magic will start working automatically.

What you've done is a strange hybrid that won't work anyway (how spring can create a bean if its not instructed to do so, and even if it was, the constructor is private).

So, to recap, you need something like this:

public class Main {
   public static void main(String  [] args) {
     ApplicationContext ctx = ...;
     ctx.getBean(SomeClassThatStartsTheFlow.class).doSomething();
   }    
}

@Service
public class AcessoCliente {

    @Autowired
    ValidadorNivelDeAcessoBusiness validadorNivelDeAcessoBusiness;

    private HashMap<String, Cliente> clientes;
    private new HashMap<String, Date> clientesNaoEncontrados;

    public AcessoCliente() {
        clientes = new HashMap<String, Cliente>();
        clientesNaoEncontrados = new HashMap<String, Date>();
    }

    /*business*/
}