>백엔드 개발 >C#.Net 튜토리얼 >C# 고급 시리즈——AOP? 앗!

C# 고급 시리즈——AOP? 앗!

黄舟
黄舟원래의
2017-02-07 15:40:111539검색

서문: 이 기사는 AOP에 관한 것입니다. 사실 블로거는 이 개념을 접한 지 몇 달밖에 되지 않았지만, 제가 이전에 작성한 코드 원칙 중 상당수를 깨달았습니다. MVC 필터링과 같은 AOP를 기반으로 했기 때문에 FilterAttribute 및 IExceptionFilter를 통해 예외 캡처를 처리할 수 있지만, 이전에는 이러한 개념이 없었습니다.


1. AOP 개념


오래된 규칙, 먼저 공식적인 설명을 살펴보겠습니다: AOP ( Aspect-Oriented Programming(관점 지향 프로그래밍)은 사전 컴파일 및 런타임 동적 에이전트를 통해 소스 코드를 수정하지 않고도 프로그램에 기능을 동적이고 균일하게 추가할 수 있는 기술입니다. 이는 전통적인 OOP 프로그래밍을 보완하는 새로운 방법론입니다. OOP는 필요한 기능을 서로 다르고 상대적으로 독립적이며 잘 캡슐화된 클래스로 나누고, 상속 및 다형성을 사용하여 관계를 정의하여 고유한 동작을 갖도록 하는 데 중점을 둡니다. AOP는 일반적인 필수 기능을 관련 클래스 분리에서 변환할 수 있기를 희망합니다. 여러 클래스가 하나의 동작을 공유할 수 있습니다. 일단 변경이 발생하면 많은 클래스를 수정할 필요가 없고 동작만 수정할 수 있습니다. AOP는 측면을 사용하여 교차 편집 문제를 모듈화하고 OOP는 클래스를 사용하여 상태와 동작을 모듈화합니다. OOP 세계에서는 프로그램이 클래스와 인터페이스를 통해 구성되며 이를 사용하여 프로그램의 핵심 비즈니스 로직을 구현하는 것이 매우 적합합니다. 그러나 로깅, 권한 확인, 예외 차단 등과 같은 교차 문제(애플리케이션의 여러 모듈에 걸쳐 있는 기능 요구 사항)를 구현하는 것은 매우 어렵습니다.


Blogger의 이해: AOP는 공통 기능에 대한 요구 사항이 향후 변경되면 공통 모듈의 코드만 변경하면 됩니다. 변경할 필요가 없습니다. 소위 관점 지향이란 비즈니스 로직이 아닌 일반적인 기능에만 초점을 맞춘다는 것을 의미합니다. 구현은 일반적으로 차단을 통해 이루어집니다. 예를 들어, 우리 웹 프로젝트에는 기본적으로 권한 확인 기능이 있습니다. 각 페이지에 들어가기 전에 현재 로그인한 사용자가 인터페이스를 볼 수 있는 권한이 있는지 확인합니다. AOP의 메커니즘은 일련의 특성을 미리 정의하여 메소드를 가로채는 기능을 갖고 이전에 원하는 비즈니스를 수행할 수 있도록 하는 것입니다. 메소드를 실행한 후, 이를 사용할 때 해당 메소드나 클래스 정의에 특정 기능만 추가하면 됩니다.


2. AOP 사용의 장점

블로거가 생각하는 장점은 주로 다음과 같습니다.

1. 일반 기능과 업무를 분리합니다. 로직을 추출함으로써 대량의 반복되는 코드를 생략할 수 있어 코드의 운용 및 유지관리에 유리하다.

2. 소프트웨어를 설계할 때 공통 기능(측면)을 추출하는 것은 소프트웨어 설계의 모듈화에 도움이 되고 소프트웨어 아키텍처의 복잡성을 줄여줍니다. 즉, 일반 기능은 별도의 모듈로 되어 있고, 이러한 일반 기능에 대한 디자인 코드는 프로젝트의 본업에서는 볼 수 없습니다.

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

획득 결과:

C# 고급 시리즈——AOP? 앗!

上面我们模拟订单提交的例子,在提交一个订单前,我们需要做很多的准备工作,比如数据有效性校验等;订单提交完成之后,我们还需要做日志记录等。上面的代码很简单,没有任何复杂的逻辑,从上面的代码可以看出,我们通过静态植入的方式手动在执行方法前和执行方法后让它做一些我们需要的功能。AOP的实现原理应该也是如此,只不过它帮助我们做了方法拦截,帮我们省去了大量重复代码,我们要做的仅仅是写好拦截前和拦截后需要处理的逻辑。


2、动态代理

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

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

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

C# 고급 시리즈——AOP? 앗!

然后定义对应的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();
     }
 
     
//定义单例模式将PolicyInjection.Create()产生的这个对象传出去,这样就避免了在调用处写这些东西
     public static UserOperation GetInstance()
     {
         if (oUserOpertion == null)
             oUserOpertion = PolicyInjection.Create();
 
         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;
    }
}

得到结果如下:

C# 고급 시리즈——AOP? 앗!

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

C# 고급 시리즈——AOP? 앗!

由于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
  {
      /// 
      /// 获取或设置服务接口。
      /// 
      private Ic_TM_PLANTService service { get; set; }
 
      public IList Find()
      {
          DTO_TM_PLANT otest = null;
          otest.NAME_C = "test";
            //异常,会进入OnException方法
      return service.FindAll();   
   }  
}

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

[TestAop]
public IList 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>();
       var lstResPage = new List();        
//.........todo
 
       return Json(new { lstDataAttr = lstRes, PageAttr = lstResPage, lstJsConnections = lstJsPlumbLines }, JsonRequestBehavior.AllowGet);
   }

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

위는 C# 고급 시리즈 - AOP? 앗! 더 많은 관련 내용은 PHP 중국어 홈페이지(www.php.cn)를 주목해주세요!


성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.