1.什么是AOP
AOP(Aspect Oriented Programming)的字面意思是“面向切面编程”。举个例子解释一下,如果我们把三层架构的表现层,业务逻辑层和数据访问层看作是河流的上游,中游和下游,那么“面向切面编程”就是架设在上游和中游分界处的三峡大坝,他对每一滴河水作一个公共的操作,比如染成红色或者过滤掉大鱼。
AOP的意义在于能够让我们在不影响原有功能的前提下,为软件横向扩展功能,说穿了就是解耦。
实现的方式往往是预编译或者动态代理。本篇讲述的是动态代理实现AOP,需要用到动态代理的框架代码,请结合《C#之动态代理的实现》食用。
2.如何实现AOP
讲到这里,再回忆一下《C#之动态代理的实现》中最后的业务代码。其中有这么一段代码:
/// <summary>
/// 具体的某个拦截器
/// </summary>
public class Interceptor : IInterceptor
{
public object Intercept(Invocation invocation)
{
Console.WriteLine("煮熟");
return invocation.Proceed();
}
}
public class Food
{
[Rewrite]
public virtual string Eat(int p1, int p2)
{
return "吃";
}
}
public class Test
{
public void Run()
{
Food f = new ProxyFactory<Interceptor>().CreateInstance<Food>();
}
}
不难发现,这段代码就已经是最简陋的AOP了。观察“煮熟”和“吃”这两个业务:“吃”作为正常的流程,“煮熟”作为插入的业务,只要Rewrite标记过的方法都会先去“煮熟”,这两个业务之间是解耦的,不存在任何调用关系,符合AOP的思想。
当然,如果不是“煮熟”,而是“红烧”呢?或者“吃”之后需要“洗碗”呢?那我们是不是就需要创建很多个Interceptor?就算创建了很多个Interceptor,怎么解决一个Food类中,有的方法需要“煮熟”,有的方法需要“红烧”呢?
这里我们就需要重新设计一下代码,把“煮熟”这个业务下放到Attribute中。
框架代码如下:
/// <summary>
/// 前切拦截器(基于动态代理的AOP框架,性能快于.netRemoting的AOP,慢于静态织入的AOP)
/// 说明:
/// 1.返回false则跳出前切拦截器链,并不执行被拦截的方法
/// 使用方法:
/// 1.自定义一个标签继承AOPBeforeAttribute
/// 2.给virtual或override方法打上自定义的标签
/// 3.使用ioc动态代理这个类,实现前切
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public abstract class AOPBeforeAttribute : RewriteAttribute
{
public virtual AOPBeforeAttribute Fill(Invocation invocation)
{
return this;
}
public abstract bool BeforeExecute();
}
/// <summary>
/// 后切拦截器
/// 说明:
/// 1.ifexecute指示了被拦截的方法是否执行,返回false则跳出后切拦截器链
/// 2.其他同AOPBeforeAttribute
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public abstract class AOPAfterAttribute : RewriteAttribute
{
public virtual AOPAfterAttribute Fill(Invocation invocation)
{
return this;
}
public abstract bool AfterExecute(bool ifexecute, bool ifsucceed);
}
/// <summary>
/// AOP的拦截器
/// </summary>
public class Interceptor : IInterceptor
{
public object Intercept(Invocation invocation)
{
object re = null;
var attributeList = invocation.DelegateMethod.Method.GetCustomAttributes(true);
int length = attributeList.Count();
bool ifexecute = true;
bool ifsucceed = false;
//前切,若返回false则跳出前切链,跳过执行直接进入后切
for (int i = length - 1; i >= 0; i--)
{
AOPBeforeAttribute a = attributeList.ElementAt(i) as AOPBeforeAttribute;
if (a != null)
{
a = a.Fill(invocation);
if (!a.BeforeExecute())
{
ifexecute = false;
break;
}
}
}
try
{
if (ifexecute)
{
re = invocation.Proceed();
ifsucceed = true;
}
}
finally
{
//后切,若返回false则跳出后切链(注意,即使被后切的程序报错也执行)
for (int i = length - 1; i >= 0; i--)
{
AOPAfterAttribute a = attributeList.ElementAt(i) as AOPAfterAttribute;
if (a != null)
{
a = a.Fill(invocation);
if (!a.AfterExecute(ifexecute, ifsucceed))
{
break;
}
}
}
}
if (!ifexecute)
{
//如果action未执行,给出默认值而非null值
Type type = invocation.DelegateMethod.Method.ReturnType;
re= Activator.CreateInstance(type);
}
return re;
}
}
业务代码如下:
public class PoachAttribute : AOPBeforeAttribute
{
public override bool BeforeExecute()
{
Console.WriteLine("煮熟");
return true;
}
}
public class WashAttribute : AOPAfterAttribute
{
public override bool AfterExecute(bool ifexecute, bool ifsucceed)
{
Console.WriteLine("洗碗");
return true;
}
}
public class Food
{
[Poach]
[Wash]
public virtual string Eat(int p1, int p2)
{
Console.WriteLine("吃");
return "";
}
}
public class Test
{
public void Run()
{
Food f = new ProxyFactory<Interceptor>().CreateInstance<Food>();
f.Eat(1, 2);
}
}
代码解释:
上述框架代码中,我们要把切入点上可能要做的事情抽象了一下。分为:
1.before,在切入点前面插入切片代码;
2.after,在切入点后面插入切片代码;
通过Interceptor对所有的before标签和after标签做链式的调用。如此一来,“煮熟”,“吃”,“洗碗”就能依次执行,互不干扰。
3.AOP在WebApi中的应用
public class AOPHttpControllerActivator : IHttpControllerActivator
{
private DefaultHttpControllerActivator defaultActivator;
public AOPHttpControllerActivator()
{
this.defaultActivator = new DefaultHttpControllerActivator();
}
public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
Type proxyType = new ProxyFactory<Interceptor>().CreateInstance(controllerType).GetType();
return this.defaultActivator.Create(request, controllerDescriptor, proxyType);
}
}
public class AOPforApi
{
public static void Register()
{
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new AOPHttpControllerActivator());
}
}
Global.asax中调用AOPforApi.Register
public class Global : HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
// 在应用程序启动时运行的代码
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
AOPforApi.Register();
}
}
业务代码
public class PoachAttribute : AOPBeforeAttribute
{
public override bool BeforeExecute()
{
Console.WriteLine("煮熟");
return true;
}
}
public class TestController : ApiController
{
[HttpGet]
[Poach]
public virtual IHttpActionResult Test()
{
return Json(new
{
code = 0,
msg = "success"
});
}
}
4.AOP在MVC中的应用
/// <summary>
/// 使用ProxyFactory重写DefaultControllerFactory
/// </summary>
public class AOPControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
return base.GetControllerInstance(requestContext, controllerType);
}
else
{
return (IController)new ProxyFactory<Interceptor>().CreateInstance(controllerType);
}
}
}
/// <summary>
/// MVC框架中的AOP注册类
/// 实现MVC中Controller的AOP编程
/// 使用方法:
/// 1.在Global中调用即可Register(必须保证AOPforMVC使用的MVC版本和项目一致)
/// </summary>
public class AOPforMVC
{
public static void Register()
{
ControllerBuilder.Current.SetControllerFactory(new AOPControllerFactory());
}
}
Global.asax中调用AOPforMVC.Register
public class Global : HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
// 在应用程序启动时运行的代码
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
AOPforMVC.Register();
}
}
业务代码
public class PoachAttribute : AOPBeforeAttribute
{
public override bool BeforeExecute()
{
Console.WriteLine("煮熟");
return true;
}
}
public class TestController : Controller
{
// GET: Test
[Poach]
public virtual ActionResult Test()
{
return View();
}
}
5.AOP在WebForm中的应用
/// <summary>
/// 使用ProxyFactory重写PageHandlerFactory
/// </summary>
public class AOPPageHandlerFactory : PageHandlerFactory
{
public override IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
{
var page = base.GetHandler(context, requestType, url, pathTranslated);
return new ProxyFactory<Interceptor>().CreateInstance(page.GetType()) as IHttpHandler;
}
}
在webconfig的configuration节点中添加如下,AAAAA请替换成AOPPageHandlerFactory的命名空间,BBBBB请替换成AOPPageHandlerFactory的程序集名称
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<handlers>
<add name="AOP" verb="*" path="*.aspx" type="AAAAA.AOPPageHandlerFactory, BBBBB" />
</handlers>
</system.webServer>
业务代码
public class PoachAttribute : AOPBeforeAttribute
{
public override bool BeforeExecute()
{
Console.WriteLine("煮熟");
return true;
}
}
public partial class WebForm1 : System.Web.UI.Page
{
[Poach]
protected virtual void Page_Load(object sender, EventArgs e)
{
Console.WriteLine("吃");
}
}
6.总结
使用AOP的场景一般是一些通用服务,比如权限检测,日志打印,事务处理等。
使用AOP的优势是
1.框架将应用程序中的商业逻辑同对其提供支持的通用服务进行分离,让程序员有更多的注意力集中在商业逻辑上;
2.商业逻辑和通用服务解耦,便于更新和扩展;
以上就是AOP的全部内容,如有纰漏和建议,欢迎大家留言指出。