编程思想--AOP

编程思想–AOP

一、什么是 AOP

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

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

​ 使用“横切”技术,AOP把软件系统分为两个部分:核心业务逻辑组件和横切关注点。横切关注点模块化为特殊的类,这些类被称为“切面”,好处:1.横切关注点都集中于一块,不会出现大量重复代码;2.核心模块只关注核心功能的代码,模块间藕合度降低。

二、AOP 的实现原理

img

如图:AOP 实际上是由目标类的代理类实现的。AOP 代理其实是由 AOP 框架动态生成的一个对象,该对象可作为目标对象使用。AOP 代理包含了目标对象的全部方法,但 AOP 代理中的方法与目标对象的方法存在差异,AOP 方法在特定切入点添加了增强处理,并回调了目标对象的方法。

三、装饰器模式实现AOP

public interface IUserProcessor
{
    void RegUser(User user);
}

面向对象:

public class UserProcessor : IUserProcessor
{
    public void RegUser(User user)
    {
        Console.WriteLine("用户已注册。Name:{0},PassWord:{1}", user.Name, user.Password);
    }
}

OOP调用:

public static void Show()
{
    User user = new User()
    {
        Name = "张三",
        Password = "123123123123"
    };
    IUserProcessor processor = new UserProcessor();
    processor.RegUser(user);
}

装饰器的模式去提供一个AOP功能

利用到了组合+继承;组合了IUserProcessor,继承了IUserProcessor接口

public class UserProcessorDecorator : IUserProcessor
{
    private IUserProcessor _UserProcessor { get; set; }
    public UserProcessorDecorator(IUserProcessor userprocessor)
    {
        this._UserProcessor = userprocessor;
    }

    public void RegUser(User user)
    {
        BeforeProceed(user);

        this._UserProcessor.RegUser(user);

        AfterProceed(user);
    }

    /// <summary>
    /// 业务逻辑之前
    /// </summary>
    /// <param name="user"></param>
    private void BeforeProceed(User user)
    {
        Console.WriteLine("方法执行前");
    }
    /// <summary>
    /// 业务逻辑之后
    /// </summary>
    /// <param name="user"></param>
    private void AfterProceed(User user)
    {
        Console.WriteLine("方法执行后");
    }
}

AOP调用

public static void Show()
{
    User user = new User()
    {
        Name = "张三",
        Password = "123123123123"
    };
    IUserProcessor processor = new UserProcessorDecorator(processor);
	processor.RegUser(user);
}

四、装饰器 + 委托 实现AOP

抽象类:

public abstract class BaseAttribute : Attribute
{
    public abstract Action Do(Action action);
}

前面执行特性:

public class ActionBefor : BaseAttribute
{
    public override Action Do(Action action)
    {
        return new Action(() =>
                          {
                              {
                                  Console.WriteLine("我在这里扩充了一些自己的东西。。。");
                              }
                              action.Invoke();
                          });
    }
}

后面执行特性:

public class ActionAfter : BaseAttribute
{
    public override Action Do(Action action)
    {
        return new Action(() =>
                          {
                              {
                                  Console.WriteLine("我在这里扩充了一些自己的东西000011。。。");
                              }
                              action.Invoke();
                          });
    }
}

调用:

public static void Show()
{
    InvokeAction invokeAction = new InvokeAction();

    Action action = () => { invokeAction.Show(); };

    Type type = invokeAction.GetType();
    if (type.IsDefined(typeof(ActionBefor), true))
    { 
        foreach (BaseAttribute attribute in type.GetCustomAttributes(typeof(BaseAttribute), true))
        {
            action = attribute.Do(action);
        }
    }
    action.Invoke();
}

四、代理模式实现AOP

五、使用.Net Remoting/RealProxy 真实代理实现AOP

真实代理:

/// <summary>
/// 真实代理
/// </summary>
/// <typeparam name="T"></typeparam>
public class MyRealProxy<T> : RealProxy
{
    private T tTarget;
    public MyRealProxy(T target) : base(typeof(T))
    {
        this.tTarget = target;
    }

