动态代理AOP实现方法过滤
上一节实现了动态代理,接下来 有时候,我不需要在每一个方法都要记录日志,做权限验证 等等。 所有就有了这样的需求。AOP实现特定方法过滤,有选择性的来对方法实现AOP 拦截。就是本节标题所示。
举个例子,对于查询的方法我不需要记录日志,所以,我就找到如果以“Get”开头的方法,就不记录日志,否则就记录日志;所以基于这样一个需求,代码如下:
public override IMessage Invoke(IMessage msg)
{
var methodCall = msg as IMethodCallMessage;
var methodInfo = methodCall.MethodBase as MethodInfo;
if (!methodInfo.Name.StartsWith("Get"))
Log("In Dynamic Proxy - Before executing '{0}'",
methodCall.MethodName);
try
{
var result = methodInfo.Invoke(_decorated, methodCall.InArgs);
if (!methodInfo.Name.StartsWith("Get"))
Log("In Dynamic Proxy - After executing '{0}' ",
methodCall.MethodName);
return new ReturnMessage(result, null, 0,
methodCall.LogicalCallContext, methodCall);
}
catch (Exception e)
{
if (!methodInfo.Name.StartsWith("Get"))
Log(string.Format(
"In Dynamic Proxy- Exception {0} executing '{1}'", e),
methodCall.MethodName);
return new ReturnMessage(e, methodCall);
}
}
在上面的代码里,有3处是代码重复的,methodInfo.Name.StartsWith("Get"),对于代码重复的,我们可以提取为方法,提取后的代码如下:
private static bool IsValidMethod(MethodInfo methodInfo)
{ return !methodInfo.Name.StartsWith("Get");
}
现在你只需要修改一个地方,但是你还是的修改类的代码,假如有一天,你的项目里需要自定过滤条件,此时最好的方法就是将Filter
定义为属性供用户自己来出来,所以这个需求的代码 现在修改为如下:
class DynamicProxy<T> : RealProxy
{ private readonly T _decorated; private Predicate<MethodInfo> _filter; public DynamicProxy(T decorated) : base(typeof(T)) { _decorated = decorated; _filter = m => true; } public Predicate<MethodInfo> Filter { get { return _filter; } set { if (value == null) _filter = m => true; else _filter = value; } } private void Log(string msg, object arg = null) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(msg, arg); Console.ResetColor(); } public override IMessage Invoke(IMessage msg) { var methodCall = msg as IMethodCallMessage; var methodInfo = methodCall.MethodBase as MethodInfo; if (_filter(methodInfo)) Log("In Dynamic Proxy - Before executing '{0}'", methodCall.MethodName); try { var result = methodInfo.Invoke(_decorated, methodCall.InArgs); if (_filter(methodInfo)) Log("In Dynamic Proxy - After executing '{0}' ", methodCall.MethodName); return new ReturnMessage(result, null, 0, methodCall.LogicalCallContext, methodCall); } catch (Exception e) { if (_filter(methodInfo)) Log(string.Format( "In Dynamic Proxy- Exception {0} executing '{1}'", e), methodCall.MethodName); return new ReturnMessage(e, methodCall); } }
}
基于这样,用户就可以自定义 那些方法需要AOP 那些方法不需要。 例如:
public class RepositoryFactory
{ public static IRepository<T> Create<T>() { var repository = new Repository<T>(); var dynamicProxy = new DynamicProxy<IRepository<T>>(repository) { Filter = m => !m.Name.StartsWith("Get") }; return dynamicProxy.GetTransparentProxy() as IRepository<T>; } }
}
运行后的代码如下图:

