Comercialización:
ventas@acsinet-solutions.com
Servicios Educacionales:
educacional@acsinet-solutions.com
Servicios de Consultoría:
consultoría@acsinet-solutions.com
Comercialización:
soporte@acsinet-solutions.com

Teléfonos:
58 93 36 63
Oficina:
58 93 36 63
Fax:
58 93 36 63
Celulares:
044 55 54 56 77 45







Mapeo Sencillo con Configuración AOP de Spring Framework

Esta serie de documentos tiene por objetivo presentar ejemplos de tipos de mapeos que se pueden realizar con Hibernate 3 y Java Persistence API ( JPA ). La intención es cubrir paulatinamente con ejemplos desde los mapeos más sencillos, hasta los más complicados.

En este tercer documento se configura funcionalidad AOP a Spring Framework para automatizar el manejo de conexiones y transacciones en una aplicación, y de esta manera simplificar enormemente el código de persistencia de una aplicación empresarial.

Una aplicación que maneja manualmente el código de conexión a base de datos y las transacciones tiende a repetir mucho código “de plomería”. Especificaciones como EJB fueron diseñadas en principio para eliminar ese tipo de responsabilidades de los hombros del programador; pero como se muestra en este tutorial este tipo de soluciones no están limitadas a aplicaciones que son ejecutadas en un EJB container.



Código Repetido

Comencemos por analizar uno de los métodos que se ha venido utilizando hasta el momento para la inserción de un registro hacia la base de datos:



   public int insertObject
   ( FacturaDTO factura )
   {
      Session session =
         _sessionFactory.openSession();

      try
         { insertObject( session, factura ); }
      finally
         { session.close(); }

      return factura.getId();
   }

   private void insertObject
  ( Session session, FacturaDTO factura )
   {
      boolean commit = false;
      Transaction transaction =
         session.beginTransaction();

      try
      {
         session.save( factura );

         commit = true;
      }
      finally
      {
         if( commit )
            transaction.commit();
         else
            transaction.rollback();
      }
   }

El listado anterior contiene bloques de código que se repiten para métodos similares:

Si se considera el efecto acumulado del código que se repite en clases de lógica de negocio, aún tan sencillas como las que se muestran en estos tutoriales, el efecto acumulado es considerable, y oscurece la lógica de negocio propia de la aplicación.

Lo ideal en este caso es poder reutilizar de alguna manera todo este código. La pregunta es ż cómo ?

El tipo de código repetido que se maneja en estas clases no es fácil de extraer por medio de estrategias básicas en programación orientada a objetos como son el manejo de delegación o de herencia. Esto se debe a que este tipo de código no se presta a ser separado al estar inmerso en métodos diferentes con firmas diferentes.

La aplicación de algunos patrones de diseño específicos pueden ayudar a extraer efectivamente este código, aunque en este caso se requerirá implementar arquitecturas particulares en un desarrollo. A veces la complejidad introducida por una arquitectura no justifica su aplicación; y en ocasiones sí lo justificará. Sin embargo, la pregunta sigue en el aire: ż existe una alternativa sencilla y práctica de reutilizar este código ?



Aspect Oriented Programming ( AOP )


La programación por aspectos es una extensión de la programación orientada a objetos ( OOP ), y buscar resolver problemas de reuso de código en situaciones en que ni la herencia ni la delegación pueden ser utilizadas de forma satisfactoria. El ejemplo de reuso de código de conexiones y transacciones es una muestra de un tipo de estas situaciones.

A este tipo de código que se repite en diferentes métodos de diferentes clases en una aplicación, cuando dichos métodos no tienen relación alguna entre sí ( en el tipo de interfase que usan, los parámetros que reciben y sus valores de retorno ) se le denomina típicamente crosscutting concerns. Además de los ejemplos que se han dado, otros ejemplos de este tipo de código es el manejo de seguridad declarativa, manejo de bitácoras, código de auditoría, manejo remoto de objetos, mecanismos transparentes de activación / pasivación de servicios, etc.

