这里顺便分享微软官方如果利用RealProxy类实现AOP的,详见地址:https://msdn.microsoft.com/zh-cn/library/dn574804.aspx
第四种:反射+ 通过定义统一的出入口,并运用一些特性实现AOP的效果,比如:常见的MVC、WEB API中的过滤器特性 ,我这里根据MVC的思路,实现了类似的MVC过滤器的AOP效果,只是中间用到了反射,可能性能不佳,但效果还是成功实现了各种拦截,正如MVC一样,既支持过滤器特性,也支持Controller中的Action执行前,执行后,错误等方法实现拦截
实现思路如下:
A.过滤器及Controller特定方法拦截实现原理:
1.获取程序集中所有继承自Controller的类型;
2.根据Controller的名称找到第1步中的对应的Controller的类型:FindControllerType
3.根据找到的Controller类型及Action的名称找到对应的方法:FindAction
4.创建Controller类型的实例;
5.根据Action方法找到定义在方法上的所有过滤器特性(包含:执行前、执行后、错误)
6.执行Controller中的OnActionExecuting方法,随后执行执行前的过滤器特性列表,如:ActionExecutingFilter
7.执行Action方法,获得结果;
8.执行Controller中的OnActionExecuted方法,随后执行执行后的过滤器特性列表,如:ActionExecutedFilter
9.通过try catch在catch中执行Controller中的OnActionError方法,随后执行错误过滤器特性列表,如:ActionErrorFilter
10.最后返回结果;
B.实现执行路由配置效果原理:
1.增加可设置路由模板列表方法:AddExecRouteTemplate,在方法中验证controller、action,并获取模板中的占位符数组,最后保存到类全局对象中routeTemplates;
2.增加根据执行路由执行对应的Controller中的Action方法的效果: Run,在该方法中主要遍历所有路由模板,然后与实行执行的请求路由信息通过正则匹配,若匹配OK,并能正确找到Controller及Action,则说明正确,并最终统一调用:Process方法,执行A中的所有步骤最终返回结果。
需要说明该模拟MVC方案并没有实现Action方法参数的的绑定功能,因为ModelBinding本身就是比较复杂的机制,所以这里只是为了搞清楚AOP的实现原理,故不作这方面的研究,大家如果有空可以实现,最终实现MVC不仅是ASP.NET MVC,还可以是 Console MVC,甚至是Winform MVC等。
以下是实现的全部代码,代码中我已进行了一些基本的优化,可以直接使用:
public abstract class Controller { public virtual void OnActionExecuting(MethodInfo action) { } public virtual void OnActionExecuted(MethodInfo action) { } public virtual void OnActionError(MethodInfo action, Exception ex) { } } public abstract class FilterAttribute : Attribute { public abstract string FilterType { get; } public abstract void Execute(Controller ctrller, object extData); } public class ActionExecutingFilter : FilterAttribute { public override string FilterType => "BEFORE"; public override void Execute(Controller ctrller, object extData) { Console.WriteLine($"我是在{ctrller.GetType().Name}.ActionExecutingFilter中拦截发出的消息!-{DateTime.Now.ToString()}"); } } public class ActionExecutedFilter : FilterAttribute { public override string FilterType => "AFTER"; public override void Execute(Controller ctrller, object extData) { Console.WriteLine($"我是在{ctrller.GetType().Name}.ActionExecutedFilter中拦截发出的消息!-{DateTime.Now.ToString()}"); } } public class ActionErrorFilter : FilterAttribute { public override string FilterType => "EXCEPTION"; public override void Execute(Controller ctrller, object extData) { Console.WriteLine($"我是在{ctrller.GetType().Name}.ActionErrorFilter中拦截发出的消息!-{DateTime.Now.ToString()}-Error Msg:{(extData as Exception).Message}"); } } public class AppContext { private static readonly Type ControllerType = typeof(Controller); private static readonly Dictionary<string, Type> matchedControllerTypes = new Dictionary<string, Type>(); private static readonly Dictionary<string, MethodInfo> matchedControllerActions = new Dictionary<string, MethodInfo>(); private Dictionary<string,string[]> routeTemplates = new Dictionary<string, string[]>(); public void AddExecRouteTemplate(string execRouteTemplate) { if (!Regex.IsMatch(execRouteTemplate, "{controller}", RegexOptions.IgnoreCase)) { throw new ArgumentException("执行路由模板不正确,缺少{controller}"); } if (!Regex.IsMatch(execRouteTemplate, "{action}", RegexOptions.IgnoreCase)) { throw new ArgumentException("执行路由模板不正确,缺少{action}"); } string[] keys = Regex.Matches(execRouteTemplate, @"(?<={)\w+(?=})", RegexOptions.IgnoreCase).Cast<Match>().Select(c => c.Value.ToLower()).ToArray(); routeTemplates.Add(execRouteTemplate,keys); } public object Run(string execRoute) { //{controller}/{action}/{id} string ctrller = null; string actionName = null; ArrayList args = null; Type controllerType = null; bool findResult = false; foreach (var r in routeTemplates) { string[] keys = r.Value; string execRoutePattern = Regex.Replace(r.Key, @"{(?<key>\w+)}", (m) => string.Format(@"(?<{0}>.[^/\\]+)", m.Groups["key"].Value.ToLower()), RegexOptions.IgnoreCase); args = new ArrayList(); if (Regex.IsMatch(execRoute, execRoutePattern)) { var match = Regex.Match(execRoute, execRoutePattern); for (int i = 0; i < keys.Length; i++) { if ("controller".Equals(keys[i], StringComparison.OrdinalIgnoreCase)) { ctrller = match.Groups["controller"].Value; } else if ("action".Equals(keys[i], StringComparison.OrdinalIgnoreCase)) { actionName = match.Groups["action"].Value; } else { args.Add(match.Groups[keys[i]].Value); } } if ((controllerType = FindControllerType(ctrller)) != null && FindAction(controllerType, actionName, args.ToArray()) != null) { findResult = true; break; } } } if (findResult) { return Process(ctrller, actionName, args.ToArray()); } else { throw new Exception($"在已配置的路由模板列表中未找到与该执行路由相匹配的路由信息:{execRoute}"); } } public object Process(string ctrller, string actionName, params object[] args) { Type matchedControllerType = FindControllerType(ctrller); if (matchedControllerType == null) { throw new ArgumentException($"未找到类型为{ctrller}的Controller类型"); } object execResult = null; if (matchedControllerType != null) { var matchedController = (Controller)Activator.CreateInstance(matchedControllerType); MethodInfo action = FindAction(matchedControllerType, actionName, args); if (action == null) { throw new ArgumentException($"在{matchedControllerType.FullName}中未找到与方法名:{actionName}及参数个数:{args.Count()}相匹配的方法"); } var filters = action.GetCustomAttributes<FilterAttribute>(true); List<FilterAttribute> execBeforeFilters = new List<FilterAttribute>(); List<FilterAttribute> execAfterFilters = new List<FilterAttribute>(); List<FilterAttribute> exceptionFilters = new List<FilterAttribute>(); if (filters != null && filters.Count() > 0) { execBeforeFilters = filters.Where(f => f.FilterType == "BEFORE").ToList(); execAfterFilters = filters.Where(f => f.FilterType == "AFTER").ToList(); exceptionFilters = filters.Where(f => f.FilterType == "EXCEPTION").ToList(); } try { matchedController.OnActionExecuting(action); if (execBeforeFilters != null && execBeforeFilters.Count > 0) { execBeforeFilters.ForEach(f => f.Execute(matchedController, null)); } var mParams = action.GetParameters(); object[] newArgs = new object[args.Length]; for (int i = 0; i < mParams.Length; i++) { newArgs[i] = Convert.ChangeType(args[i], mParams[i].ParameterType); } execResult = action.Invoke(matchedController, newArgs); matchedController.OnActionExecuted(action); if (execBeforeFilters != null && execBeforeFilters.Count > 0) { execAfterFilters.ForEach(f => f.Execute(matchedController, null)); } } catch (Exception ex) { matchedController.OnActionError(action, ex); if (exceptionFilters != null && exceptionFilters.Count > 0) { exceptionFilters.ForEach(f => f.Execute(matchedController, ex)); } } } return execResult; } private Type FindControllerType(string ctrller) { Type matchedControllerType = null; if (!matchedControllerTypes.ContainsKey(ctrller)) { var assy = Assembly.GetAssembly(typeof(Controller)); foreach (var m in assy.GetModules(false)) { foreach (var t in m.GetTypes()) { if (ControllerType.IsAssignableFrom(t) && !t.IsAbstract) { if (t.Name.Equals(ctrller, StringComparison.OrdinalIgnoreCase) || t.Name.Equals($"{ctrller}Controller", StringComparison.OrdinalIgnoreCase)) { matchedControllerType = t; matchedControllerTypes[ctrller] = matchedControllerType; break; } } } } } else { matchedControllerType = matchedControllerTypes[ctrller]; } return matchedControllerType; } private MethodInfo FindAction(Type matchedControllerType, string actionName, object[] args) { string ctrlerWithActionKey = $"{matchedControllerType.FullName}.{actionName}"; MethodInfo action = null; if (!matchedControllerActions.ContainsKey(ctrlerWithActionKey)) { if (args == null) args = new object[0]; foreach (var m in matchedControllerType.GetMethods(BindingFlags.Instance | BindingFlags.Public)) { if (m.Name.Equals(actionName, StringComparison.OrdinalIgnoreCase) && m.GetParameters().Length == args.Length) { action = m; matchedControllerActions[ctrlerWithActionKey] = action; break; } } } else { action = matchedControllerActions[ctrlerWithActionKey]; } return action; } } |
使用前,先定义一个继承自Controller的类,如:TestController,并重写相应的方法,或在指定的方法上加上所需的过滤器特性,如下代码所示:
public class TestController : Controller { public override void OnActionExecuting(MethodInfo action) { Console.WriteLine($"{action.Name}执行前,OnActionExecuting---{DateTime.Now.ToString()}"); } public override void OnActionExecuted(MethodInfo action) { Console.WriteLine($"{action.Name}执行后,OnActionExecuted--{DateTime.Now.ToString()}"); } public override void OnActionError(MethodInfo action, Exception ex) { Console.WriteLine($"{action.Name}执行,OnActionError--{DateTime.Now.ToString()}:{ex.Message}"); } [ActionExecutingFilter] [ActionExecutedFilter] public string HelloWorld(string name) { return ($"Hello World!->{name}"); } [ActionExecutingFilter] [ActionExecutedFilter] [ActionErrorFilter] public string TestError(string name) { throw new Exception("这是测试抛出的错误信息!"); } [ActionExecutingFilter] [ActionExecutedFilter] public int Add(int a, int b) { return a + b; } } |
最后前端实际调用就非常简单了,代码如下:
class MVCProgram { static void Main(string[] args) { try { var appContext = new AppContext(); object rs = appContext.Process("Test", "HelloWorld", "梦在旅途"); Console.WriteLine($"Process执行的结果1:{rs}"); Console.WriteLine("=".PadRight(50, '=')); appContext.AddExecRouteTemplate("{controller}/{action}/{name}"); appContext.AddExecRouteTemplate("{action}/{controller}/{name}"); object result1 = appContext.Run("HelloWorld/Test/梦在旅途-zuowenjun.cn"); Console.WriteLine($"执行的结果1:{result1}"); Console.WriteLine("=".PadRight(50, '=')); object result2 = appContext.Run("Test/HelloWorld/梦在旅途-zuowenjun.cn"); Console.WriteLine($"执行的结果2:{result2}"); Console.WriteLine("=".PadRight(50, '=')); appContext.AddExecRouteTemplate("{action}/{controller}/{a}/{b}"); object result3 = appContext.Run("Add/Test/500/20"); Console.WriteLine($"执行的结果3:{result3}"); object result4 = appContext.Run("Test/TestError/梦在旅途-zuowenjun.cn"); Console.WriteLine($"执行的结果4:{result4}"); } catch (Exception ex) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine($"发生错误:{ex.Message}"); Console.ResetColor(); } Console.ReadKey(); } } |
可以看到,与ASP.NET MVC有点类似,只是ASP.NET MVC是通过URL访问,而这里是通过AppContext.Run 执行路由URL 或Process方法,直接指定Controller、Action、参数来执行。
通过以上调用代码可以看出路由配置还是比较灵活的,当然参数配置除外。如果大家有更好的想法也可以在下方评论交流,谢谢!
MVC代码执行效果如下: