65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 AspectJ (AOP) 持久化到数据库

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.75/5 (4投票s)

2004 年 8 月 30 日

6分钟阅读

viewsIcon

43450

downloadIcon

583

在 AspectJ 中使用面向切面编程 (AOP) 的一种将对象持久化到数据库的方法。

使用 AspectJ (AOP) 持久化到数据库


摘要
为类的对象在数据库中实现持久化的多种方法之一。

作者
Mariano Lorente
用 AspectJ 编程

日期
2004年8月30日

信号强度
中心

环境
Eclipse, Java, AspectJ, JDBC, Aspect Object Programming (AOP)





目录
引言
抽象 Aspect
客户端 Aspect 类
测试
结论


引言

能够在短时间内为类的对象提供持久化功能,这充分展现了 Aspect 的强大能力。因为我们可以为那些本身不具备此行为的对象添加此行为,而无需更改它们的代码。

有些人会认为持久化不是 Aspect 的一部分,因为它被认为是相关类固有的部分,因此不能被视为 Aspect,尤其是当讨论业务对象时。但事实是,当我们谈论“业务对象”时,我们指的是同一事物,而不是“持久化业务对象”。业务对象包含该类所支持的业务逻辑。另一个方面是如何在数据库中保存这些信息以实现持久化。

让我们考虑另一种情况,假设我们开发了多个持久化系统:一个将数据存储在 XML 中,一个存储在纯文本文件中,另一个存储在数据库中。

现在我们来看下面的迁移示例。

例如,我们开发了一个应用程序,其中有一个 Configuration 类存储应用程序的配置数据。由于该应用程序的初始使用,我们考虑将这些配置数据存储在 XML 文件中。但随着时间的推移,该应用程序已发展壮大,现在我们需要通过网络获取配置数据,因为需要所有已安装的公司应用程序共享它们。为此,我们选择将数据存储在数据库中,因为所有团队都可以访问数据库。如果 Configuration 类是使用 Aspect 编写的,那么它将不会有任何改变,因为我们唯一需要改变的就是应用于该类的 Aspect。

接下来,我们将看到为类的对象实现持久化所遵循的方法。



抽象 Aspect

首先开发的是一个名为 Persistence 的抽象 Aspect,它将允许被其他 Aspect 扩展。这个抽象 Aspect 包含了定义 Aspect 在给定时间内行为的所有抽象切点。

抽象切点的声明需要在扩展抽象 Aspect 的 Aspect 中重写,捕获希望执行的切点的执行时间点。

这个抽象 Aspect 还扩展了 CapaDatos 类,该类包含了为对象提供持久化的所有功能。

通过扩展 CapaDatos 类,我们可以从 Aspect 访问该类的功能,就像在该 Aspect 自身内部声明了其所有函数一样。


Figura 1. Persistencia.aj

package persistencia_Aspect;

import java.sql.*;
/**
 * @author mlorente@programemos.com
 */
abstract aspect Persistencia extends CapaDatos {
    
  declare parents : FormularioCliente implements ObjetosRelacion;
        
  Persistencia() {
    super();
    System.out.println("constructor Persistencia");
  }
    
  abstract pointcut quieroLeer();
  abstract pointcut quieroGrabar();
  abstract pointcut quieroBorrar();
    
  after() : quieroLeer() {
    leer();
  }
    
  before() : quieroGrabar() {
    grabar();
  }
    
  before() : quieroBorrar() {
    borrar();
  }
    
  public void FormularioCliente.addObjetoRelacion(Object obj) {
    addObjeto((Cliente)obj);
  }
}

首先我们可以看到以下代码

declare parents : FormularioCliente implements ObjetosRelacion.

这里指示 FormularioCliente 类(在更完善的实现中,一个表单将是一个代表数据库的类)必须实现 ObjetosRelacion 接口。

在 Aspect 声明的最后可以看到以下代码
public void FormularioCliente.addObjetoRelacion(Object obj) {
    addObjeto((Cliente)obj);
}

此处实现了包含 ObjetosRelacion 接口的方法。
由于本文并非旨在讨论对象之间的关系,因此我们创建了一个最基本的对象关系实现。

对象关系会是 Aspect 吗?

接下来,我们将看到 Aspect 声明中最重要的部分。

  abstract pointcut quieroLeer();
  abstract pointcut quieroGrabar();
  abstract pointcut quieroBorrar();
    
  after() : quieroLeer() {
    leer();
  }

  before() : quieroGrabar() {
    grabar();
  }
    
  before() : quieroBorrar() {
    borrar();
  }

声明了三个抽象切点,这些切点将在扩展此 Aspect 的 Aspect 中被重写。
正如我们所见,这并不妨碍已经可以为这些切点声明通知。在这些通知中,调用了 CapaDatos 类中包含的方法,因为我们扩展了它,因此我们可以使用它们。