Una de las características más importantes dentro de AOP es la habilidad de definir interceptores; esto es, código que se ejecute siempre alrededor de otro de forma transparente hacia el cliente. Suponiendo que un método contiene la lógica necesaria para la inserción de datos hacia una tabla, el código que abre y cierra la conexión, y que hace el procesamiento de transacciones se puede definir en un interceptor como se muestra en la siguiente imagen:

Este mecanismo permite extraer todo el código repetido en los métodos de la clase ObjectManagerImpl.

Mejor aún, el mismo interceptor puede ser aplicado a diferentes clases con diferentes métodos que requieran extraer el código repetido en todas ellas:

En pocas palabras, un mismo interceptor puede ser reusado sobre múltiples clases de lógica de negocio en una aplicación.

La clave para que el manejo de interceptores AOP sea transparente a un cliente está en el manejo de interfases para las clases que implementan el servicio. Si el cliente dependiera directamente de una implementación, sería más difícil agregar un intermediario que ejecutara la lógica para el manejo de conexiones y transacciones:

En cambio, desde tutoriales pasados se ha insistido que el cliente dependa de una interfase ( ObjectManager ) y no de una implementación ( ObjectManagerImpl ):

En un escenario de este estilo, cuando aún no se han definido interceptores a un programa, las invocaciones se realizan directamente sobre la implementación; aunque la dependencia que se tiene en la clase cliente se realiza sobre la interfase. Entender con claridad este punto es esencial.

Podemos ver la dependencia que el cliente sobre la interfase ( y no la implementación ), si verificamos el siguiente fragmento de código asociado a la clase Test.java:



  /**
   * Método de ejecución de altas, bajas,
   * cambios y consultas.
   */
   private void execute() throws Exception
   {
     ObjectManager manager = getObjectManager();

     ...
   }

De esta manera, es posible agregar un intermediario que trabaje de forma transparente al cliente.

Hay un patrón de diseño llamado “Proxy” que describe este tipo de arquitecturas. De hecho, una traducción al español de la palabra inglesa “Proxy” es “Intermediario”. El interceptor AOP del que hemos venido hablando implementa dinámicamente las interfases de negocio de los servicios que queremos extender, y por lo tanto podría describirse como un proxy dinámico ( dynamic proxy ).

Existen muchas maneras para realizar manejo de AOP en java. Algunas librerías se dedican a definir maneras de integrar esta funcionalidad a la plataforma siguiendo distintas aproximaciones . Ejemplos de ellas son:

Mediante el soporte a AOP definido en Spring Framework, es posible implementar la funcionalidad para manejo de conexiones y transacciones de la que se ha hablado hasta este momento.

De hecho, no es ni siquiera necesario crearlos, ya que Spring Framework incluye todas las clases necesarias para definir funcionalidad AOP para el manejo de conexiones y transacciones en clases desarrolladas con JDBC, Hibernate, JPA, JDO y otras aproximaciones. Unicamente hay que declarar los servicios, y personalizarlos a través del uso de anotaciones sencillas.

Las siguientes secciones mostrarán cómo se hace esto.



Estructura de la Aplicación

La siguiente imagen ilustra la estructura de la aplicación que se presenta en este tutorial:

Como puede verse, es la misma estructura manejada en el tutorial básico de Spring Framework. La diferencia será en los servicios definidos, y en el código de la interfase y la implementación del servicio.

El código del DTO que se mapea hacia la base de datos no cambiará significativamente con respecto a los tutoriales anteriores, ni tampoco el código de la clase de prueba utilizada. Por lo tanto, no se hablará de estos elementos a lo largo de este documento.

Todo el código de esta aplicación puede ser obtenido a partir de aquí.



Configuración de Spring Framework: services.xml


En este caso, al archivo de configuración de Spring Framework se le agregará soporte para un servicio de transacciones, y también se le agregarán algunos servicios adicionales para el manejo de AOP.

