主要内容概要
1 AOP面向切面编程
2 静态AOP实现
3 动态代理AOP实现
4 静态织入实现AOP
5 Unity 依赖注入容器的AOP
6 MVC中的Filter 标记特性,然后该方法执行前后就多了逻辑,Invoker调用中心负责反射调用方法,检查特性,有则执行额外逻辑。
POP和OOP
POP:面向过程编程,代码是线性式 严格按照顺序
OOP:面向对象编程 万物皆对象,可以搭建大型的复杂系统
砖块—墙—房间—高楼大厦(稳定) 砖块确实是稳定,静态的
类–各种功能点—功能模块—系统(平台) 类是稳定的吗?
类是无法一直稳定,确实有场景需要改动。。。
GOF 23种设计模式 面对变化,核心套路都是依赖抽象 可以更换具体的类型 (最小的粒度只能到类)
只能替换整个对象,不能让类动态话,只能替换类;
AOP
AOP: 可以让类动态化!动态粒度更小,可以把类里面的成员给修改掉!
AOP 允许开发者动态的修改静态的OO模型,就像现实生活中对象在生命周期中会不断的改变自身。
AOP 面向切面编程 (Aspect Oriented Programming) 其实是OOP扩展。
使用Aop特点以后:
- 只聚焦于自身模块的业务功能,用户验证/日志处理/都可以通过Aop给动态加进来!程序设计变得简单。
- 功能扩展性更强,把功能集中管理,代码复用,更加规范
AOP的多种方式
是按AOP的方式:
- 装饰器模式/代理模式 静态实现
- 动态实现 (Remoting)(Emit)
- 通过Unity 支持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);
}
}
现在要想扩展这个实现,但是又不能去修改原来的实现,应该如何实现呢?例如在注册之前和之后都要加上一些操作。
// 业务逻辑之前
private void BeforeProceed(User user)
{
Console.WriteLine("方法执行前");
}
// 业务逻辑之后
private void AfterProceed(User user)
{
Console.WriteLine("方法执行后");
}
装饰器模式
装饰器的模式去提供一个AOP功能:
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);
}
}
使用的时候这么用:
User user = new User()
{
Name = "Richard",
Password = "123123123123"
};
IUserProcessor processor = new UserProcessor();
processor.RegUser(user);
Console.WriteLine("***************");
processor = new UserProcessorDecorator(processor);
processor.RegUser(user);
代理模式
代理模式去提供一个AOP功能:
public class ProxyUserProcessor : IUserProcessor
{
private IUserProcessor _UserProcessor = new UserProcessor();
public void RegUser(User user)
{
BeforeProceed(user);
this._UserProcessor.RegUser(user);
AfterProceed(user);
}
}
使用的时候可以这么用:
User user = new User()
{
Name = "Richard",
Password = "123123123123"
};
IUserProcessor processor = new UserProcessor();
processor.RegUser(user);
Console.WriteLine("***************");
processor = new ProxyUserProcessor();
processor.RegUser(user);
对比一下装饰器模式和代理模式: 前者是传递了一个UserProcessor对象到构造函数里面去,而后者是内置实例化了一个对象。这两者的侧重点不同,装饰器可以多层装饰,处理的场景不同。
这两者都是静态实现,说白了都是需要去添加代理或者装饰器类的。
使用.Net Remoting/RealProxy 实现动态代理
局限在业务类必须是继承自MarshalByRefObject类型,只有一个父类,必须是这个MarshalByRefObject。
public class UserProcessor : MarshalByRefObject, IUserProcessor
{
public void RegUser(User user)
{
Console.WriteLine("用户已注册。用户名称{0} Password{1}", user.Name, user.Password);
}
}
同时还需要两个代理类,分别是真实代理和透明代理:
透明代理类的代码是固定的,不需要修改:
//透明代理
public static class TransparentProxy
{
public static T Create<T>()
{
T instance = Activator.CreateInstance<T>();
MyRealProxy<T> realProxy = new MyRealProxy<T>(instance);
T transparentProxy = (T)realProxy.GetTransparentProxy();
return transparentProxy;
}
}
下面是真实代理,需要自己去重新实现的:
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
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("方法执行后可以加入的逻辑");
}
}
在真实使用的时候,实际上会调用Invoke方法,如下:
User user = new User()
{
Name = "Richard",
Password = "123456"
};
UserProcessor processor = new UserProcessor();
processor.RegUser(user);
Console.WriteLine("*********************");
UserProcessor userProcessor = TransparentProxy.Create();
userProcessor.RegUser(user);
使用Castle\DynamicProxy 实现动态代理
方法必须是虚方法:
public class UserProcessor : IUserProcessor
{
// 必须带上virtual 否则无效~
public virtual void RegUser(User user)
{
Console.WriteLine($"用户已注册。Name:{user.Name},PassWord:{user.Password}");
}
}
需要在Nuget里面添加这个dll:using Castle.DynamicProxy;//Castle.Core
public class MyInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
PreProceed(invocation);
invocation.Proceed();//就是调用原始业务方法
PostProceed(invocation);
}
public void PreProceed(IInvocation invocation)
{
Console.WriteLine("方法执行前");
}
public void PostProceed(IInvocation invocation)
{
Console.WriteLine("方法执行后");
}
}
使用的时候可以这么用,Castle的核心原理是基于Emit实现的:
User user = new User()
{
Name = "Richard",
Password = "123456"
};
ProxyGenerator generator = new ProxyGenerator();
MyInterceptor interceptor = new MyInterceptor();
UserProcessor userprocessor = generator.CreateClassProxy<UserProcessor>(interceptor);
userprocessor.RegUser(user);
静态织入实现AOP
PostSharp,收费工具,扩展编译工具,生成加入额外代码
Unity容器
首先,需要引入Unity, Unity.Abstractions, Unity.Configuration, Unity.Container, Unity.Interception, Unity,Interception.Configuration
这些关于Unity的Nuget包。
然后,还需要准备一个配置文件如下:
<configuration>
<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Unity.Configuration"/>
<!--Microsoft.Practices.Unity.Configuration.UnityConfigurationSection-->
</configSections>
<unity>
<sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Unity.Interception.Configuration"/>
<containers>
<container name="aopContainer">
<extension type="Interception"/>
<register type="MyAOP.UnityWay.IUserProcessor,MyAOP" mapTo="MyAOP.UnityWay.UserProcessor,MyAOP">
<interceptor type="InterfaceInterceptor"/>
<interceptionBehavior type="MyAOP.UnityWay.MoniterBehavior, MyAOP"/>
<interceptionBehavior type="MyAOP.UnityWay.LogBeforeBehavior, MyAOP"/>
<interceptionBehavior type="MyAOP.UnityWay.ParameterCheckBehavior, MyAOP"/>
<interceptionBehavior type="MyAOP.UnityWay.CachingBehavior, MyAOP"/>
<interceptionBehavior type="MyAOP.UnityWay.ExceptionLoggingBehavior, MyAOP"/>
<interceptionBehavior type="MyAOP.UnityWay.LogAfterBehavior, MyAOP"/>
</register>
</container>
</containers>
</unity>
</configuration>
这里面定义了容器的类型type,还有容器的 名字name,这是是aopContainer
。其中的核心是register
, 完整类型名称+dll名称: type="MyAOP.UnityWay.IUserProcessor,MyAOP" mapTo="MyAOP.UnityWay.UserProcessor,MyAOP"
,MyAOP是dll的名称,把接口IUserProcessor映射到UserProcessor这个上面去,MyAOP是完整名称。后面通过容器创建实例的时候就是依靠这一段配置实现的。interceptor type="InterfaceInterceptor"
是配置接口的扩展,还可以配置类和方法。
j接着,在具体使用的时候是这样,在调用RegUser方法的时候,会依次去执行配置文件里面的这些behavior,例如MoniterBehavior,LogBeforeBehavior,ParameterCheckBehavior,CachingBehavior,ExceptionLoggingBehavior,LogAfterBehavior。
User user = new User()
{
Name = "Richard",
Password = "1234567890123456789"
};
//配置UnityContainer
IUnityContainer container = new UnityContainer();
ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
fileMap.ExeConfigFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "CfgFiles\\Unity.Config");
Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
UnityConfigurationSection configSection = (UnityConfigurationSection)configuration.GetSection(UnityConfigurationSection.SectionName);
configSection.Configure(container, "aopContainer");
IUserProcessor processor = container.Resolve<IUserProcessor>();
processor.RegUser(user);
processor.GetUser(user);
最后,我们需要看一下,这些Behavior到底是怎么实现的呢?
必须实现接口IInterceptionBehavior,在方法Invoke中扩展逻辑:
- 在执行注册方法之前执行监控方法:
public class MoniterBehavior : IInterceptionBehavior
{
public IEnumerable<Type> GetRequiredInterfaces()
{
return Type.EmptyTypes;
}
public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
{
Console.WriteLine(this.GetType().Name);
string methodName = input.MethodBase.Name;
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
// 可以在这儿添加方法之前的逻辑
var query = getNext().Invoke(input, getNext);//后续逻辑执行
stopwatch.Stop();
Console.WriteLine($"{this.GetType().Name}执行耗时:{stopwatch.ElapsedMilliseconds} ms");
return query;
}
public bool WillExecute
{
get { return true; }
}
}
- 在执行注册方法之前执行日志方法:
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());//反射获取更多信息
}
var query = getNext().Invoke(input, getNext);
//下一个节点的方法已经执行完毕
//在这里计入方法执行后的逻辑
return query;
}
public bool WillExecute
{
get { return true; }
}
}
- 在执行注册方法之前执行检查参数behavior
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)
{
//throw new Exception();
return input.CreateExceptionMethodReturn(new Exception("密码长度不能小于10位"));
}
else
{
Console.WriteLine("参数检测无误");
return getNext().Invoke(input, getNext);
}
}
public bool WillExecute
{
get { return true; }
}
}
- 在执行注册方法之前执行缓存方法
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 = "Richard" });
return getNext().Invoke(input, getNext);
}
public bool WillExecute
{
get { return true; }
}
}
- 让异常包裹所有的方法,先执行业务逻辑
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; }
}
}
- 执行LogAfter方法,也是需要等业务方法执行完之后再执行的。
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; }
}
}
回过头再看一下整个的执行过程:一开始进来的时候是MoniterBehavior方法,开启秒表,然后依次往后执行LogBeforeBehavior,ParameterCheckBehavior,CachingBehavior,ExceptionLoggingBehavior,LogAfterBehavio,遇到getNext()(input, getNext)
就往后执行,直到执行到业务代码完成了之后,再依次往前返回,实际上就是net core里面的管道。getNext()(input, getNext)
和getNext().Invoke(input, getNext)
是一个意思,getNext()返回是一个委托,可以直接invoke进行执行,也可以直接调用。
值得注意的问题:
- 顺序问题,配置文件的注册顺序是调用顺序,然后才是业务方法,但是扩展逻辑可以在业务方法后;例如Exception那里。
- 接口方法不需要某个AOP扩展:a.判断方法; b.推荐用特性(在接口声明的地方用)
Input.Target.GetType().GetCustomAttributes()获取特性,然后可以针对某些方法不生效,例如:
//接口声明特性
public interface IUserProcessor
{
[TestAttritubte]
void RegUser(User user);
}