Login
Cerca all'interno di JavaPortal
Help
Home Page Documentazione Forum Progetti Partner Pubblica!
Documentazione > Tutorial > Esempi pratici con Spring - La Dependency Injection e il TDD - Seconda Parte
Modifica Impostazioni
Hide
Best Practices
EJB
Frameworks
Howto
J2EE
J2ME and Wireless
J2SE
JSP e Servlet
Java Application Server
Java IDE/Tools
Java Media
Java Security
Java Sys Admin
Java e XML
Java e SQL
OpenSource Java
Patterns
Repository
Tesi
UML
Web Services
Slide
White Paper di jws.it
project management
Eventi
Groovy

Hai una tesi in Java?
Tesine preparate
per esami?
Pubblica tutto su
JavaPortal!

Scrivi al nostro staff


Google Noop


Publio Cornelio Tacito
Tutte le cose che ora si credono antichissime furono nuove


Wiki Wiki Web


Rss Feed
Home Page
Articoli
News
Forum
Classi

  Visualizza Commenti (0) Aggiungi Commento    
Add to Shortcuts
 
Vota l'articolo
Esempi pratici con Spring - La Dependency Injection e il TDD - Seconda Parte
By Alessandro Rocca & Stefano Rossini
16 giugno 2008

  Esempi pratici con Spring - La Dependency Injection e il TDD - Seconda Parte
Program “Enterprise TDD”: un esempio pratico con Spring
Program Conclusioni
Program Bibliografia

Introduzione
Nello scorso articolo sono stati introdotti i concetti salienti riguardanti la best practice di sviluppo Test Driven Development (TDD) ed il pattern Dependency Injection (DI) [JIP_DITDD_I].
E’ stato detto come l’utilizzo di un Framework POJO (che basa il suo utilizzo sul pattern Dependency Injection-DI) agevoli il TDD in ambito Enterprise.
In questo articolo vedremo alcuni esempi pratici utilizzando il Framework Spring.


Spring
Spring è un Framework “leggero” per lo sviluppo di applicazioni J2EE che basa il suo funzionamento sul pattern Dependency Injection (DI) e consente di sviluppare applicazioni basate su POJO: Plain Old Java Object [RJ_ITTSF].


Figura 1 -  Overview Spring [SPRING]


Dato che le classi che implementano la logica di business sono POJO questo favorisce un maggiore disaccoppiamento tra logica di business e logica di accesso ai servizi infrastrutturali nonché una maggiore separazione delle competenze e responsabilità di ogni singolo oggetto.
L’implementazione della DI in Spring è costruita sull’interfaccia BeanFactory.
L' interfaccia BeanFactory rappresenta il "container" che istanzia, configura, e gestisce i bean; grazie al BeanFactory è possibile configurare e gestire qualsiasi oggetto, risolvere le dipendenze tra i vari oggetti e configurarne il ciclo di vita mediante opportuni file di configurazione XML.
Altra caratteristica di Spring, fondamentale in ottica TDD, è quella di fornire  “Out-of-Container” funzionalità J2EE.



“Enterprise TDD”: un esempio pratico con Spring top

Supponiamo di dovere sviluppare un Business Object (BO) il cui compito è di elaborare i dati, restituiti da un Data Access Object (DAO), che rappresentano le informazioni di Account di un utente.

L’interfaccia di business del nostro semplice BO (AccountBusinessObject) è composta da un solo metodo di business doFoo():

public interface AccountBusinessObject{
    public AccountModel doFoo(String id) throws BusinessException;
}


La classe concreta del Business Object (BO) implementa questa interfaccia

