• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
bobmazelin
博客园    首页    新随笔    联系   管理    订阅  订阅

AspectNet功能介绍(二)

在上一篇中我介绍了AspectNet的基本结构,从这篇开始我会具体介绍AspectNet在静态编织方面的功能.这次我给大家介绍call和execution两个pointcut.

AspectNet是一个基于.Net Framework的方面编制器,它同时能实现静态编织和动态编织,是bobmazelin个人的研究性项目,现仍然处于开发阶段,有关AOP概念的介绍请参考:www.aspectJ.org以及IBM的专题.

在上一篇中我介绍了AspectNet的基本结构,从这篇开始我会具体介绍AspectNet在静态编织方面的功能.这次我给大家介绍call和execution两个pointcut.

在上篇中我提过pointcut捕获了需要织入的代码点,在AspectNet中存在着多种捕获这种代码点的方式,call和execution是其中比较基础的两种.

1. call pointcut: 捕获了调用某些方法的代码点;

2. execution pointcut:捕获了执行某些方法的代码点.

它们非常相似,我在下文会通过一个小例子来展示它们之间的区别.

在给出例子之前,我先说明一下它们的方法模式:

修饰符 返回值类型 [方法类型.]方法名称(参数类型列表)

1. 修饰符是public, protected,private,sealed,static等,它们支持!以及并操作(比如: !private static);

2. 返回值类型就是方法的返回值类型,它支持void,*, &&, ||, !,+操作符.void就是没有返回值;*是通配符,单独的*代表任意类型,也可以部分通配,比如:System.*,Customer*.Order*等等;&&, ||, !可以组合不同的类型;+是子类操作符,代表包括其所有子类,比如:System.Object+,就代表了所有的类;

3. 方法类型描述了方法声明了类型,它和返回值类型的模式类似;注:在AspectNet中它不能被忽略;

4. 方法名称只支持*和new操作符,new代表类的构造函数;

5. 参数类型列表:通过,来分割参数类型,它支持..操作符来表示任意多了参数,比如:System.Int,..,string就表述方法的第一个参数和最后一个参数的类型,中间不限制参数的个数和类型.

注:上面对AspectNet的方法模式的描述并不全面.

理论讲完了,来点实际的,由于还没有到参数的传递阶段,我选择了最为简单的log为实例,等到介绍参数时再给出业务相对复杂的例子,这个例子主要说明两个问题,第一: 通过+来捕获子类代码点;第二,call和execution的区别.

首先是需要织入代码的类:

namespace Mazelin.AspectNet.CaseOneProject
{
    public abstract class Customer
    {
        protected IList orders = new ArrayList();

        public abstract float GetCustomerOrderPrice();

        public void AddOrder(Order order)
        {
            orders.Add(order);
        }
    }
}

namespace Mazelin.AspectNet.CaseOneProject
{
    public class NormalCustomer : Customer
    {
        public override float GetCustomerOrderPrice()
        {
            Console.WriteLine("NormalCustomer");
            float discount = 0.9f;
            float totalPrice = 0f;
            foreach (Order order in this.orders)
            {
                totalPrice += order.Price * discount;
            }
            return totalPrice;
        }
    }
}

namespace Mazelin.AspectNet.CaseOneProject
{
    public class VIPCustomer : Customer
    {
        public override float GetCustomerOrderPrice()
        {
            Console.WriteLine("VIPCustomer");
            float discount = 0.75f;
            float totalPrice = 0f;
            foreach (Order order in this.orders)
            {
                totalPrice += order.Price * discount;
            }
            return totalPrice;
        }
    }
}

namespace Mazelin.AspectNet.CaseOneProject
{
    public class Order
    {
        private Guid id = Guid.NewGuid();

        private float price = 0;

        public float Price
        {
            get { return price; }
            set { price = value; }
        }
    }
}

namespace Mazelin.AspectNet.CaseOneProject
{
    public class Main
    {
        public void Start()
        {
            Order order1 = new Order();
            order1.Price = 100f;

            Order order2 = new Order();
            order2.Price = 200f;

            NormalCustomer normalCustomer = new NormalCustomer();
            normalCustomer.AddOrder(order1);
            normalCustomer.AddOrder(order2);
            normalCustomer.GetCustomerOrderPrice();

            VIPCustomer vipCustomer = new VIPCustomer();
            vipCustomer.AddOrder(order1);
            vipCustomer.AddOrder(order2);
            vipCustomer.GetCustomerOrderPrice();
        }
    }
}

