Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Autowire depending upon the subclass

I have a abstract class AbstractService which has a reference to AbstractDAO

class AbstractService{  
   protected AbstractDAO abstractDAO;  
}

AbstractService will be extended by actual service classes like ServiceClassA , ServiceClassB etc, and AbstractDAO will be extended by DaoClassA , DaoClassB etc.

Depending upon which class is extending AbstractService, abstractDAO should be an instance of DaoClassA , DaoClassB etc

I can achieve this by having the abstractDAO setter in the extending class like

class ServiceClassA{    
    @Autowired  
    @Qualifier("daoClassA")  
    public void setAbstractDAO(AbstractDAO abstractDAO) {  
        super.abstractDAO = abstractDAO;  
    }   
}  

Is there any way to have the setter setAbstractDAO in AbstractService class itself and abstractDAO gets Autowired depending upon the subclass maybe wth SPEL+Qualifier etc

We dont want to use any XML configuration for this

like image 353
Jeenson Ephraim Avatar asked Feb 22 '12 17:02

Jeenson Ephraim


People also ask

Can we use @autowired in abstract class?

We can't use @Autowired on a constructor of an abstract class. Spring doesn't evaluate the @Autowired annotation on a constructor of an abstract class. The subclass should provide the necessary arguments to the super constructor.

Is @autowired used for dependency injection?

Spring @Autowired annotation is used for automatic dependency injection. Spring framework is built on dependency injection and we inject the class dependencies through spring bean configuration file.

Can you Autowire by type when more than one being with the same type exist?

Autowiring using property type. Allows a property to be autowired if exactly one bean of property type exists in the container. If more than one exists, it's a fatal exception is thrown, which indicates that you may not used byType autowiring for that bean.

Can you Autowire by type?

This mode specifies autowiring by property type. Spring container looks at the beans on which autowire attribute is set to byType in the XML configuration file. It then tries to match and wire a property if its type matches with exactly one of the beans name in the configuration file.


2 Answers

I wouldn't do it like this. Indeed, there is a good chance that the ServiceClassA depends on some specific method of DaoClassA. In this case, you would have to cast the protected AbstractDAO to DaoClassA each time you want to call such a specific method.

I would make it generic, and reverse the way the dependencies are injected:

public class AbstractService<T extends AbstractDAO> {  
    protected T dao;

    protected AbstractService(T dao) {
        this.dao = dao;
    }

    // methods common to all the services
}

public class ServiceClassA extends AbstractService<DaoClassA> {
    @Autowired
    public ServiceClassA(DaoClassA dao) {
        super(dao);
    }

    // methods specific to ServiceClassA
}
like image 138
JB Nizet Avatar answered Oct 11 '22 23:10

JB Nizet


I was solving similar problem like you did. I found another way, you don't have to create setter methods. Instead of, use regular constructors, but use Spring autowiring. Here is complete code:

Service classes:

public abstract class AbstractService {

    protected final AbstractDAO dao;

    // Constructor forces you to inject dao in subclass
    public AbstractService(final AbstractDAO dao) {
        this.dao = dao;
    }

    public final void service() {
        // you can do something generic here with 'dao'
        // no matter which subclass is injected
        this.dao.doSomething();
    }
}

@Component
public class ServiceClassA extends AbstractService {

    @Autowired
    public ServiceClassA(@Qualifier("daoClassA") final AbstractDAO dao) {
        super(dao);
    }

}

@Component
public class ServiceClassB extends AbstractService {

    @Autowired
    public ServiceClassB(@Qualifier("daoClassB") final AbstractDAO dao) {
        super(dao);
    }

}

Notice @Qualifier("daoClassA") in subclass constructors

Field classes:

public interface AbstractDAO {    
    public void doSomething();
}

@Component
public class DaoClassA implements AbstractDAO {

    @Override
    public void doSomething() {
        System.out.println("I am DaoClassA");
    }    
}

@Component
public class DaoClassB implements AbstractDAO {

    @Override
    public void doSomething() {
        System.out.println("I am DaoClassB");
    }    
}

And finally, now you can call your generic service with concrete Service class and concrete DAO class: (of course you can autowire them somewhere)

((AbstractService) context.getBean("serviceClassA")).service();
((AbstractService) context.getBean("serviceClassB")).service();

will print:

I am DaoClassA
I am DaoClassB
like image 5
Gondy Avatar answered Oct 12 '22 01:10

Gondy