客户端 Aspect

进行这个小型项目的想法之一是,只需编写最少的代码即可为特定类的对象提供持久化。而与 PersistenciaCliente Aspect 类似的 Aspect 将是我们编写以提供所需对象持久化的代码。

Figura 2.  PersistenciaCliente.aj

package persistencia_Aspect;

import java.sql.*;
/**
 * @author mlorente@programemos.com
 */
aspect PersistenciaCliente extends Persistencia {

  PersistenciaCliente() {
    System.out.println("constructor PersistenciaCliente");
  }
    
  pointcut quieroLeer() : execution(* *.leer_click());
  pointcut quieroGrabar() : execution(* *.grabar_click());
  pointcut quieroBorrar() : execution(* *.borrar_click());

  public String[] clave() {
    String[] a = new String[] {"IdCliente={$getId}"};
    return a;
  }
    
  public String sqlIdentityField() {
    return "{$setId}";
  }
    
  public String sqlSelect() {
    return "SELECT IdCliente{$setId}, Nombre{$setNombre}, Email{$setEmail} FROM Cliente";
  }
  
  public String sqlUpdate() {
    return "UPDATE Cliente SET Nombre='{$getNombre}', Email='{$getEmail}'";
  }
    
  public String sqlInsert() {
    return "INSERT INTO Cliente(Nombre, Email) VALUES('{$getNombre}', '{$getEmail}')";
  }
    
  public String sqlDelete() {
    return "DELETE FROM Cliente";
  }
}

在查看完构成 PersistenciaCliente Aspect 的所有代码后,我们将详细介绍它。

在以下代码中

  pointcut quieroLeer() : execution(* *.leer_click());
  pointcut quieroGrabar() : execution(* *.grabar_click());
  pointcut quieroBorrar() : execution(* *.borrar_click());

我们可以推测这是重写 Aspect Persistence 中定义的抽象切点的代码。我们的猜测是对的,因为它确实是这样做的。正如第一个切点所示,我们希望在捕获 leer_click 方法的执行时执行该切点。

正如我们所看到的,没有声明任何通知,抽象 Aspect Persistence 的通知代码将不会被执行。

其他方法不过是重写 CapaDatos 类的方法,因为抽象 Aspect Persistence 扩展了该类,而 PersistenciaCliente Aspect 扩展了 Persistence Aspect。

注意:伴随 SQL 语句和关键字的词语只是一个指示,用于表示我们希望在哪些函数中设置值或从哪些函数中检索值。因此,我们正在将数据层的值解析到业务对象的字段中。

让我们以以下语句为例
SELECT IdCliente{$setId}, Nombre{$setNombre}, Email{$setEmail} FROM Cliente

在上面的 SQL 语句中,它被指示从 Client 表中检索 IdCliente、Name 和 email。显然,这不是 SQL 代码,但它将有助于我们在数据库数据和 Client 业务对象的功能之间进行解析。因此,我们将设置或检索业务对象的方法的值。



测试

最后,我们只需要查看将允许我们进行适当测试的类。有必要提到以下代码 formulario.addObjetoRelacion(cliente),因为对象将它们关联到 FormularioCliente 类。必须注意的是,FormularioCliente 类不包含该方法,而是通过抽象 Aspect Persistence 提供的。

package persistencia_Aspect;
/**
 * @author mlorente@programemos.com
 */
import java.lang.reflect.*;
public class Test {
  public static void main(String[] args) {
    FormularioCliente formulario=new FormularioCliente();
        
    Cliente cliente=new Cliente();
    cliente.setId(1);
    Cliente cliente2=new Cliente();
    cliente2.setId(2);
        
    formulario.addObjetoRelacion(cliente);
    formulario.addObjetoRelacion(cliente2);
    formulario.leer_click();

        
    System.out.println("nombre del 1: " + cliente.getNombre() + 
      " ruta: " + cliente.getEmail());
    System.out.println("nombre del 2: " + cliente2.getNombre() + 
      " ruta: " + cliente2.getEmail());
    
    cliente.setNombre("nombre del 1 cambiado");    
    formulario.grabar_click();    


    cliente2.setNombre("nombre del 2 cambiado");
    Cliente cliente3=new Cliente();
    cliente3.setNombre("nuevo");
    cliente3.setEmail("Nueva");
        
    formulario.addObjetoRelacion(cliente3);
    formulario.borrar_click();
  }
}


结论

显然,这不是为类的对象提供持久化的最终解决方案,但它提供了多种实现方法之一。

也许您会缺少 CapaDatos 类的代码,但它与示例的 zip 文件一起提供,其他未提及的类或接口的代码也包含在其中。



用 AspectJ 编程


© . All rights reserved.