其中Order类没有什么实质的作用,Main类用于构建测试数据.Customer类中的GetCustomerOrderPrice方法就是我们需要关注的代码点.

namespace Mazelin.AspectNet.CaseOneProject
{
    public class Log
    {
        public void LogMessage()
        {
            Console.WriteLine("call GetCustomerOrderPrice!");
        }

        public void LogExecutionMessage()
        {
            Console.WriteLine("execution GetCustomerOrderPrice!");
        }
    }
}

简单的Log类.

namespace Bob.Mazelin
{
    aspect LogAspect
    {
        pointcut GetCustomerOrderPricePoint():
            call(public float Mazelin.AspectNet.CaseOneProject.Customer+.GetCustomerOrderPrice());
        pointcut GetCustomerOrderPriceexecutionPoint():
            execution(public float Mazelin.AspectNet.CaseOneProject.Customer+.GetCustomerOrderPrice());
        before():GetCustomerOrderPricePoint():LogMessage();
        before():GetCustomerOrderPriceexecutionPoint():LogExecutionMessage();
        storage LogMessage():
            call(public void Mazelin.AspectNet.CaseOneProject.Log.LogMessage());
        storage LogExecutionMessage():
            call(public void Mazelin.AspectNet.CaseOneProject.Log.LogExecutionMessage());   
    }   
}

pointcut GetCustomerOrderPricePoint捕获了调用Mazelin.AspectNet.CaseOneProject.Customer以及其子类的GetCustomerOrderPrice方法;GetCustomerOrderPriceexecutionPoint捕获了执行Mazelin.AspectNet.CaseOneProject.Customer以及其子类的GetCustomerOrderPrice方法.

storage LogMessage和LogExecutionMessage分别捕获了Log代码.两个before用于连接pointcut和storage.

[TestMethod]
        public void TestGetCustomerOrderPrice()
        {
            Main main = new Main();
            main.AddAspectAssembly(path + @"Mazelin.AspectNet\Mazelin.AspectNet.CaseOneProject\bin\Debug\Mazelin.AspectNet.CaseOneProject.dll");
            main.AddAspectFile(path + @"CaseOneGetCustomerOrderPrice.aspect");
            main.AddStorageAssembly(path + @"Mazelin.AspectNet\Mazelin.AspectNet.CaseOneProject\bin\Debug\Mazelin.AspectNet.CaseOneProject.dll");
            main.Execute();

            Assembly assembly = Assembly.LoadFrom(path + @"Mazelin.AspectNet\Mazelin.AspectNet.TestProject\bin\Debug\Aspect.Mazelin.AspectNet.CaseOneProject" + ".dll");
            object obj = assembly.CreateInstance("Mazelin.AspectNet.CaseOneProject.Main");
            obj.GetType().GetMethod("Start").Invoke(obj, new object[] { });

            obj = assembly.CreateInstance("Mazelin.AspectNet.CaseOneProject.NormalCustomer");
            obj.GetType().GetMethod("GetCustomerOrderPrice").Invoke(obj, new object[] { });
        }

单元测试代码.

结果为:

call GetCustomerOrderPrice!
execution GetCustomerOrderPrice!
NormalCustomer
call GetCustomerOrderPrice!
execution GetCustomerOrderPrice!
VIPCustomer
execution GetCustomerOrderPrice!
NormalCustomer.

下面我解释一下:

1. 单元测试中:main.Execute(); 用于织入代码;

2. 我为了方便使用反射调用新的DLL,其实不需要,直接引用就可以了.

3. 测试中首先调用Main中的Start方法,通过织入:Start方法中调用(call)NormalCustomer 和VIPCustomer的GetCustomerOrderPrice方法之前都织入了LogMessage方法的调用;注:call的意义就在此,它捕获所有调用方法的代码点,如果没有调用就不会织入;

4. 测试最后单独调用了NormalCustomer的GetCustomerOrderPrice方法是为了证明execution捕获了方法体中的执行点,而不是调用点,就是说无论是否被调用都会织入,也就是这样,call 打印了2次而execution是3次,多的一次是从外部(单元测试中单独调用的).

5. 考虑如果增加Customer的子类,是否需要再添加log的新代码呢?不需要重新编译就可以了.(注:动态织入就不需要编译)

call和execution是AspectNet中常用的pointcut类型,我上面的例子只展示了一小部分功能,不清楚的地方也请大家包涵.

最后还是期待大家的意见和建议,下次我会介绍this和target pointcut.

posted @ 2007-07-06 15:15  bobmazelin  阅读(2046)  评论(4)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3