为什么要有AOP

  需求总是变化的,比如经常会对一些方法后期增加日志、异常处理、权限、缓存、事务的处理,遇到这种情况我们往往只能修改类。

  为了应对变化,我们常常使用设计模式解决,但是也有其局限性:设计模式这能替换整个对象,但是没办法把一个类动态改变。所以我们需要引入AOP的编程思想,因为它允许开发者动态的修改静态的OO模型,构造出一个不断增长,不断变化的需求。

  AOP是一种编程思想,是对OOP面向对象编程思想的补充。

  使用AOP编程可以方便我们聚焦一些核心业务逻辑,比如权限、异常、日志、缓存、事务这种通用功能可以封装起来,通过AOP添加到指定的方法,简化程序设计。

如何使用AOP

1、添加引用

2、配置文件

在configuration节点下添加(注意看注释)

 <configSections>
   <!--这里添加一个unity扩展-->
<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="oneBehaviorTestContainer">
<extension type="Interception" />
<!--设置接口的实现类-->
<register type="IServers.IUser,IServers" mapTo="Providers.UserProvider,Providers">
<!--InterfaceInterceptor:继承接口的方法都会被拦截。
TransparentProxyInterceptor:继承类使用的方法都会被拦截。
VirtualMethodInterceptor:继承的方法必须是虚方法且必须是公开的方法才会被拦截。-->
<interceptor type="InterfaceInterceptor"/>
<!--配置文件的注册顺序是调用顺序,然后才是业务方法,但是扩展逻辑也可以在业务方法之后-->
<!--应该把捕捉异常的拦截器放到第一位,这样还可以捕捉其他拦截器内的异常-->
<interceptionBehavior type="AOPExe.Interceptions.ExceptionBehavior, AOPExe"/>
<!--应该把性能计算的拦截器放到第二位,这样还可以捕捉其他拦截器内的异常-->
<interceptionBehavior type="AOPExe.Interceptions.MonitorBehavior, AOPExe"/>
<!--参数检查-->
<interceptionBehavior type="AOPExe.Interceptions.ParameterCheckBehavior, AOPExe"/>
<!--缓存-->
<interceptionBehavior type="AOPExe.Interceptions.CachingBehavior, AOPExe"/>
</register>
</container>
</containers>
</unity>

3、程序调用

3.1程序调用

                //声明一个Unity容器
var container = new UnityContainer();
//获取到Unity部分
UnityConfigurationSection unitySection = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
//将扩展部分注册到容器
unitySection.Configure(container, "oneBehaviorTestContainer");
//获取接口实例
var userService = container.Resolve<IUser>();
//调用接口方法(该方法被添加了拦截器)
var user = userService.GetUser();
if (user!=null)
{
Console.WriteLine(user.Name);
}

3.2 拦截器

拦截器的类要实现:IInterceptionBehavior接口

    /// <summary>
/// 缓存(用于方法前)
/// </summary>
public class CachingBehavior : IInterceptionBehavior
{
public bool WillExecute => true; public IEnumerable<Type> GetRequiredInterfaces()
{
return Type.EmptyTypes;
} public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
{
return input.CreateMethodReturn(new UserModel() { Id = , Name = "缓存姓名" });
}
}
  
/// <summary> 
/// 异常处理
/// </summary>
public class ExceptionBehavior : IInterceptionBehavior
{
public bool WillExecute => true; public IEnumerable<Type> GetRequiredInterfaces()
{
return Type.EmptyTypes;
} public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
{
IMethodReturn methodReturn = getNext()(input, getNext);
if (methodReturn.Exception != null)
{
//处理异常
Console.WriteLine("ExceptionBehavior捕捉到异常:" + methodReturn.Exception.Message);
//隐藏异常
methodReturn.Exception = null;
}
else
{
Console.WriteLine("ExceptionBehavior没有捕捉到异常。");
}
return methodReturn;
}
}
/// <summary>
/// 性能检测
/// </summary>
public class MonitorBehavior : IInterceptionBehavior
{
public bool WillExecute => true; public IEnumerable<Type> GetRequiredInterfaces()
{
return Type.EmptyTypes;
} public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
{
Stopwatch sw = new Stopwatch();
sw.Start();
var returnMethod = getNext()(input, getNext);
sw.Stop();
Console.WriteLine("MonitorBehavior 本次方法共耗时:" + sw.ElapsedMilliseconds);
return returnMethod;
}
}
/// <summary>
/// 参数检查
/// </summary>
public class ParameterCheckBehavior : IInterceptionBehavior
{
public bool WillExecute => true; public IEnumerable<Type> GetRequiredInterfaces()
{
return Type.EmptyTypes;
} public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
{
Console.WriteLine("ParameterCheckBehavior,打印所有传入参数:");
foreach (var ipt in input.Inputs)
{
Console.WriteLine(ipt);
}
int id = (int)input.Inputs[];
if (id > )
{
//这种写法不对
//new Exception("Id不能超过100");
return input.CreateExceptionMethodReturn(new Exception("Id不能超过100"));
}
else
{
Console.WriteLine("参数检查通过");
}
return getNext()(input, getNext);
}
}

3.3业务方法

 public interface IUser
{
UserModel GetUser(int Id);
}
public UserModel GetUser(int Id)
{
Console.WriteLine("数据库中读取UserModel,可能时间会比较长一点点(对比缓存)");
//throw new Exception("业务方法中抛出异常");//这里抛出的异常,也可以捕获到
Thread.Sleep();
return new UserModel()
{
Id = Id
,
Name = "张三"
};
}

4、如果不想对接口内的所有方法都添加拦截该怎么办?

我能想到的办法是为接口方法添加特性,然后再拦截器内判断该方法是否含有该特性。小伙伴们有啥想法欢迎留言。

Unity容器实现AOP面向切面编程的更多相关文章

  1. 学习笔记: AOP面向切面编程和C#多种实现

    AOP:面向切面编程   编程思想 OOP:一切皆对象,对象交互组成功能,功能叠加组成模块,模块叠加组成系统      类--砖头     系统--房子      类--细胞     系统--人    ...

  2. 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之十 || AOP面向切面编程浅解析:简单日志记录 + 服务切面缓存

    代码已上传Github+Gitee,文末有地址 上回<从壹开始前后端分离[ .NET Core2.0 Api + Vue 2.0 + AOP + 分布式]框架之九 || 依赖注入IoC学习 + ...

  3. Spring:AOP面向切面编程

    AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果. AOP是软件开发思想阶段性的产物,我们比较熟悉面向过程O ...

  4. 浅谈Spring AOP 面向切面编程 最通俗易懂的画图理解AOP、AOP通知执行顺序~

    简介 我们都知道,Spring 框架作为后端主流框架之一,最有特点的三部分就是IOC控制反转.依赖注入.以及AOP切面.当然AOP作为一个Spring 的重要组成模块,当然IOC是不依赖于Spring ...

  5. 谈一谈AOP面向切面编程

    AOP是什么 : AOP面向切面编程他是一种编程思想,是指在程序运行期间,将某段代码动态的切入到指定方法的指定位置,将这种编程方式称为面向切面编程 AOP使用场景 : 日志 事务 使用AOP的好处是: ...

  6. Z从壹开始前后端分离【 .NET Core2.0/3.0 +Vue2.0 】框架之十 || AOP面向切面编程浅解析:简单日志记录 + 服务切面缓存

    本文梯子 本文3.0版本文章 代码已上传Github+Gitee,文末有地址 大神反馈: 零.今天完成的深红色部分 一.AOP 之 实现日志记录(服务层) 1.定义服务接口与实现类 2.在API层中添 ...

  7. aop面向切面编程的实现

    aop主要用于日志记录,跟踪,优化和监控 下面是来自慕课网学习的一些案例,复制黏贴就完事了,注意类和方法的位置 pom添加依赖: <dependency> <groupId>o ...

  8. AOP 面向切面编程, Attribute在项目中的应用

    一.AOP(面向切面编程)简介 在我们平时的开发中,我们一般都是面对对象编程,面向对象的特点是继承.多态和封装,我们的业务逻辑代码主要是写在这一个个的类中,但我们在实现业务的同时,难免也到多个重复的操 ...

  9. AOP面向切面编程的四种实现

     一.AOP(面向切面编程)的四种实现分别为最原始的经典AOP.代理工厂bean(ProxyFacteryBean)和默认自动代理DefaultAdvisorAutoProxyCreator以及Bea ...

随机推荐

  1. Linux 常用工具openssh之ssh-add

    前言 ssh-add命令是把专用密钥添加到ssh-agent的高速缓存中,从而提高ssh的认证速度 语法 ssh-add [-cDdLlXx] [-t life] [file ...] 选项 -D:删 ...

  2. vue-particles做背景,鼠标动画粒子连线填坑(按钮没有点击响应)

    为了提高页面展示效果,登录界面内容比较单一的,粒子效果作为背景经常使用到,vue工程中利用vue-particles可以很简单的实现页面的粒子背景效果. 解决问题: 以背景方式显示 无法获取按钮焦点, ...

  3. linux系统中的硬链接和软链接

    首先我们需要了解linux下硬链接以及软连接的基本概念.硬链接:新建的文件是已经存在的文件的一个别名,当原文件删除时,新建的文件仍然可以使用.软链接:也称为符号链接,新建的文件以“路径”的形式来表示另 ...

  4. tmobst4an

    (单选题)HTML代码: <table> <tr><td>Value 1</td><td></td></tr> &l ...

  5. 非常NB的一款快捷启动软件--Merry

    Merry 被设计为了能将日常重复性操作简化为一个快捷键或者命令.Merry 采用完全开放的体系, 可以使用 Lua 或者外部程序来扩展 Merry 的功能. 另附一个自己扩展的LUA脚本: --启动 ...

  6. [C/C++]_[Unicode转Utf8,Ansi转Unicode,Ansi文件转Utf8文件]

    http://blog.csdn.net/infoworld/article/details/15337665 场景: 1.也就只有windows需要那么麻烦,还搞一个ANSI编码.学学mac os ...

  7. python序列化对象和反序列化

    1.首先不管哪种语言都会用到序列化和反序列化的过程, 2.序列化:把对象转换为字节序列的过程称为对象的序列化:   反序列化:把对象转换为字节序列的过程称为对象的序列化. 3.序列化的作用:把对象(变 ...

  8. 基于TensorFlow的MNIST手写数字识别-初级

    一:MNIST数据集    下载地址 MNIST是一个包含很多手写数字图片的数据集,一共4个二进制压缩文件 分别是test set images,test set labels,training se ...

  9. SpringBoot2.x操作缓存的新姿势

    一.介绍 spring cache 是spring3版本之后引入的一项技术,可以简化对于缓存层的操作,spring cache与springcloud stream类似,都是基于抽象层,可以任意切换其 ...

  10. vue子向父传值

    要弄懂子组件如何向父组件传值,需要理清步骤 子组件向父组件传值的步骤 一:子组件在组件标签上通过绑定事件的方式向父组件发射数据 <!--html--><template id=&qu ...