9

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

skaffman
  • 398,947
  • 96
  • 818
  • 769
Jeenson Ephraim
  • 551
  • 2
  • 9
  • 24
  • Is there any reason you can't put e.g. `@Autowired DaoClassA dao` into `ServiceClassA`? Why does the field need to be declared in `AbstractService`? – skaffman Feb 22 '12 at 17:51
  • Great question. I've always done something similar to the approach you describe (slightly different, but same basic idea) and I've always wanted something a little more automatic. Eager to see whether anybody has a good approach. –  Feb 22 '12 at 17:56
  • @skaffman In cases I've had, I wanted the AbstractService to have access to the AbstractDao so I could write general versions of CRUD operations, among other things. –  Feb 22 '12 at 17:57
  • 1
    There will be multiple implementations of AbstractService and we dont want to have the field declared in all classes extending AbstractService – Jeenson Ephraim Feb 22 '12 at 17:59

3 Answers3

8

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
}
JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • This is more similar to the approach I have used in the past, and it is nice for the reason you note--typesafety in the subclass. But it still has the demerit that it requires type-specific injection points. Is there any good way to avoid that? (I don't think there is, but if somebody has one, I'd love to know.) –  Feb 22 '12 at 18:03
  • But still it is similar to the original approach and all the subclasses will still have to do the work – Jeenson Ephraim Feb 22 '12 at 18:07
  • You can put a DaoClassA reference in the ServiceClassA and now it will be type-specific (no casting). –  Feb 22 '12 at 18:28
  • That would be like hardcoding a reference , annotaions is also similar to hardcoding but it would be more cleaner – Jeenson Ephraim Feb 22 '12 at 18:44
5

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
Gondy
  • 4,925
  • 4
  • 40
  • 46
0

No, there isn't. You cannot access to the class or bean name that is currently populating the AutowiredAnnotationBeanPostProcessor from SPEL.

You could override AbstractBeanFactory.evaluateBeanDefinitionString and add the beanDefinition as variable in the BeanExpressionContext . Then you can derive the Dao from the Service. using SPEL in @Value annotation.

Jose Luis Martin
  • 10,459
  • 1
  • 37
  • 38