    public override IMessage Invoke(IMessage msg)
    {
        BeforeProceede(msg);	//Log  try-catch
		
        //public interface IMethodCallMessage : IMethodMessage, IMessage
        //IMethodMessage 中才有 MethodBase MethodBase { get; },为了获取到MethodBase执行Invoke,所以需要转换
        IMethodCallMessage callMessage = (IMethodCallMessage)msg;
        object returnValue = callMessage.MethodBase.Invoke(this.tTarget, callMessage.Args);

        AfterProceede(msg);

        return new ReturnMessage(returnValue, new object[0], 0, null, callMessage);
    }

    public void BeforeProceede(IMessage msg)
    {
        Console.WriteLine("方法执行前可以加入的逻辑");
    }
    public void AfterProceede(IMessage msg)
    {
        Console.WriteLine("方法执行后可以加入的逻辑");
    }
}

透明代理:

/// <summary>
/// 透明代理
/// </summary>
public static class TransparentProxy
{
    public static T Create<T>()
    {
        T instance = Activator.CreateInstance<T>();
        //真实代理 构造函数
        MyRealProxy<T> realProxy = new MyRealProxy<T>(instance);	
        
        //System.Runtime.Remoting.Proxies.RealProxy 中的GetTransparentProxy方法
        T transparentProxy = (T)realProxy.GetTransparentProxy();	
        return transparentProxy;
    }
}

因为框架限制,抽象类必须是interface类型的,不能是abstract class。但是可以继承MarsalByRef的类

用户抽象类:

public interface IUserProcessor
{
    void RegUser(User user);
}

用户实现类:

/// <summary>
/// 必须继承自MarshalByRefObject父类,否则无法生成
/// </summary>
public class UserProcessor : MarshalByRefObject, IUserProcessor
{
    public void RegUser(User user)
    {
        Console.WriteLine("用户已注册。用户名称{0} Password{1}", user.Name, user.Password);
    }

    public void Login(User user)
    {
        Console.WriteLine("用户已登录。用户名称{0} Password{1}", user.Name, user.Password);
    }
}

调用:

public static void Show()
{
    User user = new User()
    {
        Name = "张三",
        Password = "123456"
    };
	//透明代理TransparentProxy 创建对象
    //创建出来的对象是 System.Runtime.Remoting.Proxies._TransparentProxy
    UserProcessor userProcessor = TransparentProxy.Create<UserProcessor>();
    userProcessor.RegUser(user);

    userProcessor.Login(user);
}

执行 userProcessor.RegUser(user) 的时候,启动执行真实代理MyRealProxy中的 Invoke 方法,而真实代理中的如下片段才是执行 userProcessor对象的RegUser方法的位置,所以你可以在其前后加入其他逻辑代码:

IMethodCallMessage callMessage = (IMethodCallMessage)msg;
object returnValue = callMessage.MethodBase.Invoke(this.tTarget, callMessage.Args);

真实代理利用 InvokeuserProcessor.RegUser(user) 方法重新包了一层,

Invoke 中参数 IMessage msg ,记录了执行的是那个类+方法+参数等信息,取到了MethodBase类数据便可以执行该类上的Invoke方法,和反射中获取到MethodInfo在利用Invoke执行是一样的,因为MethodInfo 继承了 MethodBase。

image-20220404153659943

六、使用Castle\DynamicProxy 动态代理实现AOP

使用 Castle/DynamicProxy 实现动态代理,方法必须是虚方法

抽象类:

public interface IUserProcessor
{
    void RegUser(User user);
}

实现类:

public class UserProcessor : IUserProcessor
{
    /// <summary>
    /// 必须带上virtual 否则无效~
    /// </summary>
    /// <param name="user"></param>
    public virtual void RegUser(User user)
    {
        Console.WriteLine($"用户已注册。Name:{user.Name},PassWord:{user.Password}");
    }
}

切入者:

public class MyInterceptor : IInterceptor
{
    /// <summary>
    /// 方法执行时
    /// </summary>
    /// <param name="invocation"></param>
    public void Intercept(IInvocation invocation)
    {
        PreProceed(invocation);
        invocation.Proceed();	//就是调用原始业务方法
        PostProceed(invocation);
    }

