Autofac的切面编程实现

面向切面编程:Autofac.Annotation扩展组件是我开源的一款利用打标签完成autofac容器的注入组件。

https://github.com/yuzd/Autofac.Annotation

我们之前介绍了利用Aspect标签来完成拦截器功能

Aspect是一对一的方式,我想要某个class开启拦截器功能我需要针对每个class去配置。 详情请点击

比如说 我有2个 controller 每个controller都有2个action方法,


    [Component]
    public class ProductController
    {
        public virtual string GetProduct(string productId)
        {
            return "GetProduct:" + productId;
        }
        
        public virtual string UpdateProduct(string productId)
        {
            return "UpdateProduct:" + productId;
        }
    }
    
    [Component]
    public class UserController
    {
        public virtual string GetUser(string userId)
        {
            return "GetUser:" + userId;
        }
        
        public virtual string DeleteUser(string userId)
        {
            return "DeleteUser:" + userId;
        }
    }

如果我需要这2个controller的action方法都在执行方法前打log 在方法执行后打log 按照上一节Aspect的话 我需要每个controller都要配置。如果我有100个controller的画我就需要配置100次,这样我觉得太麻烦了。所以我参考了Spring的Pointcut切面编程的方式实现了一套类似的,下面看如何用Pointcut的方式方便的配置一种切面去适用于N个对象。

定义一个切面:创建一个class 上面打上Pointcut的标签 如下:

Pointcut标签类有如下属性:

属性名说明
Name名称Pointcut切面的名称(默认为空,和拦截方法进行匹配,参考下面说明)
RetType匹配目标类的方法的返回类型(默认是%)
NameSpace匹配目标类的namespace(默认是%)
ClassName匹配目标类的类名称(必填)
MethodName匹配目标类的方法名称(默认是%)

匹配算法

 举例:

匹配结果匹配模板要匹配的字符串
匹配结果:true"%"""
匹配结果:true"%"" "
匹配结果:true"%""asdfa asdf asdf"
匹配结果:true"%""%"
匹配结果:false"_"""
匹配结果:true"_"" "
匹配结果:true"_""4"
匹配结果:true"_""C"
匹配结果:false"_""CX"
匹配结果:false"[ABCD]"""
匹配结果:true"[ABCD]""A"
匹配结果:true"[ABCD]""b"
匹配结果:false"[ABCD]""X"
匹配结果:false"[ABCD]""AB"
匹配结果:true"[B-D]""C"
匹配结果:true"[B-D]""D"
匹配结果:false"[B-D]""A"
匹配结果:false"[^B-D]""C"
匹配结果:false"[^B-D]""D"
匹配结果:true"[^B-D]""A"
匹配结果:true"%TEST[ABCD]XXX""lolTESTBXXX"
匹配结果:false"%TEST[ABCD]XXX""lolTESTZXXX"
匹配结果:false"%TEST[^ABCD]XXX""lolTESTBXXX"
匹配结果:true"%TEST[^ABCD]XXX""lolTESTZXXX"
匹配结果:true"%TEST[B-D]XXX""lolTESTBXXX"
匹配结果:true"%TEST[^B-D]XXX""lolTESTZXXX"
匹配结果:true"%Stuff.txt""Stuff.txt"
匹配结果:true"%Stuff.txt""MagicStuff.txt"
匹配结果:false"%Stuff.txt""MagicStuff.txt.img"
匹配结果:false"%Stuff.txt""Stuff.txt.img"
匹配结果:false"%Stuff.txt""MagicStuff001.txt.img"
匹配结果:true"Stuff.txt%""Stuff.txt"
匹配结果:false"Stuff.txt%""MagicStuff.txt"
匹配结果:false"Stuff.txt%""MagicStuff.txt.img"
匹配结果:true"Stuff.txt%""Stuff.txt.img"
匹配结果:false"Stuff.txt%""MagicStuff001.txt.img"
匹配结果:true"%Stuff.txt%""Stuff.txt"
匹配结果:true"%Stuff.txt%""MagicStuff.txt"
匹配结果:true"%Stuff.txt%""MagicStuff.txt.img"
匹配结果:true"%Stuff.txt%""Stuff.txt.img"
匹配结果:false"%Stuff.txt%""MagicStuff001.txt.img"
匹配结果:true"%Stuff%.txt""Stuff.txt"
匹配结果:true"%Stuff%.txt""MagicStuff.txt"
匹配结果:false"%Stuff%.txt""MagicStuff.txt.img"
匹配结果:false"%Stuff%.txt""Stuff.txt.img"
匹配结果:false"%Stuff%.txt""MagicStuff001.txt.img"
匹配结果:true"%Stuff%.txt""MagicStuff001.txt"
匹配结果:true"Stuff%.txt%""Stuff.txt"
匹配结果:false"Stuff%.txt%""MagicStuff.txt"
匹配结果:false"Stuff%.txt%""MagicStuff.txt.img"
匹配结果:true"Stuff%.txt%""Stuff.txt.img"
匹配结果:false"Stuff%.txt%""MagicStuff001.txt.img"
匹配结果:false"Stuff%.txt%""MagicStuff001.txt"
匹配结果:true"%Stuff%.txt%""Stuff.txt"
匹配结果:true"%Stuff%.txt%""MagicStuff.txt"
匹配结果:true"%Stuff%.txt%""MagicStuff.txt.img"
匹配结果:true"%Stuff%.txt%""Stuff.txt.img"
匹配结果:true"%Stuff%.txt%""MagicStuff001.txt.img"
匹配结果:true"%Stuff%.txt%""MagicStuff001.txt"
匹配结果:true"?Stuff?.txt?""1Stuff3.txt4"
匹配结果:false"?Stuff?.txt?""1Stuff.txt4"
匹配结果:false"?Stuff?.txt?""1Stuff3.txt"
匹配结果:false"?Stuff?.txt?""Stuff3.txt4"

    // *Controller 代表匹配 只要是Controller结尾的类都能匹配
    // Get* 代表上面匹配成功的类下 所以是Get打头的方法都能匹配
    [Pointcut(ClassName = "*Controller",MethodName = "Get*")]
    public class LoggerPointCut
    {
        
    }

