SpringAop

来源:互联网 发布:车库数据er图 编辑:程序博客网 时间:2024/06/13 15:32

AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

 

而AOP技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。

 

使用“横切”技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理。Aop 的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。正如Avanade公司的高级方案构架师Adam Magee所说,AOP的核心思想就是“将应用程序中的商业逻辑同对其提供支持的通用服务进行分离。”

 

实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。





配置可以通过xml文件来进行,大概有四种方式:

1.        配置ProxyFactoryBean,显式地设置advisors, advice, target等

2.        配置AutoProxyCreator,这种方式下,还是如以前一样使用定义的bean,但是从容器中获得的其实已经是代理对象

3.        通过<aop:config>来配置

4.        通过<aop: aspectj-autoproxy>来配置,使用AspectJ的注解来标识通知及切入点


AOP的简单应用

为了说明AOP的工作原理,博主打算先从一个简单的例子开始,通过静态拦截的方式来了解AOP是如何工作的。

1、静态拦截

复制代码
    public class Order    {        public int Id { set; get; }        public string Name { set; get; }        public int Count { set; get; }        public double Price { set; get; }        public string Desc { set; get; }    }    public interface IOrderProcessor    {        void Submit(Order order);    }    public class OrderProcessor : IOrderProcessor    {        public void Submit(Order order)        {            Console.WriteLine("提交订单");        }    }    public class OrderProcessorDecorator : IOrderProcessor    {        public IOrderProcessor OrderProcessor { get; set; }        public OrderProcessorDecorator(IOrderProcessor orderprocessor)        {            OrderProcessor = orderprocessor;        }        public void Submit(Order order)        {            PreProceed(order);            OrderProcessor.Submit(order);            PostProceed(order);        }        public void PreProceed(Order order)        {            Console.WriteLine("提交订单前,进行订单数据校验....");            if (order.Price < 0)            {                Console.WriteLine("订单总价有误,请重新核对订单。");            }        }        public void PostProceed(Order order)        {            Console.WriteLine("提交带单后,进行订单日志记录......");            Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "提交订单,订单名称:" + order.Name + ",订单价格:" + order.Price);        }    }
复制代码

调用代码:

复制代码
     static void Main(string[] args)        {            Order order = new Order() { Id = 1, Name = "lee", Count = 10, Price = 100.00, Desc = "订单测试" };            IOrderProcessor orderprocessor = new OrderProcessorDecorator(new OrderProcessor());            orderprocessor.Submit(order);            Console.ReadLine();        }

2、动态代理

了解了静态拦截的例子,你是否对AOP有一个初步的认识了呢。下面我们就来到底AOP该如何使用。按照园子里面很多牛人的说法,AOP的实现方式大致可以分为两类:动态代理和IL 编织两种方式。博主也不打算照本宣科,分别拿Demo来说话吧。下面就以两种方式各选一个代表框架来说明。

动态代理方式,博主就以微软企业库(MS Enterprise Library)里面的PIAB(Policy Injection Application Block)框架来作说明。

首先需要下载以下几个dll,然后添加它们的引用。

然后定义对应的Handler

复制代码
   public class User    {        public string Name { set; get; }        public string PassWord { set; get; }    }    #region 1、定义特性方便使用    public class LogHandlerAttribute : HandlerAttribute    {        public string LogInfo { set; get; }        public int Order { get; set; }        public override ICallHandler CreateHandler(IUnityContainer container)        {            return new LogHandler() { Order = this.Order, LogInfo = this.LogInfo };        }    }    #endregion    #region 2、注册对需要的Handler拦截请求    public class LogHandler : ICallHandler    {        public int Order { get; set; }        public string LogInfo { set; get; }        //这个方法就是拦截的方法,可以规定在执行方法之前和之后的拦截        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)        {            Console.WriteLine("LogInfo内容" + LogInfo);            //0.解析参数            var arrInputs = input.Inputs;            if (arrInputs.Count > 0)            {                var oUserTest1 = arrInputs[0] as User;            }            //1.执行方法之前的拦截            Console.WriteLine("方法执行前拦截到了");            //2.执行方法            var messagereturn = getNext()(input, getNext);            //3.执行方法之后的拦截            Console.WriteLine("方法执行后拦截到了");            return messagereturn;        }    }    #endregion    #region 3、用户定义接口和实现    public interface IUserOperation    {        void Test(User oUser);        void Test2(User oUser, User oUser2);    }    //这里必须要继承这个类MarshalByRefObject,否则报错    public class UserOperation : MarshalByRefObject, IUserOperation    {        private static UserOperation oUserOpertion = null;        public UserOperation()        {            //oUserOpertion = PolicyInjection.Create<UserOperation>();        }        //定义单例模式将PolicyInjection.Create<UserOperation>()产生的这个对象传出去,这样就避免了在调用处写这些东西        public static UserOperation GetInstance()        {            if (oUserOpertion == null)                oUserOpertion = PolicyInjection.Create<UserOperation>();            return oUserOpertion;        }        //调用属性也会拦截        public string Name { set; get; }        //[LogHandler],在方法上面加这个特性,只对此方法拦截        [LogHandler(LogInfo = "Test的日志为aaaaa")]        public void Test(User oUser)        {            Console.WriteLine("Test方法执行了");        }        [LogHandler(LogInfo = "Test2的日志为bbbbb")]        public void Test2(User oUser, User oUser2)        {            Console.WriteLine("Test2方法执行了");        }    }    #endregion
