面向切面編程AOP

1、引言:html

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

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

  

2、靜態攔截方式實現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

調用方式:ide

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

 

3、動態代理實現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 一、定義特性方便使用
    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 二、註冊對須要的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 三、用戶定義接口和實現
    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);

 

4、MVC中的filterpost

  在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();
 }  

 

5、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  
      // 一、調用帶元數據參數和返回值的操做  
          Console.WriteLine("\n20和35相加的結果是:{0}", client.AddInt(20, 35));
          // 二、調用帶有數據協定的操做  
          ServiceTest.Student student = client.GetStudent();
          Console.WriteLine("\n學生信息---------------------------");
          Console.WriteLine("姓名:{0}\n年齡:{1}", student.StudentName, student.StudentAge);
          // 三、調用帶消息協定的操做  
          Console.WriteLine("\n15乘以70的結果是:{0}", client.ComputingNumbers("", 15, 70));

 

完畢

 

 

引用:

AOP技術基礎

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

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

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

.NET裏簡易實現AOP

相關文章
相關標籤/搜索