El contenido de este archivo quedaría finalmente como sigue:



  <?xml version='1.0'? >

  <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
     "http://www.springframework.org/dtd/spring-beans.dtd">

  <beans default-autowire="byName">

     <bean id="dataSource"
       class="org.apache.commons.dbcp.BasicDataSource"
       destroy-method="close">
       <property name="driverClassName">
         <value>...</value>
       </property>
       <property name="url">         <value>...</value>
      </property>
       <property name="username">
        <value>...</value>
      </property>
       <property name="password">
        <value>...</value>
      </property>
     </bean>

     <bean id="sessionFactory" class=
     "org.springframework.orm.hibernate3.annotation.
      AnnotationSessionFactoryBean">

       <property name="annotatedClasses">
         <list>
           <value>
           single_table_simple_key_spring.FacturaDTO
          </value>
         </list>
       </property>

       <property name="hibernateProperties">
         <props>
           <prop key="hibernate.dialect">
           org.hibernate.dialect.InformixDialect
         </prop>
         </props>
       </property>
   </bean>

   <bean id="transactionManager"
   class="org.springframework.orm.hibernate3.HibernateTransactionManager"/>

   <bean id="transactionInterceptor"
     class= "org.springframework.transaction.interceptor.
     TransactionInterceptor">
     <property name="transactionAttributeSource">
       <bean class=
         "org.springframework.transaction.annotation.
         AnnotationTransactionAttributeSource" />
     </property>
   </bean>

   <bean class=
     "org.springframework.aop.framework.autoproxy.
     DefaultAdvisorAutoProxyCreator" />

   <bean class=
     "org.springframework.transaction.interceptor.
     TransactionAttributeSourceAdvisor" />

   <!-- ADMINISTRADORES -->

     <bean id="objectManager"
     class="single_table_simple_key_spring.
       ObjectManagerImpl"/>

   </beans>

Visualmente, podríamos ilustrar el contenido de este archivo como sigue:

Nótese la dependencia de los servicios entre sí. El administrador de transacciones depende de la fábrica de sesiones Hibernate; y la definición de los interceptores depende del administrador de transacciones ( ya que los servicios AOP controlarán las transacciones para los servicios ).



Servicio de DataSource JDBC

En este caso, el servicio de transacciones se define simplemente por el siguiente elemento:



  <bean id="transactionManager"
    class="org.springframework.orm.hibernate3.
      HibernateTransactionManager"/>

En este caso se define un servicio de transacciones basado en Hibernate 3; este servicio depende del servicio de Hibernate, pero como se está manejando enlazado automático de servicios ( autowiring ) no es necesario indicar dicha dependencia. De no manejarse autoenlazado, el servicio debería definirse como sigue:



   <bean id="transactionManager"
     class="org.springframework.orm.hibernate3.
      HibernateTransactionManager">
     <property name="sessionFactory">
      <ref local="sessionFactory"/>
     </property>
   </bean>

Esta implementación es más que suficiente para aplicaciones que ejecutan en ambientes Java SE.

Sin embargo, Spring Framework puede ser configurado para trabajar en ambientes Java EE donde existan instancias de tipo DataSource definidas en JNDI, y el manejo de transacciones se realice por medio de JTA.

En este tipo de ambientes, la definición de los servicios de conexión a base de datos y de transacciones se manejaría por medio de las siguientes implementaciones alternativas:



  <bean id="dataSource" class=
     "org.springframework.jndi.JndiObjectFactoryBean">
     <property name="jndiName">
       <value>
        java:comp/env/jdbc/ds1
       </value>
     </property>
   </bean>

   <bean id="transactionManager" autowire="no"
     class="org.springframework.transaction.jta.
     JtaTransactionManager"/>

