.NET 通过 Autofac 和 DynamicProxy 实现AOP - 晓晨Master - 博客园
Excerpt
什么是AOP?引用百度百科:AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。实现AOP主要由两种方式,一种是编译时静态植入,优点是效率高,缺点是缺乏灵活性,.net下postsharp为代表者(
什么是AOP?引用百度百科:AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。实现AOP主要由两种方式,一种是编译时静态植入,优点是效率高,缺点是缺乏灵活性,.net下postsharp为代表者(这个是收费的)。另一种方式是动态代理,优缺点与前者相反,动态为目标类型创建代理,通过代理调用实现拦截。AOP能做什么,常见的用例是事务处理、日志记录等等。下面就讲讲Autofac怎么实现AOP,Autofac是一个.net下非常优秀,性能非常好的IOC容器(.net下效率最高的容器),加上AOP简直是如虎添翼。Autofac的AOP是通过Castle(也是一个容器)项目的核心部分实现的,名为Autofac.Extras.DynamicProxy,顾名思义,其实现方式为动态代理。
使用前的准备:
通过Nuget安装程序包 :Autofac、Autofac.Extras.DynamicProxy,安装成功之后会增加三个引用
下面正式开始了!
第一步:创建拦截器
下面是一个简单的拦截器示例,该拦截器的功能是显示被拦截的方法名称、参数列表和返回结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <span> 1</span> <span>///</span> <span><summary></span> <span> 2</span> <span>///</span><span> 拦截器 需要实现 IInterceptor接口 Intercept方法 </span><span> 3</span> <span>///</span> <span></summary></span> <span> 4</span> <span>public</span> <span>class</span><span> CallLogger: IInterceptor </span><span> 5</span> <span> { </span><span> 6</span> <span> TextWriter _output; </span><span> 7</span> <span> 8</span> <span>public</span><span> CallLogger(TextWriter output) </span><span> 9</span> <span> { </span><span>10</span> _output =<span> output; </span><span>11</span> <span> } </span><span>12</span> <span>13</span> <span>///</span> <span><summary></span> <span>14</span> <span>///</span><span> 拦截方法 打印被拦截的方法执行前的名称、参数和方法执行后的 返回结果 </span><span>15</span> <span>///</span> <span></summary></span> <span>16</span> <span>///</span> <span><param name="invocation"></span><span>包含被拦截方法的信息</span><span></param></span> <span>17</span> <span>public</span> <span>void</span><span> Intercept(IInvocation invocation) </span><span>18</span> <span> { </span><span>19</span> <span>20</span> _output.WriteLine(<span>"</span><span>你正在调用方法 \"{0}\" 参数是 {1}... </span><span>"</span><span>, </span><span>21</span> <span> invocation.Method.Name, </span><span>22</span> <span>string</span>.Join(<span>"</span><span>, </span><span>"</span>, invocation.Arguments.Select(a => (a ?? <span>""</span><span>).ToString()).ToArray())); </span><span>23</span> <span>24</span> <span>//</span><span>在被拦截的方法执行完毕后 继续执行</span> <span>25</span> <span> invocation.Proceed(); </span><span>26</span> <span>27</span> _output.WriteLine(<span>"</span><span>方法执行完毕,返回结果:{0}</span><span>"</span><span>, invocation.ReturnValue); </span><span>28</span> <span> } </span><span>29</span> }
|
第二步:注册拦截器到Autofac容器
拦截器必须注册到Aufofac容器中,可以通过拦截器类型或者命名注入,这两种方式会让使用拦截器的方法有所不同(后面会讲到)。
1 2 3 4 5 6
| <span>1</span> <span>//</span><span> 命名注入</span> <span>2</span> builder.Register(c => <span>new</span><span> CallLogger(Console.Out)) </span><span>3</span> .Named<IInterceptor>(<span>"</span><span>log-calls</span><span>"</span><span>); </span><span>4</span> <span>5</span> <span>//</span><span> 类型注入</span> <span>6</span> builder.Register(c => <span>new</span> CallLogger(Console.Out));
|
第三步:启用拦截器
启用拦截器主要有两个方法:EnableInterfaceInterceptors(),EnableClassInterceptors()。
EnableInterfaceInterceptors方法会动态创建一个接口代理
EnableClassInterceptors方法会创建一个目标类的子类代理类,这里需要注意的是只会拦截虚方法,重写方法
启用拦截器示例代码:
1 2 3 4
| <span>//</span><span>启用类代理拦截</span> builder.RegisterType<Circle><span>().EnableClassInterceptors(); </span><span>//</span><span>启用接口代理拦截</span> builder.RegisterType<Circle>().EnableInterfaceInterceptors();
|
第四步:指明要拦截的类型
有两种方法:
第一种:给类型加上特性Attribute
第二种:在注册类型到容器的时候动态注入拦截器
1 2
| <span>1</span> <span>//</span><span>动态注入拦截器CallLogger</span> <span>2</span> builder.RegisterType<Circle>().InterceptedBy(<span>typeof</span>(CallLogger)).EnableClassInterceptors();
|
第五步:测试效果了
1.类代理拦截
Circle类代码:
2.接口代理拦截
IShape接口代码:
1 2 3 4 5 6 7 8
| <span>1</span> <span>public</span> <span>interface</span><span> IShape </span><span>2</span> <span> { </span><span>3</span> <span>///</span> <span><summary></span> <span>4</span> <span>///</span><span> 形状的面积 </span><span>5</span> <span>///</span> <span></summary></span> <span>6</span> <span>void</span><span> Area(); </span><span>7</span> <span>8</span> }
|
Circle类代码:
1 2 3 4 5 6 7 8
| <span>1</span> <span>public</span> <span>class</span><span> Circle:IShape </span><span>2</span> <span> { </span><span>3</span> <span>//</span><span>重写父类抽象方法</span> <span>4</span> <span>public</span> <span>void</span><span> Area() </span><span>5</span> <span> { </span><span>6</span> Console.WriteLine(<span>"</span><span>你正在调用圆求面积的方法</span><span>"</span><span>); </span><span>7</span> <span> } </span><span>8</span> }
|
如果有什么地方写得不对欢迎批评改正,如果有什么疑问,欢迎提问。