面向切面編程AOP


一、引言:

  面向切面編程,自我理解是:在軟件系統實現過程中,在很多模塊操作中都會用到一些相同的固定的邏輯操作,比如權限驗證、日志記錄、性能檢測等,這些都是公共的邏輯,貫穿整個系統實現過程中。面向切面編程就是將這些公共的邏輯和系統本身核心的業務邏輯分離開來集中管理,這樣一方面對減輕系統本身的業務邏輯,另一方面降低耦合度,提高可重用性,便於后期擴展維護。 

  AOP為Aspect Oriented Programming的縮寫,意為:面向切面編程,通過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生范型。利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率。(百度百科)

  

二、靜態攔截方式實現AOP

靜態攔截方式最為簡單,只是從原理上面描述AOP的實現過程。

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);
        }
    }
View Code

調用方式:

Order order = new Order() { Id = 1, Name = "lee", Count = 10, Price = 100.00, Desc = "訂單測試" };
            IOrderProcessor orderprocessor = new OrderProcessorDecorator(new OrderProcessor());
            orderprocessor.Submit(order);

 

三、動態代理實現AOP

using Microsoft.Practices.Unity.InterceptionExtension;
using Microsoft.Practices.Unity;
using Microsoft.Practices.EnterpriseLibrary.PolicyInjection;

namespace ConsoleDmeo.AOP
{
    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
}
View Code

調用方式:

var oUserTest1 = new ConsoleDmeo.AOP.User() { Name = "test2222", PassWord = "yxj" };
                var oUserTest2 = new ConsoleDmeo.AOP.User() { Name = "test3333", PassWord = "yxj" };
                var oUser = UserOperation.GetInstance();
                oUser.Test(oUserTest1);
                oUser.Test2(oUserTest1, oUserTest2);

 

四、MVC中的filter

  在MVC項目中filter實現的AOP,簡單介紹:

自定義授權類:

public class MyAuthorizeAttribute:AuthorizeAttribute
    {
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            filterContext.Result = new RedirectResult("/Login/Index");
            base.OnAuthorization(filterContext);
        }

        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            string userName = httpContext.User.Identity.Name;

            if (userName == "admin")
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }

上面自定義了請求授權方法和自定義授權檢查,在局部控制器中使用:

[MyAuthorize]
 public ActionResult Index()
 {
      return View();
 }  

 

五、WCF中的消息攔截

  在WCF的系統中,在客戶端和服務器端互相通信的過程中,客戶端和服務端互相調用,其中程序員需要對調用過程進行控制,例如客戶端在發送請求前可以先進行日志記錄等,整個過程本人感覺和面向切面編程的定義非常相符,因此拿到此處一並進行記錄。要對SOAP消息進行攔截和修改,需要實現兩個接口,它們都位於System.ServiceModel.Dispatcher (程序集System.ServiceModel)。下面分別介紹:

接口一:IClientMessageInspector

從名字中我們可以猜測,它是用來攔截客戶消息的,而看看它的方法,你就更加肯定當初的猜測了。

  • BeforeSendRequest:向服務器發送請求前攔截或修改消息(事前控制)
  • AfterReceiveReply:接收到服務器的回復消息后,在調用返回之前攔截或修改消息(事后諸葛亮)

接口二:IDispatchMessageInspector

剛才那個接口是針對客戶端的,而這個是針對服務器端的。

  • AfterReceiveRequest:接收客戶端請求后,在進入操作處理代碼之前攔截或修改消息(欺上)
  • BeforeSendReply:服務器向客戶端發送回復消息之前攔截和修改消息(瞞下)。

 

下面是在網上找到的一個實例,已經在本文底部進行引用。

首先創建一個簡單的WCF的服務:

[ServiceContract]
    public interface IServiceTest
    {
        [OperationContract]
        int AddInt(int a, int b);
        [OperationContract]
        Student GetStudent();
        [OperationContract]
        CalResultResponse ComputingNumbers(CalcultRequest inMsg);  
    }

服務的實現:

public class ServiceTest : IServiceTest
    {
        public int AddInt(int a, int b)
        {
            return a + b;
        }

        public Student GetStudent()
        {
            Student stu = new Student();
            stu.StudentName = "小明";
            stu.StudentAge = 22;
            return stu;
        }

        public CalResultResponse ComputingNumbers(CalcultRequest inMsg)
        {
            CalResultResponse rmsg = new CalResultResponse();
            switch (inMsg.Operation)
            {
                case "":
                    rmsg.ComputedResult = inMsg.NumberA + inMsg.NumberB;
                    break;
                case "":
                    rmsg.ComputedResult = inMsg.NumberA - inMsg.NumberB;
                    break;
                case "":
                    rmsg.ComputedResult = inMsg.NumberA * inMsg.NumberB;
                    break;
                case "":
                    rmsg.ComputedResult = inMsg.NumberA / inMsg.NumberB;
                    break;
                default:
                    throw new ArgumentException("運算操作只允許加、減、乘、除。");
                    break;
            }
            return rmsg;
        }  
    }
View Code

 

編寫wcf的消息攔截器:

引用:

using System.ServiceModel;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Description;
using System.ServiceModel.Channels;
View Code

攔截器及行為:

/// <summary>
    /// WCF消息攔截
    /// </summary>
    public class MyMessageInspector:IClientMessageInspector,IDispatchMessageInspector
    {
        void IClientMessageInspector.AfterReceiveReply(ref Message reply, object correlationState)
        {
            Console.WriteLine("客戶端接收到的回復:\n{0}", reply.ToString());
        }

        object IClientMessageInspector.BeforeSendRequest(ref Message request, IClientChannel channel)
        {
            Console.WriteLine("客戶端發送請求前的SOAP消息:\n{0}", request.ToString());
            return null;
        }

        object IDispatchMessageInspector.AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
        {
            Console.WriteLine("服務器端:接收到的請求:\n{0}", request.ToString());
            return null;
        }

        void IDispatchMessageInspector.BeforeSendReply(ref Message reply, object correlationState)
        {
            Console.WriteLine("服務器即將作出以下回復:\n{0}", reply.ToString());
        }  
    }

    /// <summary>  
    /// 插入到終結點的Behavior  
    /// </summary>  
    public class MyEndPointBehavior : IEndpointBehavior
    {
        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
            // 不需要  
            return;
        }

        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
            // 植入“偷聽器”客戶端  
            clientRuntime.ClientMessageInspectors.Add(new MyMessageInspector());
        }

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
            // 植入“偷聽器” 服務器端  
            endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new MyMessageInspector());
        }

        public void Validate(ServiceEndpoint endpoint)
        {
            // 不需要  
            return;
        }
    }  
View Code

 

服務端:新建一個控制台解決方案,綁定wcf:

Uri baseAddress = new Uri("http://192.168.5.111:11378/services");  
            // 聲明服務器主機  
            ServiceHost host = new ServiceHost(typeof (ServiceTest));
            WSHttpBinding binding = new WSHttpBinding();
            host.AddServiceEndpoint(typeof (IServiceTest), binding, baseAddress);
            if (host.Description.Behaviors.Find<ServiceMetadataBehavior>() == null)
            {
                ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
                behavior.HttpGetEnabled = true;
                behavior.HttpGetUrl = baseAddress;
                host.Description.Behaviors.Add(behavior);
            }

            // 把自定義的IEndPointBehavior插入到終結點中  
            foreach (var endpont in host.Description.Endpoints)
            {
                endpont.EndpointBehaviors.Add(new Demolib.MyEndPointBehavior());
            }  

            host.Open();
            Console.WriteLine("服務已啟動。"); 

 

客戶端:

ServiceTest.ServiceTestClient client = new ServiceTestClient();         
client.Endpoint.EndpointBehaviors.Add(new Demolib.MyEndPointBehavior()); // 記得在客戶端也要插入IEndPointBehavior  
      // 1、調用帶元數據參數和返回值的操作  
          Console.WriteLine("\n20和35相加的結果是:{0}", client.AddInt(20, 35));
          // 2、調用帶有數據協定的操作  
          ServiceTest.Student student = client.GetStudent();
          Console.WriteLine("\n學生信息---------------------------");
          Console.WriteLine("姓名:{0}\n年齡:{1}", student.StudentName, student.StudentAge);
          // 3、調用帶消息協定的操作  
          Console.WriteLine("\n15乘以70的結果是:{0}", client.ComputingNumbers("", 15, 70));

 

完畢

 

 

引用:

AOP技術基礎

C#進階系列——AOP?AOP!

WCF入門(一)——簡單的示例

傳說中的WCF(10):消息攔截與篡改

.NET里簡易實現AOP


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM