2012-02-22 18 views
6

ho una classe astratta AbstractService che ha un riferimento a AbstractDAOAutowire a seconda della sottoclasse

class AbstractService{ 
    protected AbstractDAO abstractDAO; 
} 

AbstractService sarà esteso per classi di servizio effettivo come ServiceClassA, ServiceClassB ecc, e AbstractDAO sarà prorogato di DaoClassA, DaoClassB ecc

seconda di quale classe sta estendendo AbstractService, abstractDAO dovrebbe essere un'istanza di DaoClassA, DaoClassB ecc

posso realizzare questo avendo il setter abstractDAO nella classe che estende come

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

Esiste un modo per avere il setter setAbstractDAO in AbstractService classe stessa e abstractDAO ottiene Autowired a seconda della sottoclasse forse wth SPEL + Qualifier ecc

Non vogliamo usare qualsiasi configurazione XML per questo

+0

C'è qualche ragione per cui non si può mettere per esempio '@Autowired DaoClassA dao' in' ServiceClassA'? Perché il campo deve essere dichiarato in "AbstractService"? – skaffman

+0

Ottima domanda. Ho sempre fatto qualcosa di simile all'approccio che descrivi (leggermente diverso, ma la stessa idea di base) e ho sempre desiderato qualcosa di un po 'più automatico. Desideroso di vedere se qualcuno ha un buon approccio. –

+0

@skaffman Nei casi che ho avuto, volevo che l'AbstractService avesse accesso a AbstractDao per poter scrivere, tra le altre cose, le versioni generali delle operazioni CRUD. –

risposta

7

Non lo farei così. In effetti, ci sono buone probabilità che ServiceClassA dipenda da qualche metodo specifico di DaoClassA. In questo caso, dovresti trasmettere l'AbstractDAO protetto a DaoClassA ogni volta che vuoi chiamare un metodo così specifico.

ce l'avrei fatta generica, e invertire il modo in cui le dipendenze sono iniettati:

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 
} 
+0

Questo è più simile all'approccio che ho usato in passato, ed è bello per il motivo che noti - typesafety in la sottoclasse. Ma ha ancora il demerito che richiede punti di iniezione specifici per tipo. C'è un buon modo per evitarlo? (Non penso che ci sia, ma se qualcuno ne ha uno, mi piacerebbe saperlo.) –

+0

Ma ancora è simile all'approccio originale e tutte le sottoclassi dovranno ancora fare il lavoro –

+0

Puoi mettere un DaoClassA riferimento nel ServiceClassA e ora sarà specifico per tipo (senza casting). –

0

No, non c'è. Non è possibile accedere al nome della classe o del bean che sta attualmente compilando lo AutowiredAnnotationBeanPostProcessor da SPEL.

È possibile sovrascrivere AbstractBeanFactory.evaluateBeanDefinitionString e aggiungere beanDefinition come variabile nello BeanExpressionContext. Quindi puoi derivare il Dao dal Servizio. utilizzando SPEL nell'annotazione @Value.

2

Stavo risolvendo problemi simili come hai fatto tu. Ho trovato un altro modo, non devi creare metodi setter. Invece di utilizzare i costruttori regolari, ma utilizzare il ciclo di avvio automatico Spring. Ecco il codice completo:

classi di servizio:

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); 
    } 

} 

Avviso @Qualifier("daoClassA") nei costruttori delle sottoclassi

classi di campo:

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"); 
    }  
} 

E, infine, ora è possibile chiamare il servizio generico con classe di servizio concreta e classe DAO concreta: (ovviamente puoi autowire da qualche parte)

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

stamperà:

I am DaoClassA 
I am DaoClassB 
Problemi correlati