文章目录
编程思想–AOP
一、什么是 AOP
AOP(Aspect-OrientedProgramming,面向切面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为(日志、安全、事务)的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,模块间的藕合度高,而不利于各个模块的重用。
而AOP技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即切面。所谓“切面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。
使用“横切”技术,AOP把软件系统分为两个部分:核心业务逻辑组件和横切关注点。横切关注点模块化为特殊的类,这些类被称为“切面”,好处:1.横切关注点都集中于一块,不会出现大量重复代码;2.核心模块只关注核心功能的代码,模块间藕合度降低。
二、AOP 的实现原理
如图: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);
真实代理利用 Invoke
给 userProcessor.RegUser(user)
方法重新包了一层,
Invoke
中参数 IMessage msg
,记录了执行的是那个类+方法+参数等信息,取到了MethodBase类数据便可以执行该类上的Invoke方法,和反射中获取到MethodInfo在利用Invoke执行是一样的,因为MethodInfo 继承了 MethodBase。
六、使用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
数据如下:
七、使用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()
可以利用 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
数据如下:
GetNextInterceptionBehaviorDelegate getNext
数据如下:
因为我是在执行 ParameterCheckBehavior 的方法时,获取的参数数据,所以这里显示的是1
。
8.4 结果
与 七、使用EntLib\PIAB Unity 实现动态代理
执行原理基本一致,都是执行到 最后一个切入者,再继续使用 getNext().Invoke() 就是执行原本的方法。
九、总结
- TransparentProxyInterceptor:直接在类的方法上进行标记,但是这个类必须继承MarshalByRefObject…不建议用
- VirtualMethod:直接在类的方法上进行标记,但这个方法必须是虚方法(就是方法要带virtual关键字)
- InterfaceInterceptor:在接口的方法上进行标记,这样继承这个接口的类里实现这个接口方法的方法就能被拦截