复制代码

最后我们来看调用的代码:

复制代码
        static void Main(string[] args)        {            try            {                var oUserTest1 = new User() { Name = "test2222", PassWord = "yxj" };                var oUserTest2 = new User() { Name = "test3333", PassWord = "yxj" };                var oUser = UserOperation.GetInstance();                oUser.Test(oUserTest1);                oUser.Test2(oUserTest1,oUserTest2);            }            catch (Exception ex)            {                //throw;            }        }
复制代码

得到结果如下:

我们来看执行Test()方法和Test2()方法时候的顺序。

由于Test()和Test2()方法上面加了LogHander特性,这个特性里面定义了AOP的Handler,在执行Test和Test2方法之前和之后都会进入Invoke()方法里面。其实这就是AOP的意义所在,将切面的通用功能在统一的地方处理,在主要逻辑里面直接用过特性使用即可。

3、IL编织

静态织入的方式博主打算使用PostSharp来说明,一来这个使用起来简单,二来项目中用过这种方式。

Postsharp从2.0版本就开始收费了。为了说明AOP的功能,博主下载了一个免费版本的安装包,使用PostSharp与其它框架不太一样的是一定要下载安装包安装,只引用类库是不行的,因为上文说过,AOP框架需要为编译器或运行时添加扩展。使用步骤如下:

(1)下载Postsharp安装包,安装。

(2)在需要使用AOP的项目中添加PostSharp.dll 这个dll的引用。

(3)定义拦截的方法:

复制代码
    [Serializable]    public class TestAop : PostSharp.Aspects.OnMethodBoundaryAspect    {     //发生异常时进入此方法        public override void OnException(MethodExecutionArgs args)        {            base.OnException(args);        }     //执行方法前执行此方法        public override void OnEntry(MethodExecutionArgs args)        {            base.OnEntry(args);        }     //执行方法后执行此方法        public override void OnExit(MethodExecutionArgs args)        {            base.OnExit(args);        }    }
复制代码

注意这里的TestAop这个类必须要是可序列化的,所以要加上[Serializable]特性

(4)在需要拦截功能的地方使用。

在类上面加特性拦截,此类下面的所有的方法都会具有拦截功能。

复制代码
  [TestAop]public class Impc_TM_PLANT : Ifc_TM_PLANT    {        /// <summary>        /// 获取或设置服务接口。        /// </summary>        private Ic_TM_PLANTService service { get; set; }                public IList<DTO_TM_PLANT> Find()        {            DTO_TM_PLANT otest = null;            otest.NAME_C = "test";//异常,会进入OnException方法
        return service.FindAll();      }  }
复制代码

方法上面加特性拦截,只会拦截此方法。

复制代码
        [TestAop]        public IList<DTO_TM_PLANT> Find()        {            DTO_TM_PLANT otest = null;            otest.NAME_C = "test";            return service.FindAll();        }
复制代码

有没有感觉很简单,很强大,其实这一简单应用,解决我们常见的日志、异常、权限验证等功能简直太小菜一碟了。当然Postsharp可能还有许多更加高级的功能,有兴趣可以深究下。

4、MVC里面的Filter

复制代码
   public class AOPFilterAttribute : ActionFilterAttribute, IExceptionFilter    {        public void OnException(ExceptionContext filterContext)        {            throw new System.NotImplementedException();        }        public override void OnActionExecuting(ActionExecutingContext filterContext)        {                        base.OnActionExecuting(filterContext);        }        public override void OnActionExecuted(ActionExecutedContext filterContext)        {            base.OnActionExecuted(filterContext);        }    }
复制代码

在controller里面使用该特性:

复制代码
     [AOPFilter]        public JsonResult GetEditModel(string strType)        {            var lstRes = new List<List<DragElementProp>>();            var lstResPage = new List<PageProperty>();        //.........todo            return Json(new { lstDataAttr = lstRes, PageAttr = lstResPage, lstJsConnections = lstJsPlumbLines }, JsonRequestBehavior.AllowGet);        }
复制代码

调试可知,在执行GetEditModel(string strType)方法之前,会先执行OnActionExecuting()方法,GetEditModel(string strType)之后,又会执行OnActionExecuted()方法。这在我们MVC里面权限验证、错误页导向、日志记录等常用功能都可以方便解决。