定义好了一个Pointcut切面后 需要定义这个切面的拦截方法

配合Pointcut切面标签,可以在打了这个标签的class下定义拦截方法, 在方法上得打上特定的标签,有如下几种:

切面执行方法上打标签种类说明
Before标签在匹配成功的类的方法执行前执行
After标签在匹配成功的类的方法执行后执行
Around标签承接了 匹配成功的类的方法的执行权(如果一个切面配置了Around又配置了Before或者After,那么会只执行Around)

以上3种标签有一个可选的参数:Name (默认为空,可以和Pointcut的Name进行mapping)

  • 因为一个class上可以打多个Pointcut切面,一个Pointcut切面可以根据name去匹配对应拦截方法

    // *Controller 代表匹配 只要是Controller结尾的类都能匹配
    // Get* 代表上面匹配成功的类下 所以是Get打头的方法都能匹配
    [Pointcut(ClassName = "*Controller",MethodName = "Get*")]
    public class LoggerPointCut
    {
        /// <summary>
        /// 打上Before标签 代表满足匹配的方法 在执行之前会执行下面的Before()方法
        /// </summary>
        [Before]
        public void Befor()
        {
            Console.WriteLine("before");
        }

        /// <summary>
        /// 打上After标签 代表满足匹配的方法 在执行之前会执行下面的After()方法
        /// </summary>
        [After]
        public void After()
        {
            Console.WriteLine("after");
        }
    }

如果是用Around环绕的话


    // *Controller 代表匹配 只要是Controller结尾的类都能匹配
    // Get* 代表上面匹配成功的类下 所以是Get打头的方法都能匹配
    [Pointcut(ClassName = "*Controller",MethodName = "Get*")]
    public class LoggerPointCut
    {
        
        /// <summary>
        /// 打上Around标签 承接了 匹配成功的类的方法的执行权
        /// </summary>
        /// <param name="context"></param>
        [Around]
        public void Around(AspectContext context)
        {
            //执行原目标方法前
            Console.WriteLine(context.InvocationContext.MethodInvocationTarget.Name + "-->Start");
            //执行原目标方法
            context.InvocationProceedInfo.Invoke();
            //执行原目标方法后
            Console.WriteLine(context.InvocationContext.MethodInvocationTarget.Name + "-->End");
        }
    }
执行方法的参数说明:

执行方法的参数可以是DI容器的类型,会在执行时自动注入进来,可以使用Autowired,Value等标签来修饰参数。

还有一个特殊的执行参数可以注入,那就是AspectContext,这个类型里面你可以获取到被拦截的方法的信息,以及执行原方法的委托。

注意:这个执行后 有2种

  • 正常执行成功

  • 有异常,若方法参数注入了AspectContext 那么可以通过Exception属性值获得异常内容

按照上面的配置

  • ProductController.GetProduct 会被匹配

  • UserController.GetUser 会被匹配

在执行上面这2个方法的时候会

  • 先执行 LoggerPointCut.Before方法

  • 再执行 LoggerPointCut.After方法

利用Autofac的这个开源扩展组件很方便的实现切面编程,总结切面编程三步骤

  • 1.定义一个切面,编写要匹配的目标类的方法(采用sql的like方式匹配),所以一个切面可以拦截n个目标

  • 2.定义对应的拦截方法

  • 3.执行被匹配的方法时会执行对应的拦截方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值