public class AccountBusinessObjectSpringImpl
    implements AccountBusinessObject {

e utilizza, nel metodo di business doFoo(), un oggetto DAO per accedere al Database (Pattern DAO):

public AccountModel doFoo(String id)throws BusinessException {
   try{
      AccountModel model = this.dataAccessObject.readAccount(id);
      model=this.businessLogic(model);
      return model;
   }
   catch(DaoException daoe){
      throw new BusinessException(daoe.getMessage());
   }
}


La classe DAO concreta implementa la seguente interfaccia AccountDao:

public interface AccountDao {
   public AccountModel readAccount(String pk) throws DaoException;
}


La classe di business viene sollevata dall’incarico di istanziare il DAO, a tale operazione penserà Spring. L’unica cosa necessaria è dichiarare nella classe di business una proprietà d’interfaccia AccountDao e il relativo metodo setter:

public class AccountSpringBusinessObjectImpl
        implements AccountBusinessObject {
   private AccountDao dataAccessObject;

   public void setDataAccessObject(AccountDao dao) {
      String method = "SpringBusinessObjectImpl.setDataAccessObject: ";
      this.dataAccessObject = dao;
   }


Sarà compito di Spring iniettare, mediante il relativo metodo setter, l’opportuno oggetto DAO configurato nel file XML.

A questo punto passiamo allo sviluppo della classe DAO e alla definizione delle varie dipendenze tra le classi all’interno del file di configurazione di Spring, nel nostro caso è la semplice relazione: “BO usa DAO”.

Supponiamo di non avere ancora il DBMS pronto, per non perdere tempo prezioso, possiamo “rimediare” sviluppando un DAO mock che, senza accedere realmente al DB, restituisca dei dati fittizi (hardcoded, configurati su file, …) in modo da permettere lo sviluppo della classe di Business e la predisposizione della suite di test:

public class AccountDaoMockImpl implements AccountDao{

   public AccountDaoMockImpl() throws DaoException{
   }

   public AccountModel readAccount(String pk) {
      AccountModel accountOM = null;
      // Leggo i dati mock da file (properties, xml, ...)
      // per semplicità in questo esempio i dati mock sono harcoded
      if(pk.equalsIgnoreCase("001")){
         accountOM = new new AccountModel("001", "Ale Maio", "amaio@aaa.it", 80.0);
      }
      return accountOM;
   }

La dipendenza tra il BO ed il DAO (quest’ultimo per il momento in versione mock) viene definita nel seguente file di configurazione xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
            "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
   <bean id="mockDAO" class="it.kok.samples.ditdd.dao.AccountDaoMockImpl"
                                                            singleton="true">
   </bean>
   <bean id="springBusinessObject"
        class="it.kok.samples.ditdd.business.AccountSpringBusinessObjectImpl">
      <property name="dataAccessObject" ref="mockDAO" />
   </bean>
</beans>


Con questa configurazione l’environment Spring inietta nella nostra classe it.kok.samples.ditdd.business.AccountSpringBusinessObjectImpl  l’oggetto it.kok.samples.ditdd.dao.AccountDaoMockImpl mediante il metodo: AccountBusinessObjectSpringImpl.setDataAccessObject():

Non rimane che sviluppare la classe di test unit:

public class TestUnitAccountBusinessObject extends TestCase{

   public void testBoDoFoo() {
      try {    
         ApplicationContext ctx
            = new FileSystemXmlApplicationContext(SPRING_CONFIG_FILE);
         AccountBusinessObject bo =
            (AccountBusinessObject) ctx.getBean("springBusinessObject");
         // Test utente esistente
         AccountModel res=bo.doFoo("001");
           
         assertNotNull(res);
         assertEquals("001", res.getId());
         assertEquals("Ale Maio", res.getName());
         assertEquals("amaio@aaa.it", res.getAddress());           
         assertEquals(80.0, res.getBalance()); 
  
         // Test utente NON esistente
         res=bo.doFoo("123");
         assertNull(res);
           
     }catch(Throwable t) {
          fail(t.getMessage());
       }
    }

 

 Come si può vedere siamo in grado di testare le funzionalità del BO POJO in un ambiente cosiddetto Unmanaged (Out-of-Container), e quindi in assenza sia dell’Application Server che del Database.

 

Figura 2 -  Scenario di test del Business Object con Dao mock in ambiente Unmanaged


Una volta pronto il DBMS si procede con lo sviluppo del DAO vero e proprio sostituendo quello Mock.
Per la parte di accesso ai dati usufruiamo di un altro Framework java, Hibernate. Lo scopo principale di Hibernate è quello di fornire una soluzione Object-Relational Mapping (ORM), ovvero di mappare classi Java (Plain Old Java Object) con le tabelle di un database relazionale; sulla base di questo mapping, Hibernate fornisce uno strato di accesso ai dati trasparente all'utilizzatore.

La classe concreta DAO viene creata implementando l’interfaccia AccountDao (la stessa interfaccia che veniva implementata precedentemente dalla classe Mock):

public class AccountDaoSpringImplHybernate implements AccountDao{

   private String CLASS_NAME = "AccountDaoSpringImplHybernate";
   
   private SessionFactory sessionFactory = null;

   public AccountDaoSpringImplHybernate() throws DaoException{
   }

   public void setSessionFactory(SessionFactory sessionFactory) {
    this.sessionFactory = sessionFactory;
   }

   public AccountModel readAccount(String pk) {

      HibernateTemplate template = new HibernateTemplate(sessionFactory);
      // lettura account
      AccountModel accountOM=(AccountModel)template.get(AccountModel.class, pk);
     return accountOM;
   }

Il file di mapping Hybernate per l’esempio proposto è il seguente:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="it.kok.damples.ditdd.dao">
   <class name="it.kok.samples.ditdd.dao.AccountModel" table="account">
      <id name="id" column="id"> 
           <generator class="native"/>
      </id>
      <property name="name" type="java.lang.String" column="name"/>
      <property name="address" type="java.lang.String" column="address"/>
      <property name="balance" type="double" column="balance"/>
   </class>
</hibernate-mapping>


A questo punto è nostro interesse verificare che mapping Hybernate (e il relativo SQL generato) siano corretti. Anche in questo caso puntiamo inizialmente a verificare il corretto funzionamento del DAO eseguendo il test in ambiente Unmanaged.

Per questo test del DAO Out-Of-Container, nel file XML di Spring si devono specificare le proprietà:
•    driverClassName
•    url
•    username
•    password

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"             destroy-method="close">
      <property name="driverClassName" value="org.gjt.mm.mysql.Driver" />
      <property name="url" value="jdbc:mysql://localhost:3306/kokdb" />
      <property name="username" value=""/>
      <property name="password" value=""/>
   </bean>

    <bean id="mySessionFactory"                                                         class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
      <property name="dataSource" ref="myDataSource"/>
      <property name="mappingResources">
         <list>
            <value>account.hbm.xml</value>
         </list>
      </property>
      <property name="hibernateProperties">
         <value>
            hibernate.dialect=org.hibernate.dialect.MySQLDialect
            hibernate.show_sql=true
         </value>
      </property>
  </bean>

    <bean id="hibernateDAO"                                                             class="it.kok.samples.ditdd.dao.AccountDaoSpringImplHybernate" singleton="true">
     <property name="sessionFactory" ref="mySessionFactory" />
   </bean>

</beans>


Sviluppiamo ed eseguiamo un test di unità per verificare il corretto funzionamento del DAO appena sviluppato:

public class TestUnitAccountDaoHybernate extends TestCase {
   public void testSelect() {
      try {
         // Read the configuration file
         ApplicationContext ctx =
                 new FileSystemXmlApplicationContext(SPRING_CONFIG_FILE);

         //Instantiate an object
         AccountDaoSpringImplHybernate dao =
            (AccountDaoSpringImplHybernate) ctx.getBean("hibernateDAO");
         AccountModel res=dao.readAccount("001");
         assertNotNull(res);
         assertEquals("001", res.getId());
         assertEquals("Ale Maio", res.getName());
         assertEquals("amaio@aaa.it", res.getAddress());           
         assertEquals(80.0, res.getBalance());

            . . .

         // Test utente non esistente
         res=dao.readAccount("123");
         assertNull(res);
           
      } catch(Exception ex) {
         ex.printStackTrace();
         fail(ex.getMessage());
      }
   }

Una volta ottenuto verde, rieseguiamo, sempre in ambiente Unmanaged,  il test relativo al BO in modo da verificarne il corretto funzionamento con il DAO appena sviluppato.

Solo quando tutti i test Out-of-Container risulteranno verdi, ovvero dopo aver certificato  la correttezza del codice sviluppato (nel nostro caso il BO e il DAO), saremo pronti a procedere con il deploy nel Container e a rieseguire i precedenti test in un ambiente Managed (In-Container).
Di fronte a uno scenario in cui i test Out-of-Container  hanno esito positivo (verde) e i test In-Container hanno esito negativo (rosso), il problema è da imputare al deploy o alla configurazione dell’Application Server, l’errore è già ben circoscritto e la problem determination già indirizzata.



Figura 3 -  Scenario di test Out-Of-Container del Business Object con il DB


Notare come con Spring le risorse vengono iniettate dall’esterno evitando di “sporcare” le nostre classi, le quali risultano essere così indipendenti dall’ambiente di esecuzione (Managed, Unmanaged) e più semplici da testare.

Con la Dependency Injection è possibile spostare le nostre classi da un ambiente di test all’altro cambiando opportunamente i file di configurazione Spring che pilotano la DI. Se nello scenario Out-of-Container (Unmanaged) era necessario specificare tutte le configurazioni (DataSource, SessionFactory, DAO, BO ) nello scenario In-Container (Managed) non è necessario specificarle tutte in quanto alcune informazioni sono già presenti nei file di deploy (es: configurazione del DataSource all’interno del file mysql-ds.xml nel caso di JBoss) comportando una modifica del file di configurazione Spring atta a recuperare le risorse da iniettare attraverso JNDI (org.springframework.jndi.JndiObjectFactoryBean).


   

Figura 4 -  Differenze di configurazione tra ambiente Unmanaged e Managed


Per usare il nostro BO in un Application Server (es: JBOSS), ad esempio per essere utilizzato da un EJB, senza dovere toccare alcuna riga di codice  possiamo procedere al seguente deploy:



Figura 5 -  Utilizzo del BO da un EJB (EJB Container JBoss)


Analogo discorso è possibile farlo nel caso in cui il BO debba essere usato in un Servlet Container (es: TOMCAT).
Sempre a parità di .class, in caso di chiamata del BO da parte di una Servlet, il deploy risulta come riportato nell’immagine sottostante:

 

Figura 6 -  Utilizzo del BO all’interno di una WebApp (Servlet Container Tomcat) 



Conclusioni top
In questo articolo abbiamo visto in pratica, grazie al Framework Spring, come la Dependency Injection renda semplice l’esecuzione del proprio codice al di fuori degli Application Server permettendo di testare le proprie classi Java in modo isolato, agile ed efficace.

Nel prossimo articolo presenteremo lo stesso esempio pratico basandoci sul Framework EJB 3.0.



Bibliografia top
[JIP_DITDD_I]  A. Rocca, S. Rossini: La Dependency Injection e il TDD in Applicazioni Enterprise

[RJ_ITTSF] R.Johnson: Introducing to the Spring Framework

[SPRING] http://www.springframework.org

Username:
Password:
To sign up for an account, click register... Register
Hide





Powered By



Campagna Anti-IF


Skin


PARTNER
Zio Budda
HostingJava


LICENZA



Eccetto dove diversamente specificato, i contenuti di questo sito sono rilasciati sotto licenza Creative Commons

Sitemap  © 2002-2004 Copyright Information. Privacy . Today is domenica 1 agosto 2010