20181122_C#中AOP_使用Unity实现AOP
一. 使用Unity的AOP实现
a) 整体项目截图:
b) 添加Unity的Nuget包, 直接使用最新版就行, 需要添加两个 Unity 和 Unity.Interception (这个是为AOP做的一个扩展)
c) AOP配置文件, 详细注释(CfgFiles\Unity.Config)
- <!--这是一个标准Unity配置文件的格式-->
- <configuration> <!--根节点名称-->
- <configSections>
- <!--name容器的名称 type 表示如何查找节点, 这是固定写法-->
- <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="aopContainer">
- <!--定义一个容器; 表示在IUserProcessor这个接口下所有的方法都给他增加, 这个容器(aopContainer)内所包含的方法-->
- <extension type="Interception"/>
- <!--这里使用Interception类型的扩展; 其实在Unity中有三种类型来处理这件事; 这种是属于实现接口的方法, 还有一种是继承父类; 第三种是使用Virtual虚方法的形式, 使用虚方法有点类似于CastleProxyAOP这个代理-->
- <!--把接口映射到具体的实现类; 也就是说告诉容器如果遇到了MyAOP.UnityWay.IUserProcessor这个抽象, 就使用MyAOP.UnityWay.UserProcessor来帮我们实例化-->
- <!--MyAOP.UnityWay.IUserProcessor完整的接口名称, MyAOP是程序所在的dll名称, 程序集的名称(当然在这里是当前exe文件的名称-->
- <!--MyAOP.UnityWay.UserProcessor具体的实现类, MyAOP和上面的解释一样-->
- <!--MyAOP是程序集的名称-->
- <!-- register type="MyAOP.UnityWay.IUserProcessor,MyAOP" mapTo="MyAOP.UnityWay.UserProcessor,MyAOP" 这一句整体的意思就是告诉容器, 如果遇到IUserProcessor这个抽象, 就使用UserProcessor类来帮我进行初始化一个具体的实例出来-->
- <register type="MyAOP.UnityWay.IUserProcessor,MyAOP" mapTo="MyAOP.UnityWay.UserProcessor,MyAOP">
- <!--Unity中的AOP有三种模式, 第一种必须继承MarshalByRefObject这个父类的AOP
- 第二种必须是虚方法的AOP
- 第三种就是下面写的接口形式的 InterfaceInterceptor 一般都用这种接口形式的:
- 表示只要是接口下的所有方法, 都会被增加上下面的方法
- -->
- <interceptor type="InterfaceInterceptor"/> <!--AOP支持的模式; 推荐使用这种接口模式的-->
- <!--下面这五个表示 在IUserProcessor接口中的所有方法, 都拥有下面方法的扩展-->
- <!--异常处理的; 注意如果你想全局处理异常, 则应该把异常处理的Behavior放到最顶层, 否则在Unity中自己抛出的异常则会抓不到-->
- <interceptionBehavior type="MyAOP.UnityWay.ExceptionLoggingBehavior, MyAOP"/>
- <!--缓存的behavior; 注意缓存的Behavior也应该放在记录日志之前; 需要注意的是, 如果被缓存命中了, 那么后面的AOP也不会执行的-->
- <interceptionBehavior type="MyAOP.UnityWay.CachingBehavior, MyAOP"/>
- <!--方法执行前写日志-->
- <interceptionBehavior type="MyAOP.UnityWay.LogBeforeBehavior, MyAOP"/>
- <!--参数检查-->
- <interceptionBehavior type="MyAOP.UnityWay.ParameterCheckBehavior, MyAOP"/>
- <!--方法执行后做的事情-->
- <interceptionBehavior type="MyAOP.UnityWay.LogAfterBehavior, MyAOP"/>
- </register>
- </container>
- </containers>
- </unity>
- </configuration>
d) Model代码:
- public class User
- {
- public int Id { get; set; }
- public string Name { get; set; }
- public string Password { get; set; }
- }
e) IuserProcessor代码:
- public interface IUserProcessor
- {
- void RegUser(User user);
- User GetUser(User user);
- }
f) UserProcessor代码:
- public class UserProcessor : IUserProcessor
- {
- public void RegUser(User user)
- {
- Console.WriteLine("用户已注册。");
- //throw new Exception("11");
- }
- [Obsolete]
- public User GetUser(User user)
- {
- return user;
- }
- }
g) LogBeforeBehavior代码(在方法执行之前写日志):
- /// <summary>
- /// 1. 标准的通过AOP写日志的功能, 必须实现IInterceptionBehavior; 这个接口来自于Unity容器
- /// </summary>
- public class LogBeforeBehavior : IInterceptionBehavior
- {
- /// <summary>
- /// 3. 此方法为固定写法
- /// </summary>
- /// <returns></returns>
- public IEnumerable<Type> GetRequiredInterfaces()
- {
- return Type.EmptyTypes;
- }
- /// <summary>
- /// 4. 关键方法
- /// </summary>
- /// <param name="input"></param>
- /// <param name="getNext"></param>
- /// <returns></returns>
- public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
- {
- Console.WriteLine("LogBeforeBehavior");
- //5. 如果有的方法想用, 有的方法不想被AOP, 则可以在这里做切断
- //这里可以将某个方法打上特性,然后获取MemberInfo中的特性信息, 以此来判断哪个
- //方法需要AOP切入,哪些方法不需要AOP切入
- Console.WriteLine(input.MethodBase.Name);
- // Console.WriteLine( input.MethodBase.GetCustomAttributes(true)[0].ToString() );
- foreach (var item in input.Inputs) //input中包含了所有的参数信息
- {
- Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(item));
- //反射&序列化获取更多信息
- }
- // Unity.Interception.InterceptionBehaviors.InvokeInterceptionBehaviorDelegate gn = getNext();
- // return getNext()(input, getNext);//注意这种写法也是可以的; 这种写法的意思就是getNext()方法返回了一个委托, 然后委托又被调用了
- return getNext().Invoke(input, getNext);//4. 固定写法
- // return null;
- //return gn.Invoke(input, getNext);
- //getNext()表示执行完当前的方法之后, 去执行原始方法; 但是注意的是, getNext()中可能有多个方法
- }
- /// <summary>
- /// 2. 固定写法
- /// </summary>
- public bool WillExecute
- {
- get { return true; }
- }
- }
h) LogAfterBehavior代码(方法执行之后写日志):
- //IInterceptionBehavior来自于AOP容器中 Unity.Interception.InterceptionBehaviors;
- public class LogAfterBehavior : IInterceptionBehavior
- {
- /// <summary>
- /// 固定写法
- /// </summary>
- /// <returns></returns>
- public IEnumerable<Type> GetRequiredInterfaces()
- {
- return Type.EmptyTypes;
- }
- /// <summary>
- /// 可以记录调用时间, 参数, 函数名称
- /// </summary>
- /// <param name="input"></param>
- /// <param name="getNext"></param>
- /// <returns></returns>
- public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
- {
- //getNext()(input, getNext); 关键点; 如果业务代码写在这句话之前, 那么就会先执行业务代码, 再执行配置文件中配置的实例代码; 反之同理
- IMethodReturn methodReturn = getNext().Invoke(input, getNext);//执行后面的全部动作
- //原始方法执行后
- Console.WriteLine("LogAfterBehavior");
- Console.WriteLine(input.MethodBase.Name);
- foreach (var item in input.Inputs)
- {
- Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(item));
- //反射&序列化获取更多信息
- }
- Console.WriteLine("LogAfterBehavior" + methodReturn.ReturnValue);
- return methodReturn;
- }
- /// <summary>
- /// 固定写法
- /// </summary>
- public bool WillExecute
- {
- get { return true; }
- }
- }
i) ParameterCheckBehavior代码(在方法执行之前进行参数校验):
- /// <summary>
- /// 参数检查
- /// </summary>
- 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[] as User;//可以不写死类型,反射+特性完成数据有效性监测
- if (user.Password.Length < )//可以过滤一下敏感词
- {
- //返回一个异常
- return input.CreateExceptionMethodReturn(new Exception("密码长度不能小于10位"));
- //注意只要抛出异常, 那么后面的都不会再执行了, 在这里也就是说后的 logafterbehavior是不会再执行了
- //throw new Exception("密码长度不能小于10位");
- }
- else
- {
- Console.WriteLine("参数检测无误");
- return getNext().Invoke(input, getNext);
- }
- }
- public bool WillExecute
- {
- get { return true; }
- }
- }
j) ExceptionLoggingBehavior代码(异常处理):
- /// <summary>
- /// 异常处理
- /// </summary>
- public class ExceptionLoggingBehavior : IInterceptionBehavior
- {
- public IEnumerable<Type> GetRequiredInterfaces()
- {
- return Type.EmptyTypes;
- }
- public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
- {
- IMethodReturn methodReturn = getNext()(input, getNext);
- Console.WriteLine("ExceptionLoggingBehavior");
- if (methodReturn.Exception == null) //检查methodReturn中是否有异常
- {
- Console.WriteLine("无异常");
- }
- else
- {
- Console.WriteLine($"异常:{methodReturn.Exception.Message}");
- }
- return methodReturn;
- }
- public bool WillExecute
- {
- get { return true; }
- }
- }
k) CachingBehavior代码(缓存处理):
- /// <summary>
- /// 缓存AOP
- /// </summary>
- public class CachingBehavior : IInterceptionBehavior
- {
- public IEnumerable<Type> GetRequiredInterfaces()
- {
- return Type.EmptyTypes;
- }
- /// <summary>
- /// 定义缓存字典
- /// </summary>
- private static Dictionary<string, object> CachingBehaviorDictionary = new Dictionary<string, object>();
- public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
- {
- Console.WriteLine("CachingBehavior");
- //把方法的名称和所有的参数列表全部标识为key , 然后放到字典里面;
- string key = $"{input.MethodBase.Name}_{Newtonsoft.Json.JsonConvert.SerializeObject(input.Inputs)}";
- //当方法名和参数都不变, 则表示有缓存
- if (CachingBehaviorDictionary.ContainsKey(key))
- {
- return input.CreateMethodReturn(CachingBehaviorDictionary[key]);//直接返回 [短路器] 不再往下; 包括后面的Behavior也不会再执行了; 因为全部被直接短路了
- }
- else
- {
- //如果字典中没有, 则继续执行这里
- IMethodReturn result = getNext().Invoke(input, getNext);
- if (result.ReturnValue != null) //并将其加入到缓存列表中去; 当然缓存中放一个null没有任何意义, 所以这里判断一下
- {
- //不存在则, 加入缓存中
- CachingBehaviorDictionary.Add(key, result.ReturnValue);
- }
- return result;
- }
- }
- public bool WillExecute
- {
- get { return true; }
- }
- }
l) UnityConfigAOP代码(调用):
- /// <summary>
- /// 使用EntLib\PIAB Unity 实现动态代理
- ///
- /// 1. 添加引用→右键→添加 NuGet包, 添加Unity的包引用
- /// 2. 这里添加的 Unity是5.8.13的版本, 而Unity.Interception是5.5.0; 因为最新的(5.5.5)不支持 .net 4.0
- /// 3. Unity是一个 Unity的容器; 而Unity.Interception当Unity做AOP时的一个扩展
- /// </summary>
- public class UnityConfigAOP
- {
- public static void Show()
- {
- User user = new User()
- {
- Name = "孙悟空",
- Password = ""
- };
- {
- //1. 初始化UnityContainer容器
- IUnityContainer container = new UnityContainer();
- ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
- //2. 开始读取配置文件
- fileMap.ExeConfigFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "CfgFiles\\Unity.Config");
- Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
- UnityConfigurationSection configSection = (UnityConfigurationSection)configuration.GetSection(UnityConfigurationSection.SectionName);
- //3. 使用配置文件中aopContainer节点下的所有配置信息来初始化container这个AOP容器
- configSection.Configure(container, "aopContainer"); //10. 这个aopContainer就是配置文件中的 <container name="aopContainer"> 这里这个容器的名称 ; 注意如果配置文件中的名字和这里的名字不一样就会报出以下错误:
- // The container named "aopContainer" is not defined in this configuration section.
- // 在这里创建对象, 创建的规则就来自于配置文件的 <register type="MyAOP.UnityWay.IUserProcessor,MyAOP" mapTo="MyAOP.UnityWay.UserProcessor,MyAOP"> 这一行; 如果是 IUserProcessor 类型, 就是使用UserProcessor 来创建实例
- IUserProcessor processor = container.Resolve<IUserProcessor>();
- processor.RegUser(user); //4. 注意, 当程序运行到这里的时候, 正常情况应该是去调用userProcessor的Reguser方法, 但是由于在配置文件中进行了配置, 所以它会去执行 LogBeforeBehavior 的 Invoke的方法(当aopContainer这容器中只有一个LogBeforeBehavior的时候, 如果多个, 则那个方法在前面则优先执行, 其它的上依次的调)
- User userNew1 = processor.GetUser(user); //调用GetUser的时候, 也会执行配置文件中对应配置了的方法
- //演示缓存Behavior
- User userNew2 = processor.GetUser(user);
- }
- }
- }
20181122_C#中AOP_使用Unity实现AOP的更多相关文章
- 运用Unity实现AOP拦截器
运用Unity实现AOP拦截器[结合异常记录实例] 本篇文章将通过Unity实现Aop异常记录功能:有关Unity依赖注入可以看前两篇文章: 1:运用Unity实现依赖注入[结合简单三层实例] 2:运 ...
- 运用Unity实现AOP拦截器[结合异常记录实例]
本篇文章将通过Unity实现Aop异常记录功能:有关Unity依赖注入可以看前两篇文章: 1:运用Unity实现依赖注入[结合简单三层实例] 2:运用Unity实现依赖注入[有参构造注入] 另早期 ...
- Ioc 之 Unity的AOP功能
前面我们介绍了Unity的依赖注入功能,现在来介绍下Unity的AOP功能.AOP是面向切面编程,它能够使我们在不改变现有代码结构的情况下额外的为其添加一些功能. 我们还是使用配置文件来对类型进行注入 ...
- Xcode中如何集成Unity
项目中需要集成unity,摸索了大半周,碰到了很多坑,终于搞定. 我的方法是,通过unity导出一个空的iOS项目,然后再新建一个Xcode项目,针对配置页面一一对应.直到配置完全一样,然后倒入相关资 ...
- 在.NET项目中使用PostSharp,实现AOP面向切面编程处理
PostSharp是一种Aspect Oriented Programming 面向切面(或面向方面)的组件框架,适用在.NET开发中,本篇主要介绍Postsharp在.NET开发中的相关知识,以及一 ...
- JavaEE开发之Spring中的依赖注入与AOP
上篇博客我们系统的聊了<JavaEE开发之基于Eclipse的环境搭建以及Maven Web App的创建>,并在之前的博客中我们聊了依赖注入的相关东西,并且使用Objective-C的R ...
- unity中调试模型时unity崩溃问题
这个问题是在我调试3D模型资源时出现的,每当在Scene场景中调试模型时unity崩溃,出现Unity Bug Reporter页面,反复出现这个问题,很烧脑 对于这个问题我表示很无语,但是经过不断查 ...
- JavaEE开发之Spring中的依赖注入与AOP编程
上篇博客我们系统的聊了<JavaEE开发之基于Eclipse的环境搭建以及Maven Web App的创建>,并在之前的博客中我们聊了依赖注入的相关东西,并且使用Objective-C的R ...
- [译]如何在ASP.NET Core中实现面向切面编程(AOP)
原文地址:ASPECT ORIENTED PROGRAMMING USING PROXIES IN ASP.NET CORE 原文作者:ZANID HAYTAM 译文地址:如何在ASP.NET Cor ...
随机推荐
- Django中通过定时任务触发页面静态化的方式
安装 pip install django-crontab 添加应用 INSTALLED_APPS = [ ... 'django_crontab', # 定时任务 ... ] 设置任务的定时时间 在 ...
- New Concept English there (5)
25w/m Editors of newspapers and magazines often go to extremes to provide their readers with unimpor ...
- PostgreSQL copy命令使用记录
上篇写到用pg_bulkload来导入数据,但是实际的环境是solaris,pg_bulkload还不支持,于是用copy的方式,下面附上脚本 -bash-4.1$ cat copy.sh #!/bi ...
- 从JDK源码角度看Boolean
Java的Boolean类主要作用就是对基本类型boolean进行封装,提供了一些处理boolean类型的方法,比如String类型和boolean类型的转换. 主要实现源码如下: public fi ...
- HTTP 1.1 协议规范
1. 内容协商 请求一个特殊编码的过程在 HTTP 1.1 规范中称为内容协商:
- js之验证码倒计时功能
<!DOCTYPE html> <html > <head> <meta http-equiv="Content-Type" conten ...
- rancher下的kubernetes之一:构建标准化vmware镜像
学习kubernetes的时候,我们需要在kubernetes环境下实战操作,然而kubernetes环境安装并不容易,现在通过rancher可以简化安装过程,咱们来实战rancher下的kubern ...
- 在IIS上搭建FTP站点
操作环境 系统:win7 IIS版本:7.5 FTP传输工具:FlashXP 概述 本文介绍了如何在win7下利用IIS(默认已安装IIS和FTP功能)搭建FTP站点,FTP站点的常用配置. 快速搭建 ...
- CentOS跨网段访问
centos6.2_64删除虚拟网卡 virbr0 卸载以下组件,然后重启系统 yum remove libvirt yum remove libvirt-python 来源:http://www.i ...
- HDU - 4339: Query(bitset暴力找下一个为1的)
题意:给定A,B长度相同的字符串,Q次操作,修改操作位单个字符修改,查询操作为询问从某点开始有多少连续相同的字符. 思路:我们把不相同的设为1,相同的设为0,那么询问就是找下一个为1的为位置,可以用线 ...