Get 开头的方法就没记录日志。
此外,如果你不想更改 业务类,你更改AOP 类,代码如下:
class DynamicProxy<T> : RealProxy
{ private readonly T _decorated; private Predicate<MethodInfo> _filter; public event EventHandler<IMethodCallMessage> BeforeExecute; public event EventHandler<IMethodCallMessage> AfterExecute; public event EventHandler<IMethodCallMessage> ErrorExecuting; public DynamicProxy(T decorated) : base(typeof(T)) { _decorated = decorated; Filter = m => true; } public Predicate<MethodInfo> Filter { get { return _filter; } set { if (value == null) _filter = m => true; else _filter = value; } } private void OnBeforeExecute(IMethodCallMessage methodCall) { if (BeforeExecute != null) { var methodInfo = methodCall.MethodBase as MethodInfo; if (_filter(methodInfo)) BeforeExecute(this, methodCall); } } private void OnAfterExecute(IMethodCallMessage methodCall) { if (AfterExecute != null) { var methodInfo = methodCall.MethodBase as MethodInfo; if (_filter(methodInfo)) AfterExecute(this, methodCall); } } private void OnErrorExecuting(IMethodCallMessage methodCall) { if (ErrorExecuting != null) { var methodInfo = methodCall.MethodBase as MethodInfo; if (_filter(methodInfo)) ErrorExecuting(this, methodCall); } } public override IMessage Invoke(IMessage msg) { var methodCall = msg as IMethodCallMessage; var methodInfo = methodCall.MethodBase as MethodInfo; OnBeforeExecute(methodCall); try { var result = methodInfo.Invoke(_decorated, methodCall.InArgs); OnAfterExecute(methodCall); return new ReturnMessage( result, null, 0, methodCall.LogicalCallContext, methodCall); } catch (Exception e) { OnErrorExecuting(methodCall); return new ReturnMessage(e, methodCall); } }
}
上面定义了3 事件,分别是 BeforeExecute, AfterExecute and ErrorExecuting ,他们分别被调用 OnBeforeExecute, OnAfterExecute and OnErrorExecuting
其他 OnBeforeExecute, OnAfterExecute and OnErrorExecuting 会验证,如果存在事件处理函数,并且进行了方法过滤,他们就会被调用。所以一个设置事件的Repository Factory 定义如下:
public class RepositoryFactory
{ private static void Log(string msg, object arg = null) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(msg, arg); Console.ResetColor(); } public static IRepository<T> Create<T>() { var repository = new Repository<T>(); var dynamicProxy = new DynamicProxy<IRepository<T>>(repository); dynamicProxy.BeforeExecute += (s, e) => Log( "Before executing '{0}'", e.MethodName); dynamicProxy.AfterExecute += (s, e) => Log( "After executing '{0}'", e.MethodName); dynamicProxy.ErrorExecuting += (s, e) => Log( "Error executing '{0}'", e.MethodName); dynamicProxy.Filter = m => !m.Name.StartsWith("Get"); return dynamicProxy.GetTransparentProxy() as IRepository<T>; }
}
到此,你现在可以选择在程序执行之前,执行之后,或者发生错误时,是否应用到AOP。
不是替代工具
使用AOP可以增加Code到应用贷程序的所有层,且没有重复的代码。我展示的例子至少通过一个基于装饰者模式的普通代理类应用一个拥有事件和表达式过滤器的AOP到你的类。
如您所见,RealProxy类是一个灵活的类,你可以完全控制代码,没有外部依赖。然而,请注意,RealProxy不可能替代其他AOP工具,比如PostSharp。PostSharp使用一个完全不同的方法。将中间语言(IL)代码post-compilation一步,而不是使用反射,所以它比RealProxy应该有更好的性能。相对于PostSharp, 你还必须做更多的工作来实现一个基于RealProxy。但是 PostSharp,您只需要创建方面类和一个属性添加到类(或方法),并且这就是所有。
另一方面,RealProxy,你可以l完全控制你的源代码,没有外部依赖项,您可以扩展和定制你想要的。例如,如果您想应用只在一个方面有日志属性的方法,你可以这样做:
public override IMessage Invoke(IMessage msg)
{var methodCall = msg as IMethodCallMessage;
var methodInfo = methodCall.MethodBase as MethodInfo;
if (!methodInfo.CustomAttributes
.Any(a => a.AttributeType == typeof (LogAttribute)))
{
var result = methodInfo.Invoke(_decorated, methodCall.InArgs);
return new ReturnMessage(result, null, 0,
methodCall.LogicalCallContext, methodCall);
}
...
除此之外,使用的技术RealProxy(拦截代码,允许程序来取代它)是强大的。例如,如果您想创建一个模拟框架,用于创建通用的模拟和测试的子类,您可以使用RealProxy类拦截所有调用,并将其替换为你自己的行为,但这另一篇文章的主题!
tks! 到此AOP 就告一段落了。
动态代理AOP实现方法过滤的更多相关文章
- 浅析DispatchProxy动态代理AOP
浅析DispatchProxy动态代理AOP(代码源码) 最近学习了一段时间Java,了解到Java实现动态代理AOP主要分为两种方式JDK.CGLIB,我之前使用NET实现AOP切面编程,会用Fil ...
- 动态代理案例1:运用Proxy动态代理来增强方法
动态代理案例1: /*要求:运用Proxy动态代理来增强方法 题目: 1.定义接口Fruit,其中有addFruit方法 2.定义实现类FruitImpl,实现Fruit接口 3.定 ...
- spring aop 动态代理批量调用方法实例
今天项目经理发下任务,需要测试 20 个接口,看看推送和接收数据是否正常.因为对接传输的数据是 xml 格式的字符串,所以我拿现成的数据,先生成推送过去的数据并存储到文本,以便验证数据是否正确,这时候 ...
- Java动态代理的实现方法
AOP的拦截功能是由java中的动态代理来实现的.说白了,就是在目标类的基础上增加切面逻辑,生成增强的目标类(该切面逻辑或者在目标类函数执行之前,或者目标类函数执行之后,或者在目标类函数抛出异常时候执 ...
- 动态代理 aop切面实现事务管理
1.定义接口和实现 public interface UserService { public String getName(int id); public Integer getAge(int id ...
- java动态代理——字段和方法字节码的基础结构及Proxy源码分析三
前文地址:https://www.cnblogs.com/tera/p/13280547.html 本系列文章主要是博主在学习spring aop的过程中了解到其使用了java动态代理,本着究根问底的 ...
- 5.动态代理AOP实现-DynamicProxy模式
通过动态代理模式Interceptor实现在RegUser()方法本身业务前后加上一些自己的功能,如:PreProceed和PostProceed,即不修改UserProcessor类又能增加新功能 ...
- spring中使用动态代理(AOP)
spring是整合了BGLIB和JDK两种动态代理 示例:使用CGLIB代理 public class MyCar { private String color = "blue"; ...
- Spring-Boot的动态代理AOP原理
前言 Spring AOP使用了动态代理技术,动态代理在业界比较流行的实现方式有,CGLIB,Javassist,ASM等等. Spring动态代理实现方式 Spring采用了JDK和CGLIB两种方 ...
随机推荐
- activity启动模式之singleTask
activity启动模式之singleTask 一.简介 如果另外一个应用调用了C2,C2在栈底,如果这个程序里面再嗲用C1,C3,C2,那么这个C2就是调用onNewIntant的,C1和C3都被销 ...
- 22个HTML5的初级技巧
Web技术的发展速度太快了,如果你不与时俱进,就会被淘汰.因此,为了应对即将到来的HTML5,本文总结了22个HTML5的初级技巧,希望能对你进一步学习好HTML5会有所帮助. 1. 新的Doctyp ...
- web自动化常用定位和方法总结
一. driver常用方法 二. 常用定位 三. 元素在页面不可见区域 四. iframe的操作 五. 页面弹出框:加等待时间 六. windows弹出框 七. 鼠标操作 八. 下拉列表 注意:下图中 ...
- 51nod 1287 线段树
http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1287 简单的线段树题目,直接写个二分查找大于等于x的最小位置就好了. # ...
- Java 文件上传中转
org.apache.commons.httpclient.methods.multipart Class MultipartRequestEntity java.lang.Object org.ap ...
- IOS-SQLite数据库使用详解
使用SQLite数据库 创建数据库 创建数据库过程需要3个步骤: 1.使用sqlite3_open函数打开数据库: 2.使用sqlite3_exec函数执行Create Table语句,创建数据库表: ...
- opencv:访问像素
a.使用指针 #include <opencv.hpp> using namespace cv; using namespace std; int main() { //指针访问每个像素并 ...
- jQuery 图片自动播放
var imgArray = new Array("images/1.jpg", "images/2.jpg","images/3.jpg" ...
- 010-对象——构造方法__construct析构方法__destruct使用方法 PHP重写与重载
<?php /*抽象方法和抽象类 抽象类必须通过子类继承之后,才能实现实例化. 类中有抽象方法,必须声明抽象类. 如果是抽象类,可以没有抽象方法,但必须通过子类去继承之后,实现实例化 final ...
- linux简单介绍,helloworld,vi使用,用户管理
linux特点1.免费的.开源的2.支持多线程.多用户的3.安全性好4.对内存和文件管理优越 缺点:操作相对困难 linux最小只需要4m -> 嵌入式开发 我们使用 vm[虚拟机] 虚拟了一个 ...