Por otra parte, en aplicaciones Java SE con Spring Framework donde se trabaje con una sola conexión JDBC, pero no se maneje Hibernate, el siguiente administrador de transacciones puede ser utilizado:



   <id="transactionManager" class=
     "org.springframework.jdbc.datasource.
     DataSourceTransactionManager">
     <property name="dataSource">
      <ref local="dataSource"/>
     </property>
   </bean>


Servicios AOP

La definición de los servicios relacionados con AOP en el archivo de configuración services.xml es un poco más extensa:



   <bean id="transactionInterceptor"
     class="org.springframework.transaction.interceptor.
     TransactionInterceptor">
     <property name="transactionAttributeSource">
       <bean class=
         "org.springframework.transaction.annotation.
         AnnotationTransactionAttributeSource" />
     </property>
   </bean>

   <bean class=
     "org.springframework.aop.framework.autoproxy.
     DefaultAdvisorAutoProxyCreator" />

   <bean class=
     "org.springframework.transaction.interceptor.
     TransactionAttributeSourceAdvisor"/>

De esta manera se configura la creación automática de interceptores para las clases de servicios que así lo requieran.

La buena noticia es que se trata de código que se agrega una vez, y luego nos olvidamos de él. No hay que personalizarlo ni estar dándole mantenimiento. Y si se compara este código XML con todo lo que se ahorrará en las clases de implementación, se apreciará realmente todo el trabajo que nos ahorrará.

Si no se estuviera utilizando autoenlazado en el archivo de configuración, el código anterior debería incluir la referencia a los servicios respectivos:



   <bean id="transactionInterceptor" class=
        "org.springframework.transaction.interceptor.
         TransactionInterceptor">
     <property name="transactionManager"
       ref="transactionManager"/>
     <property name="transactionAttributeSource">
       <bean class=
         "org.springframework.transaction.annotation.
           AnnotationTransactionAttributeSource" />
     </property>
   </bean>

   <bean class=
     "org.springframework.aop.framework.autoproxy.
       DefaultAdvisorAutoProxyCreator" />
   <bean class=
     "org.springframework.transaction.interceptor.
       TransactionAttributeSourceAdvisor">
     <property name="transactionInterceptor"
       ref="transactionInterceptor"/>
   </bean>

Spring Framework permite indicar los servicios que trabajarán con AOP directamente sobre el archivo XML, o directamente sobre los servicios que trabajarán de esta manera a través de anotaciones. En este caso se manejará el segundo caso, debido a que es más práctico y sencillo, además de que es el enfoque utilizado para realizar este tipo de labores a partir de EJB 3. El soporte a anotaciones en Spring Framework se realiza por medio de la clase AnnotationTransactionAttributeSource, que se configura como parte del código anterior.



Interfase de Lógica de Negocio: ObjectManager.java





La interfase de lógica de negocio ahora incluirá metadata para configurar el interceptor AOP; esta metadata se define en forma de anotaciones J2SE 5.0. El código asociado a esta interfase quedaría finalmente como sigue:



   package single_table_simple_key_spring;

  import org.springframework.transaction.
     annotation.Propagation;
  import org.springframework.transaction.
    annotation.Transactional;

  /**
  * Interfase que define un servicio para el manejo
  * de facturas.
  */
  @Transactional( rollbackFor=Exception.class )
  public interface ObjectManager
     {
     @Transactional( propagation=Propagation.REQUIRED )
     public int insertObject( FacturaDTO factura );

     @Transactional( propagation=Propagation.SUPPORTS,
        readOnly = true )
     public FacturaDTO getObject( int id );

     @Transactional( propagation=Propagation.SUPPORTS,
        readOnly = true )
     public FacturaDTO queryObject( int id );

     @Transactional( propagation=Propagation.SUPPORTS,
        readOnly = true )
     public int findMaxKey();

     @Transactional( propagation=Propagation.REQUIRED )
     public void updateObject( FacturaDTO factura );

     @Transactional( propagation=Propagation.REQUIRED )
     public void deleteObject( int id );
     }


Nótese que ahora la interfase y sus métodos han sido marcadas utilizando la anotación org.springframework.transaction.annotation.Transactional. Esta anotación es la que indicará la transaccionalidad de un método, y abrirá y cerrará conexiones y transacciones para los métodos que lo requieran.



Esta anotación es reconocida por las características de AOP de Spring Framework que fueron configuradas según la discusión realizada en la sección anterior, y de esta manera se creará un interceptor AOP de forma transparente al resto del código de la aplicación.



Si se coloca la anotación @Transactional a nivel interfase, ésta aplicará para todos los métodos de la interfase; cualquier definición realizada a nivel método redefinirá la declaración realizada a nivel clase.



El definir a nivel interfase @Transactional( rollbackFor=Exception.class ) indica que todos los métodos tendrán transaccionalidad requerida ( por defecto ), y que cualquier excepción de tipo checked lanzada por un método ocasionará un rollback a la transacción. Por defecto, solamente las excepciones de tipo runtime ocasionan rollback a las transacciones.





Posteriormente, a nivel método se indica qué métodos son transaccionales ( transaccionalidad requerida ) y cuáles no ( transaccionalidad soportada ). Por defecto, todos los métodos tendrán transaccionalidad requerida, por lo que las declaraciones que dicen @Transactional( propagation=Propagation.REQUIRED ) son innecesarias. Con esto se quiere decir que el código de la interfase podría reescribirse simplemente como:



   package single_table_simple_key_spring;

   import org.springframework.transaction.
    annotation.Propagation;
   import org.springframework.transaction.
    annotation.Transactional;

   /**
   * Interfase que define un servicio para el manejo
   * de facturas.
   */
   @Transactional( rollbackFor=Exception.class )
   public interface ObjectManager
     {
     public int insertObject( FacturaDTO factura );

     @Transactional( propagation=Propagation.SUPPORTS,
        readOnly = true )
     public FacturaDTO getObject( int id );

     @Transactional( propagation=Propagation.SUPPORTS,
        readOnly = true )
     public FacturaDTO queryObject( int id );

     @Transactional( propagation=Propagation.SUPPORTS,
        readOnly = true )
     public int findMaxKey();

     public void updateObject( FacturaDTO factura );

     public void deleteObject( int id );
     }


La constante SUPPORTS indica que el método no utilizará transacciones, a menos que sea invocado desde otro servicio donde una transacción ya haya sido abierta con anterioridad.



Mediante el manejo de estas constantes se pueden combinar de forma transparente métodos de diferentes servicios de lógica de negocio en una sola transacción, lo cual sería difícil de lograr adecuadamente si la transaccionalidad se controlara de forma manual.





Las constantes REQUIRED y SUPPORTS se modelaron en Spring Framework basadas en valores definidos por la especificación EJB. Existen otras constantes definidas por la enumeración org.springframework.transaction.annotation.Propagation. Un resumen de dichas constantes se muestra en la siguiente tabla:



REQUIRED El componente requiere una transacción abierta para operar. Si un componente invoca a éste y ya ha abierto una transacción con anterioridad, dicha transacción es utilizada ( como se ilustra en la imagen anterior ); de lo contrario, se crea la transacción.
REQUIRES_NEW El componente requiere una nueva transacción para operar. No se aprovecha una transacción existente aunque exista.
MANDATORY El componente requiere forzosamente que se haya abierto una transacción con anterioridad. Si la transacción no existe, se lanzará una excepción.
SUPPORTS El componente soporta el manejo de transacciones, y las utilizará si hay alguna definida con anterioridad; de lo contrario, trabajará de forma transaccional.
NOT_SUPPORTED El componente trabajará de forma no transaccional aunque sea invocado desde otro que haya abierto previamente una transacción.
NEVER El componente trabajará de forma no transaccional; si existe una transacción abierta previamente, se lanzará una exepción.


