概念
1、Aspect-Oriented Programming(面向切面编程),AOP就是对OOP(面向对象编程)的一种功能扩展。
2、需要将核心业务与公共业务分离。
AOP优势
1、将通用功能从业务逻辑中抽离出来,可以省略大量重复代码,有利于代码的操作和维护。
2、模块化开发,降低软件架构的复杂度。
静态AOP
采用装饰器模式或者代理模式实现。
抽象类与实现类:
public interface IInfoProcessor
{
void ReadInfo(string info);
}
public class InfoProcessor : IInfoProcessor
{
public InfoProcessor()
{
Console.WriteLine("InfoProcessor 构造函数执行了");
}
public void ReadInfo(string info)
{
Console.WriteLine($"主要业务逻辑,接收到一条信息或请求:{info}");
}
}
装饰器模式实现:
/// <summary>
/// 装饰器模式提供一个AOP功能
/// </summary>
public class InfoProcessorDecorator : IInfoProcessor
{
private IInfoProcessor _infoProcessor { get; set; }
public InfoProcessorDecorator(IInfoProcessor infoProcessor)
{
this._infoProcessor = infoProcessor;
}
public void ReadInfo(string info)
{
this.BeforeProceed(info);
this._infoProcessor.ReadInfo(info);
this.AfterProceed(info);
}
/// <summary>
/// 业务逻辑之前
/// </summary>
/// <param name="user"></param>
private void BeforeProceed(string info)
{
Console.WriteLine("方法执行前");
}
/// <summary>
/// 业务逻辑之后
/// </summary>
/// <param name="user"></param>
private void AfterProceed(string info)
{
Console.WriteLine("方法执行后");
}
}
上端调用:
public static void Execute()
{
/// 使用了设计模式:装饰模式
/// 好像功能实现,但是,需要对多个类进行切面的时候,需要创建多个Decorator
/// 没有通用性
IInfoProcessor processor = new InfoProcessor();// 实际的业务逻辑
processor = new InfoProcessorDecorator(processor);
processor.ReadInfo("哈哈哈");
}
动态AOP
采用Castle库实现。
AOP有很多方法可以实现,Castle的一个库,用来做动态AOP 。
抽象类与实现类:
public interface IInfoProcessor
{
void ReadInfo(string info);
}
public class InfoProcessor : IInfoProcessor
{
public InfoProcessor()
{
Console.WriteLine("InfoProcessor 构造函数执行了");
}
/// 注意:被切面的对象方法必须是 virtual
/// 虚方法在生成代理对象的时候可以被重写
// 需要把切面逻辑转移到特性里面去
[Log]
[Monitor]
public virtual void ReadInfo(string info)
{
Console.WriteLine($"主要业务逻辑,接收到一条信息或请求:{info}");
}
[Monitor]
public virtual void UpdateInfo(string info)
{
}
}
特性类中实现切面逻辑:
public abstract class AbstractAttributeBase : Attribute
{
public abstract void Execute();
}
public class LogAttribute : AbstractAttributeBase
{
public override void Execute()
{
Console.WriteLine("Log 切面逻辑");
}
}
public class MonitorAttribute : AbstractAttributeBase
{
public override void Execute()
{
Console.WriteLine("Monitor 切面逻辑");
}
}
继承接口,实现动态代理,并且将切面转移到特性中去(目前特性只实现了在业务逻辑之前加切面,如果要实现分别在业务逻辑的前后加逻辑,需要在特性中定义Before和After属性来实现;执行顺序可以增加Order属性来实现)
public class MyInterceptor :StandardInterceptor
{
public void Intercept(IInvocation invocation)
{
if (invocation.MethodInvocationTarget.IsDefined(typeof(AbstractAttributeBase), true))
{
foreach (var attr in invocation.MethodInvocationTarget.GetCustomAttributes(typeof(AbstractAttributeBase), true))
{
var log = attr as AbstractAttributeBase;
log.Execute();
}
}
//this.PreProceed(invocation);
invocation.Proceed();// 执行真正的逻辑
//this.PostProceed(invocation);
// 需要把切面逻辑转移到特性里面去
}
public void PreProceed(IInvocation invocation)
{
Console.WriteLine("方法执行前");
}
public void PostProceed(IInvocation invocation)
{
Console.WriteLine("方法执行后");
}
}
上端调用:
public static void Execute()
{
/// 使用代理的方式,针对不同的对象生成实例 生成一个代理对象
ProxyGenerator generator = new ProxyGenerator();// 代理生成器
MyInterceptor interceptor = new MyInterceptor();
InfoProcessor userprocessor = generator.CreateClassProxy<InfoProcessor>(interceptor);
userprocessor.ReadInfo("哈哈哈");
}
动态AOP与IOC整合
手动实现IOC,并在对象创建时整合动态的AOP功能
public class MyConstructorAttribute : Attribute
{
}
public class MyParameterAttribute : Attribute
{
public MyParameterAttribute(string shortName = null)
{
this.ShortName = shortName;
}
public string ShortName { get; set; }
}
public interface IMyIoc
{
// 注册
void Register<From, To>(string shortName = null);
// 获取实例
From Resolve<From>(string shortName = null);
}
/// <summary>
/// 第三方Ioc容器,需要与业务无关
/// </summary>
public class MyIoc : IMyIoc
{
Dictionary<string, Type> _containerDic = new Dictionary<string, Type>();
private string GenerateKey(string abstractName, string shortName) => $"{abstractName}_@$_{shortName}";
public void Register<AFrom, BTo>(string shortName = null)
{
string key = typeof(AFrom).FullName;// 获取抽象的全名称
key = GenerateKey(key, shortName);// 根据抽象全名称和ShortName组合一个Key【字典的索引】
if (!_containerDic.ContainsKey(key))
{
this._containerDic.Add(key, typeof(BTo));// 把需要创建实例的对象类型保存下来
}
}
/// <summary>
/// 1、根据抽象类型获取实例类型
/// 2、初始化构造参数
/// 3、构造实例
/// </summary>
/// <typeparam name="AFrom"></typeparam>
/// <param name="shortName"></param>
/// <returns></returns>
public AFrom Resolve<AFrom>(string shortName = null)
{
return (AFrom)ResolveObject(typeof(AFrom), shortName);
}
// 依赖N层的时候,需要递归创建对象
private object ResolveObject(Type abstractType, string shortName = null)
{
string key = abstractType.FullName;
key = GenerateKey(key, shortName);
if (!_containerDic.ContainsKey(key)) return null;
Type type = this._containerDic[key];
// 检查类型的构造函数
//var ctor = type.GetConstructors()[0];
// 通过以下方式获取标定了特性的构造函数
ConstructorInfo constructorInfo =
type.GetConstructors().FirstOrDefault(c => c.IsDefined(typeof(MyConstructorAttribute), true));
if (constructorInfo == null)
// 选择参数最多的构造函数
constructorInfo = type.GetConstructors().OrderByDescending(c => c.GetParameters().Length).First();
List<object> paramList = new List<object>();
// 检查构造函数的参数
foreach (var param in constructorInfo.GetParameters())
{
string paramShortName = "";
if (param.IsDefined(typeof(MyParameterAttribute), true))
{
paramShortName = ((MyParameterAttribute)param.GetCustomAttribute(typeof(MyParameterAttribute))).ShortName;
}
Type paramType = param.ParameterType;//参数的抽象类型
//string paramKey = GenerateKey(paramType.FullName, "");// 参数对应的类型的Key
//Type paramInstanceType = this._containerDic[paramKey];// 需要实例化的类型
//object paramInstance = Activator.CreateInstance(paramInstanceType);// 创建参数实例
object paramInstance = this.ResolveObject(paramType, paramShortName);// 创建参数实例
// 参数 也要做构造 函数检查
paramList.Add(paramInstance);
}
获取线程ID
//int threadId = System.Threading.Thread.CurrentThread.ManagedThreadId;
// 反射获取实例 对象
object instance = Activator.CreateInstance(type, paramList.ToArray());
// 利用反射获取所有属性,进行注入
// 属性注入
foreach (var item in type.GetProperties())
{
//item.IsDefined()
/// 判断是否有特性
/// 如果有特性就注入 利用ResolveObject方法
}
/// 字典-》名称+Type
/// 进程单例、线程单例
/// 名称+{Type,线程ID,实例对象列表,实例化类型(枚举:常规、进程单例、线程单例)}
//动态AOP实现
ProxyGenerator generator = new ProxyGenerator();// 代理生成器
MyInterceptor interceptor = new MyInterceptor();
instance = generator.CreateInterfaceProxyWithTarget(abstractType, instance, interceptor);
return instance;
}
}
上端调用:
static void Main(string[] args)
{
IMyIoc myIoc = new MyIoc();
myIoc.Register<IInfoProcessor, InfoProcessor>();
IInfoProcessor infoProcessor = myIoc.Resolve<IInfoProcessor>();
infoProcessor.ReadInfo("Hello AOP");
}
Prism.Unity中的AOP
Unity支持三方方式的AOP实现
- Interface(主流)—适用于BLL业务逻辑层的类,包括接口和实现类
- VirsualMethod—适用于ViewModel类
- MarshalByRefObject(单继承,很少用)
需要引用的库:
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Xaml.Behaviors.Wpf" version="1.1.19" targetFramework="net48" />
<package id="Prism.Core" version="8.0.0.1909" targetFramework="net48" />
<package id="Prism.Unity" version="8.0.0.1909" targetFramework="net48" />
<package id="Prism.Wpf" version="8.0.0.1909" targetFramework="net48" />
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.2" targetFramework="net48" />
<package id="System.Threading.Tasks.Extensions" version="4.5.2" targetFramework="net48" />
<package id="Unity.Abstractions" version="5.11.6" targetFramework="net48" />
<package id="Unity.Configuration" version="5.11.2" targetFramework="net48" />
<package id="Unity.Container" version="5.11.8" targetFramework="net48" />
<package id="Unity.Interception" version="5.11.1" targetFramework="net48" />
<package id="Unity.Interception.Configuration" version="5.11.1" targetFramework="net48" />
</packages>
定义特性和切面
public class LogHander : ICallHandler
{
public int Order { get; set; }// 执行顺序
public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
Console.WriteLine("LogHander方法执行前");
IMethodReturn methodReturn = getNext().Invoke(input, getNext);
Console.WriteLine("LogHander方法执行后");
/// 主要业务逻辑
/// 还有另外一个切面的话,需要把当前执行过的主业务逻辑传下去
return methodReturn;
}
}
public class MonitorHandler : ICallHandler
{
public int Order { get; set; }
public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
Console.WriteLine("MonitorHandler方法执行前");
IMethodReturn methodReturn = getNext().Invoke(input, getNext);
Console.WriteLine("MonitorHandler方法执行后");
return methodReturn;
}
}
public class LogHandlerAttribute : HandlerAttribute
{
public override ICallHandler CreateHandler(IUnityContainer container)
{
return new LogHander { Order = this.Order };
}
}
public class MonitorHandlerAttribute : HandlerAttribute
{
public override ICallHandler CreateHandler(IUnityContainer container)
{
return new MonitorHandler { Order = this.Order };
}
}
实现的业务逻辑:
public interface IMenuBll
{
[LogHandlerAttribute(Order = 2)]
[MonitorHandler(Order = 1)]
void GetMenus();
}
public class MenuBll : IMenuBll
{
public void GetMenus()
{
Console.WriteLine("获取了系统菜单=============");
}
}
1、代码配置AOP
protected override Window CreateShell()
{
/// 使用的容器:Unity
IUnityContainer unityContainer = Container.Resolve<IUnityContainer>();
// 代码处理 添加一个扩展,扩展用来选择容器,创建对象的时候考虑一下切面
unityContainer.AddNewExtension<Interception>().RegisterType<IMenuBll, MenuBll>();
unityContainer.Configure<Interception>().SetInterceptorFor<IMenuBll>(new InterfaceInterceptor());
unityContainer.Resolve<IMenuBll>().GetMenus();
return Container.Resolve<MainWindow>();
}
2、配置文件配置AOP
Unity.config配置文件
<configuration>
<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Unity.Configuration"/>
</configSections>
<unity>
<sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Unity.Interception.Configuration"/>
<containers>
<container name="aopContainer">
<extension type="Interception"/>
<register type="My.PrismLesson.Project.BLL.IMenuBll,My.PrismLesson.Project" mapTo="My.PrismLesson.Project.BLL.MenuBll,My.PrismLesson.Project">
<interceptor type="InterfaceInterceptor"/>
<interceptionBehavior type="My.PrismLesson.Project.Behavior.LogBeforeBehavior, My.PrismLesson.Project"/>
</register>
<register type="My.PrismLesson.Project.ViewModels.LoginWindowViewModel,My.PrismLesson.Project">
<interceptor type="VirtualMethodInterceptor"/>
<interceptionBehavior type="My.PrismLesson.Project.Behavior.LogBeforeBehavior, My.PrismLesson.Project"/>
</register>
<!--Mash-->
</container>
</containers>
</unity>
</configuration>
需要配置interceptionBehavior节点
public class LogBeforeBehavior : IInterceptionBehavior
{
public bool WillExecute => true;
public IEnumerable<Type> GetRequiredInterfaces()
{
return Type.EmptyTypes;
}
public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
{
MethodInfo methodInfo = input.Target.GetType().GetMethod(input.MethodBase.Name);
//相关的逻辑也可以改成特性中执行,上面的MethodInfo可以获取相应的特性
Console.WriteLine("LogBeforeBehavior方法执行前");
IMethodReturn methodReturn = getNext().Invoke(input, getNext);
Console.WriteLine("LogBeforeBehavior方法执行后");
return methodReturn;
}
}
protected override Window CreateShell()
{
/// 使用的容器:Unity
IUnityContainer unityContainer = Container.Resolve<IUnityContainer>();
// 配置文件
ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
fileMap.ExeConfigFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "UnityConfig\\Unity.Config");
Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
UnityConfigurationSection configSection = (UnityConfigurationSection)configuration.GetSection(UnityConfigurationSection.SectionName);
configSection.Configure(unityContainer, "aopContainer");
unityContainer.Resolve<IMenuBll>().GetMenus();
return Container.Resolve<MainWindow>();
}
3、ViewModel层实现AOP
以上的例子都是针对BLL(业务逻辑层)实现的AOP,只要ViewModel是通过IOC容器来实例化的,那么同样可以对ViewModel来实现AOP的功能。
ViewModel中要加切面的业务逻辑函数要为虚方法(virtual)
public class LoginWindowViewModel
{
public LoginWindowViewModel(Prism.Regions.IRegionManager region)
{
/// 如果VM不是由UnityContainer创建的,Region就不可能注入
}
private DelegateCommand<Window> _loginCommand;
public DelegateCommand<Window> LoginCommand
{
get
{
if (_loginCommand == null)
_loginCommand = new DelegateCommand<Window>((w) =>
{
DoLogin();
});
return _loginCommand;
}
set { _loginCommand = value; }
}
/// <summary>
/// 这个方法要为虚方法
/// </summary>
public virtual void DoLogin()
{
// 登录逻辑执行
Console.WriteLine("执行登录逻辑");
}
}
1)、配置文件配置AOP
Unity.config配置文件,配置interceptor为VirtualMethodInterceptor,采用虚方法注入,所以ViewModel中要加切面的业务逻辑函数要为虚方法(virtual)。
<configuration>
<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Unity.Configuration"/>
</configSections>
<unity>
<sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Unity.Interception.Configuration"/>
<containers>
<container name="aopContainer">
<extension type="Interception"/>
<register type="My.PrismLesson.Project.ViewModels.LoginWindowViewModel,My.PrismLesson.Project">
<interceptor type="VirtualMethodInterceptor"/>
<interceptionBehavior type="My.PrismLesson.Project.Behavior.LogBeforeBehavior, My.PrismLesson.Project"/>
</register>
<!--Mash-->
</container>
</containers>
</unity>
</configuration>
2)、代码配置AOP
protected override Window CreateShell()
{
/// 使用的容器:Unity
IUnityContainer unityContainer = Container.Resolve<IUnityContainer>();
// VM也可以进行代码的方式处理 实操
// 实战使用Prism -》Unity容器-》日志利用AOP的方式处理 业务逻辑的时候不用管一些公共逻辑
unityContainer.RegisterType<LoginWindowViewModel>(new Interceptor<VirtualMethodInterceptor>(),new InterceptionBehavior<LogBeforeBehavior>());
return Container.Resolve<MainWindow>();
}