    /// <summary>
    /// 方法执行前
    /// </summary>
    /// <param name="invocation"></param>
    public void PreProceed(IInvocation invocation)
    {
        Console.WriteLine("方法执行前");
    }

    /// <summary>
    /// 方法执行后
    /// </summary>
    /// <param name="invocation"></param>
    public void PostProceed(IInvocation invocation)
    {
        Console.WriteLine("方法执行后");
    }
}

调用:

public static void Show()
{
    User user = new User()
    {
        Name = "张三",
        Password = "123456"
    };
	//ProxyGenerator 属于Castle.DynamicProxy 的类,并非自己定义
    ProxyGenerator generator = new ProxyGenerator();
    //自定义的切入者
    MyInterceptor interceptor = new MyInterceptor();
	
    UserProcessor userprocessor = generator.CreateClassProxy<UserProcessor>(interceptor);
    userprocessor.RegUser(user);
}

调用RegUser 方法的时候,执行 切入者中的 Intercept 方法,参数中的 IInvocation 数据如下:

image-20220404160843972

七、使用EntLib\PIAB Unity 实现动态代理

7.1 特性对应的行为

UserHandler.cs

public class UserHandler : ICallHandler
{
    public int Order { get; set; }
    public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
    {
        User user = input.Inputs[0] as User;
        if (user.Password.Length < 10)
        {
            return input.CreateExceptionMethodReturn(new Exception("密码长度不能小于10位"));
        }
        Console.WriteLine("参数检测无误");

        IMethodReturn methodReturn = getNext()(input, getNext); //getNext.Invoke().Invoke(input, getNext);

        //Console.WriteLine("已完成操作");

        return methodReturn;
    }
}

LogHandler.cs

public class LogHandler : ICallHandler
{
    public int Order { get; set; }

    public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
    {
        User user = input.Inputs[0] as User;
        string message = $"RegUser:Username:{user.Name},Password:{user.Password}";
        Console.WriteLine("日志已记录,Message:{0},Ctime:{1}", message, DateTime.Now);
        return getNext()(input, getNext);
    }
}

ExceptionHandler.cs

public class ExceptionHandler : ICallHandler
{
    public int Order { get; set; }
    public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
    {
        IMethodReturn methodReturn = getNext()(input, getNext);
        if (methodReturn.Exception == null)
        {
            Console.WriteLine("无异常");
        }
        else
        {
            Console.WriteLine($"异常:{methodReturn.Exception.Message}");
        }
        return methodReturn;
    }
}

7.2 特性

UserHandlerAttribute.cs

public class UserHandlerAttribute : HandlerAttribute
{
    public override ICallHandler CreateHandler(IUnityContainer container)
    {
        ICallHandler handler = new UserHandler() { Order = this.Order };
        return handler;
    }
}

LogHandlerAttribute.cs

public class LogHandlerAttribute : HandlerAttribute
{
    public override ICallHandler CreateHandler(IUnityContainer container)
    {
        return new LogHandler() { Order = this.Order };
    }
}

ExceptionHandlerAttribute.cs

public class ExceptionHandlerAttribute : HandlerAttribute
{
    public override ICallHandler CreateHandler(IUnityContainer container)
    {
        return new ExceptionHandler() { Order = this.Order };
    }
}

AfterLogHandlerAttribute.cs

public class AfterLogHandlerAttribute : HandlerAttribute
{
    public override ICallHandler CreateHandler(IUnityContainer container)
    {
        return new AfterLogHandler() { Order = this.Order };
    }
}

7.3 业务

接口:

[UserHandlerAttribute(Order = 1)]
[ExceptionHandlerAttribute(Order = 3)]
[LogHandlerAttribute(Order = 2)]
[AfterLogHandlerAttribute(Order = 5)]
public interface IUserProcessor
{
    void RegUser(User user);
    User GetUser(User user);
}

实现类:

public class UserProcessor : IUserProcessor
{
    public void RegUser(User user)
    {
        Console.WriteLine("用户已注册。");
    }

    public User GetUser(User user)
    {
        return user;
    }
}

7.4 调用

public static void Show()
{ 
    User user = new User()
    {
        Name = "张三",
        Password = "1234567890123456789"
    };

    IUnityContainer container = new UnityContainer();	//声明一个容器
    container.RegisterType<IUserProcessor, UserProcessor>();	//声明UnityContainer并注册IUserProcessor
	//添加扩展
    container.AddNewExtension<Interception>();
    //为IUserProcessor指定切入者
    container.RegisterType<IUserProcessor, UserProcessor>()
        .Configure<Interception>()
        .SetInterceptorFor<IUserProcessor>(new InterfaceInterceptor());
	
    IUserProcessor userprocessor = container.Resolve<IUserProcessor>();

    Console.WriteLine("********************");
    userprocessor.RegUser(user);//调用
    userprocessor.GetUser(user);//调用
}

7.5 结果

执行到最后一个切入者逻辑时,继续使用 getNext().Invoke(input, getNext) = 执行原本的方法 RegUser()

image-20220404181355303

可以利用 input.InvocationContext.Add("param", "123"); 在字典中添加数据,为之后的逻辑提供数据。

八、使用EntLib\PIAB Unity 读取配置文件方式实现动态代理

需要通过Nuget 引入文件 Unity.Interception + Unity 常用包

切入者们的相关类必须要实现这个IInterceptionBehavior接口,实现方法,尤其是 Invoke 方法

执行—按照配置文件配置的顺序执行;LogBeforeBehavior-> ParameterCheckBehavior-> CachingBehavior-> ExceptionLoggingBehavior

8.1 配置文件

Unity.Config:

<configuration>
	<configSections>
		<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Unity.Configuration"/>
	</configSections>
	<unity>
		<!--支持AOP-->
		<sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Unity.Interception.Configuration"/>
		<containers>
			<container name="zhaoxContainner">
				<extension type="Interception"/>
				<!--支持AOP-->
				<register type="ZhaoxiFramework.AOP.UnityWay.IUserProcessor,ZhaoxiFramework.AOP" mapTo="ZhaoxiFramework.AOP.UnityWay.UserProcessor,ZhaoxiFramework.AOP">
					<interceptor type="InterfaceInterceptor"/>
					<interceptionBehavior type="ZhaoxiFramework.AOP.UnityWay.LogBeforeBehavior, ZhaoxiFramework.AOP"/>
					<interceptionBehavior type="ZhaoxiFramework.AOP.UnityWay.ParameterCheckBehavior, ZhaoxiFramework.AOP"/>
					<interceptionBehavior type="ZhaoxiFramework.AOP.UnityWay.CachingBehavior, ZhaoxiFramework.AOP"/>
					<interceptionBehavior type="ZhaoxiFramework.AOP.UnityWay.ExceptionLoggingBehavior, ZhaoxiFramework.AOP"/>
                    <interceptionBehavior type="ZhaoxiFramework.AOP.UnityWay.LogAfterBehavior, ZhaoxiFramework.AOP"/>
				</register>
			</container>
		</containers>
	</unity>
</configuration>

8.2 切入者行为

LogBeforeBehavior.cs:

/// <summary>
/// 不需要特性
/// </summary>
public class LogBeforeBehavior : IInterceptionBehavior
{
    public IEnumerable<Type> GetRequiredInterfaces()
    {
        return Type.EmptyTypes;
    }

    public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
    {
        Console.WriteLine("LogBeforeBehavior");
        foreach (var item in input.Inputs)
        {
            Console.WriteLine(item.ToString());//反射获取更多信息
        }
        return getNext().Invoke(input, getNext);
    }

    public bool WillExecute
    {
        get { return true; }
    }
}

ParameterCheckBehavior.cs:

public class ParameterCheckBehavior : IInterceptionBehavior
{
    public IEnumerable<Type> GetRequiredInterfaces()
    {
        return Type.EmptyTypes;
    }

    public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
    {
        Console.WriteLine("ParameterCheckBehavior");
        User user = input.Inputs[0] as User;
        if (user.Password.Length < 10)
        {
            return input.CreateExceptionMethodReturn(new Exception("密码长度不能小于10位"));
        }
        else
        {
            Console.WriteLine("参数检测无误");
            return getNext().Invoke(input, getNext);
        }
    }

    public bool WillExecute
    {
        get { return true; }
    }
}

CachingBehavior.cs:

public class CachingBehavior : IInterceptionBehavior
{
    public IEnumerable<Type> GetRequiredInterfaces()
    {
        return Type.EmptyTypes;
    }

    public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
    {
        Console.WriteLine("CachingBehavior");
        //input.Target.GetType().GetCustomAttributes()
        if (input.MethodBase.Name.Equals("GetUser"))
            return input.CreateMethodReturn(new User() { Id = 234, Name = "张三" });
        var result=  getNext().Invoke(input, getNext);//去执行下一个

        Console.WriteLine("加入点自己的逻辑");
        return result;
    }

    public bool WillExecute
    {
        get { return true; }
    }
}

ExceptionLoggingBehavior.cs:

public class ExceptionLoggingBehavior : IInterceptionBehavior
{
    public IEnumerable<Type> GetRequiredInterfaces()
    {
        return Type.EmptyTypes;
    }

    public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
    {
        Console.WriteLine("ExceptionLoggingBehavior");
        IMethodReturn methodReturn = getNext()(input, getNext);
        if (methodReturn.Exception == null)
        {
            Console.WriteLine("无异常");
        }
        else
        {
            Console.WriteLine($"异常:{methodReturn.Exception.Message}");
        }
        return methodReturn;
    }

    public bool WillExecute
    {
        get { return true; }
    }
}

LogAfterBehavior.cs

public class LogAfterBehavior : IInterceptionBehavior
{
    public IEnumerable<Type> GetRequiredInterfaces()
    {
        return Type.EmptyTypes;
    }

    public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
    {
        Console.WriteLine("LogAfterBehavior");
        foreach (var item in input.Inputs)
        {
            Console.WriteLine(item.ToString());//反射获取更多信息
        }
        IMethodReturn methodReturn = getNext()(input, getNext);
        Console.WriteLine("LogAfterBehavior" + methodReturn.ReturnValue);
        return methodReturn;
    }

    public bool WillExecute
    {
        get { return true; }
    }
}

8.3 调用

public static void Show()
{
    User user = new User()
    {
        Name = "张三",
        Password = "1234567890123456789"
    };
    //配置UnityContainer
    IUnityContainer container = new UnityContainer();
    ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
    fileMap.ExeConfigFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "ZhaxiCfgFiles\\Unity.Config");
    Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);

    UnityConfigurationSection configSection = (UnityConfigurationSection)configuration.GetSection(UnityConfigurationSection.SectionName);
    configSection.Configure(container, "zhaoxContainner"); 
    IUserProcessor processor = container.Resolve<IUserProcessor>();
    processor.RegUser(user);
    processor.GetUser(user);
}

先根据配置文件执行那些切入者的逻辑,如此处就是先执行的LogBeforeBehavior 的Invoke方法,getNext 是一个委托,需要的参数和 IInterceptionBehavior.Invoke的参数一致。例如当执行LogBeforeBehavior 的时候,调用getNext().Invoke(input, getNext); 其实就是接着去执行 ParameterCheckBehavior 中的 Invoke 方法,就是这样一层传一层,一层层执行的。

IMethodInvocation input 数据如下:

image-20220404172436442

GetNextInterceptionBehaviorDelegate getNext 数据如下:

image-20220404173119116

image-20220404173743874

因为我是在执行 ParameterCheckBehavior 的方法时,获取的参数数据,所以这里显示的是1

8.4 结果

七、使用EntLib\PIAB Unity 实现动态代理 执行原理基本一致,都是执行到 最后一个切入者,再继续使用 getNext().Invoke() 就是执行原本的方法。

image-20220404180406795

九、总结

  • TransparentProxyInterceptor:直接在类的方法上进行标记,但是这个类必须继承MarshalByRefObject…不建议用
  • VirtualMethod:直接在类的方法上进行标记,但这个方法必须是虚方法(就是方法要带virtual关键字)
  • InterfaceInterceptor:在接口的方法上进行标记,这样继承这个接口的类里实现这个接口方法的方法就能被拦截
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

武功山上捡瓶子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值