En otras palabras, mediante el uso de REQUIRED, REQUIRES_NEW y MANDATORY existirá una conexión y una transacción abierta para cada uno de los servicios; y mediante el uso de SUPPORTS, NOT_SUPPORTED y NEVER existiría únicamente una conexión.



En escenarios simples como los que se muestran en este tutorial, el manejo de REQUIRED y SUPPORTS son suficientes para cubrir la funcionalidad que se requiere. Si un método termina de forma normal, una transacción es cerrada ( COMMIT ) en caso de existir; y si un método lanza una excepción, la transacción es abortada ( ROLLBACK ).



Implementación de Lógica de Negocio: ObjectManagerImpl.java





El código asociado a la implementación de la lógica de negocio se simiplificará muchísimo, ya que toda la creación y cierre de conexiones, y la apertura y cierre de transacciones se eliminará de la clase.



Por ejemplo, el código relacionado con la inserción de un registro se reduciría simplemente a lo siguiente:



  public int insertObject( FacturaDTO factura )
     {
       Session session = _sessionFactory.getCurrentSession();
       session.save( factura );
       return factura.getId();
     }


Este método se ha reducido a básicamente el almacenado del registro en base de datos, y a regresar el identificador asignado por el mecanismo de persistencia.



Si se compara este código con el método presentado al inicio de este tutorial, la diferencia es bastante notable.



Por su parte, la lectura de un registro a partir de la base de datos quedaría como sigue:



   public FacturaDTO getObject( int id )
   {
     Session session =
       _sessionFactory.getCurrentSession();
       return ( FacturaDTO )session.
         load( FacturaDTO.class, id );
   }


El resto de los métodos también se reduciría bastante, y se muestran en el siguiente fragmento de código:



  public FacturaDTO queryObject( int id )
   {
     Session session =
      _sessionFactory.getCurrentSession();

     Criteria criteria = session.
       createCriteria( FacturaDTO.class );

     criteria.add( Expression.eq( "id", id ) );

     List results = criteria.list();

     return ( FacturaDTO )results.get( 0 );
   }

   public int findMaxKey()
   {
     Session session =
      _sessionFactory.getCurrentSession();

     Query query = session.
     createQuery("SELECT MAX( factura.id )
      FROM FacturaDTO factura");

     return ( Integer )query.uniqueResult();
   }

   public void updateObject( FacturaDTO factura )
   {
     Session session =
       _sessionFactory.getCurrentSession();

     session.update( factura );
   }

  public void deleteObject( int id )
   {
     Session session =
       _sessionFactory.getCurrentSession();

     Query query = session.createQuery
     ( "DELETE FROM FacturaDTO factura
       WHERE factura.id = :id" );

     query.setInteger( "id", id );

     query.executeUpdate();
   }


La invocación a estos métodos siempre se realiza a través de un interceptor AOP.



En el caso en el que la configuración de los interceptores no se haya realizado de forma adecuada, el código asociado a dichos interceptores no aparecerá en el stack trace de las excepciones, y eso nos dará una indicación de que dicha configuración no se realizó de forma adecuada, por ejemplo:



   [ERROR]17/10/2007 14:12:33,652 (Test): Error:
  org.hibernate.HibernateException:
    No Hibernate Session bound
  to thread, and configuration   does not allow
  creation of non-transactional one here
   at
   org.springframework.orm.hibernate3.
 s; LocalSessionFactoryBean$
  TransactionAwareInvocationHandler.
  invoke(Local  SessionFactoryBean.java:1113)
   at $Proxy8.getCurrentSession(Unknown Source)
   at single_table_simple_key_spring.ObjectManagerImpl.
  insertObject(ObjectManagerImpl.java:33)
   at single_table_simple_key_spring.Test.execute(Test.java:36)
   at single_table_simple_key_spring.Test.main(Test.java:20)


En este caso puede verse que entre el método execute() de la clase Test, y el método insertObject() de la clase ObjectManagerImpl, no existe ninguna invocación intermedia que indique la existencia de un intermediario AOP. Esto implica que este código está operando de forma directa como en la siguiente imagen, y que por lo tanto por ello no se ha abierto ninguna conexión ni transacción:





Esto implicaría que la referencia no fue inicializada, debido a que el método setSessionFactory() no fue invocado. Las razones para esto pueden ser los siguientes:



  [ERROR]17/10/2007 14:19:15,548 (Test): Error:
   java.lang.IllegalArgumentException
   at single_table_simple_key_spring.ObjectManagerImpl.
    insertObject(ObjectManagerImpl.java:33)
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   at sun.reflect.NativeMethodAccessorImpl.
    invoke(NativeMethodAccessorImpl.java:39)
   at sun.reflect.DelegatingMethodAccessorImpl.
    invoke(DelegatingMethodAccessorImpl.java:25)
   at java.lang.reflect.Method.invoke(Method.java:597)
   at org.springframework.aop.support.AopUtils.
    invokeJoinpointUsingReflection(AopUtils.java:287)
   at org.springframework.aop.framework.ReflectiveMethodInvocation.
    invokeJoinpoint(ReflectiveMethodInvocation.java:181)
   at org.springframework.aop.framework.ReflectiveMethodInvocation.
    proceed(ReflectiveMethodInvocation.java:148)
   at org.springframework.transaction.interceptor.
    TransactionInterceptor.invoke(TransactionInterceptor.java:100)
   at org.springframework.aop.framework.ReflectiveMethodInvocation.
    proceed(ReflectiveMethodInvocation.java:170)
   at org.springframework.aop.framework.JdkDynamicAopProxy.
    invoke(JdkDynamicAopProxy.java:176)
   at $Proxy11.insertObject(Unknown Source)
   at single_table_simple_key_spring.Test.execute(Test.java:36)
   at single_table_simple_key_spring.Test.main(Test.java:20)


Nótese que en este caso existen invocaciones entre el método execute() de la clase Test, y el método insertObject() de la clase ObjectManagerImpl. Esto quiere decir que el proxy está siendo creado, y que el error que se presenta no se refiere a esto. La interpretación correcta de stack traces es esencial durante el desarrollo de aplicaciones sobre plataforma java; la serie de tutoriales destinada a interpretar errores que está en este mismo sitio web trata a detalle este tema.



Conclusiones



El manejo de estrategias como AOP simplifica enormemente la labor de codificación de una aplicación, lo cual facilita el mantenimiento de la aplicación, y combinado con el manejo de servicios utilizando aproximaciones como Spring Framework, se promueve su modularidad.





Regresar






Copyright © ACSINET S.A. de C.V. 2007 http://www.acsinet-solutions.com

Derechos Reservados de este Material:
Atribución-No Comercial-Licenciamiento Recíproco 2.5 México

http://creativecommons.org/licenses/by-nc-sa/2.5/mx/

Eres libre de: a) copiar, distribuir y comunicar públicamente la obra, b) hacer obras derivadas. Bajo las condiciones siguientes: a) Atribución. Debes reconocer la autoría de la obra en los términos especificados por el propio autor o licenciante. b) No comercial. No puedes utilizar esta obra para fines comerciales. c) Licenciamiento Recíproco. Si alteras, transformas o creas una obra a partir de esta obra, solo podrás distribuir la obra resultante bajo una licencia igual a ésta.
Al reutilizar o distribuir la obra, tiene que dejar bien claro los términos de la licencia de esta obra. Alguna de estas condiciones puede no aplicarse si se obtiene el permiso del titular de los derechos de autor. Nada en esta licencia menoscaba o restringe los derechos morales del autor.

Java and all Java-based trademarks and logos are trademarks of Sun Microsystems in the United States and/or other countries.

Other products and services are trademarks of their respective owners.




Copyright © ACSINET S.A. de C.V. 2000 - 2007 Derechos Reservados.


Java™ is a trademark of Sun Microsystems in the United States and/or other countries All other products and services are trademarks of their respective owners.