• 对Spring.Net的AOP一些思考及应用


    前言
         这几天在配置Spring.NET,配到AOP的时候发现自己现在还是没有理解到Spring AOP的实现,只是认识到了一个思想,以前配的时候,看的是给好的例子用,真正用的时候还是要想一下,所以写个笔记,深刻的认识一下。


    初探AOP
        对AOP的初步理解是在《JAVA给力起飞》那一本书。当时一句话给我一种醍醐灌顶的感觉:AOP(面向切面编程),为什么要叫面向切面编程?把一个类看成一个树轮,在面向横切逻辑,也就是在开发中遇到的一些通用问题,比如性能检测,访问控制,事务管理以及日志记录等等,以前的解决办法是给每一个类里面都加上这些东西,这样就遇到一个问题:类于类之间强烈耦合,要新增修改删除这个功能你要在代码级别修改代码, 我们无法通过抽象父类消除上面说的 重复性代码,因为这些代码依附在业务逻辑中了。AOP独辟蹊径,不通过纵向架构,而是横向的看代码,把这些横向的逻辑代码抽取出来,做成一个独立的模块。
        抽取出来比较容易,但如何将这些独立的模块整合起来,这才是事情的关键,这也是AOP所要解决的问题。

                                                

    AOP的相关术语
         参考



    .NET中模拟代理

    准备工作
    1.
     public class CompanyDao
        {
            public void Save()
            {
                Console.WriteLine("保存数据");
            }
        }

    2.
     public interface ICompanyManager
        {
            string UserName { get; set; }

            void Save();
        }
    3.
        public interface ISecurityManager
        {
            bool IsPass(string userName);
        }

    解释一下场景,这个是一个保存用户的模块,我们希望在保存用户前验证这个用户是否具有相应权限来做操作。我们通常会这样做:

    4.
     public class SimpleCompanyManager : ICompanyManager
        {
            #region 可通过外部注入的属性

            public string UserName { get; set; }

            public CompanyDao Dao { get; set; }

            #endregion

            public void Save()
            {
                //判断权限
                ISecurityManager security = new SecurityManager();
                if (security.IsPass(UserName))
                {
                    //执行业务方法
                    //.......
                    //调用DAO层方法
                    Dao.Save();
                }
                else
                {
                    //执行其它业务方法...
                    Console.WriteLine("您没有该权限");
                }
            }
        }

    这样就出现一个问题 SimpleCompanyManager 这个类依赖了 SecurityManager,发生了业务性的耦合。我们用代理模式来实现看看会发生什么。

    什么是代理模式?是给某一个对象提供一个代理对象,并由代理对象控制对源对象的引用。代理就是一个人或一个机构代表另一个人或者一个机构采取行动。某些情况下,客户不想或者不能够直接引用一个对象,代理对象可以在客户和目标对象直接起到中介的作用。客户端分辨不出代理主题对象与真实主题对象。代理模式可以并不知道真正的被代理对象,而仅仅持有一个被代理对象的接口,这时候代理对象不能够创建被代理对象,被代理对象必须有系统的其他角色代为创建并传入。

    将之前的业务逻辑分割出来。

     public class CompanyManager : ICompanyManager
        {
            #region 可通过外部注入的属性

            public string UserName { get; set; }

            public CompanyDao Dao { get; set; }

            #endregion

            public void Save()
            {
                //执行业务方法
                //.......
                //调用DAO层方法
                Dao.Save();
            }
        }

    然后做一个代理类

    public class CompanyProxyManager : ICompanyManager
        {
            public string UserName { get; set; }

            private ICompanyManager target = new CompanyManager();

            public void Save()
            {
                //判断权限
                ISecurityManager security = new SecurityManager();
                if (security.IsPass(UserName))
                {
                    //调用目标对象Save方法
                    target.Save();
                }
                else
                {
                    Console.WriteLine("您没有该权限");
                }
            }
        }

    CompanyManager类就不必与判断权限的类 SecurityManager耦合,相当于是加了一层,但这样实现起来比较麻烦这样,

    下面我们看看Spring是怎么帮我们处理的

        public class AroundAdvice : IMethodInterceptor
        {
            //权限系统类(可外部注入)
            private ISecurityManager manager = new Service.SecurityManager();

            public object Invoke(IMethodInvocation invocation)
            {
                //拦截Save方法
                if (invocation.Method.Name == "Save")
                {
                    ICompanyManager target = (ICompanyManager)invocation.Target;

                    return manager.IsPass(target.UserName) ? invocation.Proceed() : null;
                }
                else
                {
                    return invocation.Proceed();
                }
            }
        }

    class Program
        {
            static void Main(string[] args)
            {
                ICompanyManager target = new CompanyManager() { Dao = new CompanyDao(), UserName = "admin" };
                
                ProxyFactory factory = new ProxyFactory(target);
                factory.AddAdvice(new AroundAdvice());

                ICompanyManager manager = (ICompanyManager)factory.GetProxy();
                manager.Save();

                Console.ReadLine();
            }
        }
                                
    和JAVA版的Spring如出一辙,在JAVA中,如果使用CGLIB的动态代理,那个代理类实现的是 MethodInterceptor这个接口(JDK的动态代理接口为 InvocationHandler,java.lang.reflect这个包里面)。.NET里的spring只是少了一个I!如此看来语言是相通的,特别对于统一框架而言:D。   ( 其实实际的项目经常用的是@Aspectj注解,支持正则表达式,用起来更加方便

    有关CGLIB:利用CGLIB可以为任何类创建织入横切逻辑对象的代理创建器,CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。代理为控制要访问的目标对象提供了一种途径。当访问对象时,它引入了一个间接的层。JDK自从1.3版本开始,就引入了动态代理,并且经常被用来动态地创建代理。JDK的动态代理用起来非常简单,但它有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的继承的类,该怎么办?现在我们可以使用CGLIB包。 CGLIB是一个强大的高性能的 代码生成 包。它广泛的被许多AOP的 框架 使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)。

    其中Invoke的方法就是讲横切逻辑代码和业务逻辑代码编织到一起了,这里很像我们以前软件体系结构的作业,只是当时没想到这些 :(
    Program里的代码很简单,先实例化出业务逻辑模块,这个就是目标对象,用来组装的,将其托管到 ProxyFactory,然后加入 Advice(通知,感觉叫增强更合适),最后使用 GetProxy获取代理,执行方法。



    AOP的通知类型
        参考

    Spring.Net的自动代理
        如果应用程序需要创建很多 AOP代理,比如当需要代理某个服务层的所有对象时,这种方法就会使配置文件变的相当庞大。为简化配置过程,Spring.NET提供了“ 自动代理”的功能,可以根据条件自动创建 代理对象,也就是说,可以将多个对象分组以作为要代理的候选对象。 自动代理使用起来比较简单和方便.

    1.对象名称切入点
        首先准备增强类
        public class AroundAdvice : IMethodInterceptor
        {
            public object Invoke(IMethodInvocation invocation)
            {
                Console.WriteLine("开始:  " + invocation.TargetType.Name + "." + invocation.Method.Name);
                object result = invocation.Proceed();
                Console.WriteLine("结束:  " + invocation.TargetType.Name + "." + invocation.Method.Name);
                return result;
            }
        }

    配置对增强类的配置
          <object id="aroundAdvisor" type="Spring.Aop.Support.AttributeMatchMethodPointcutAdvisor, Spring.Aop">
            <property name="Advice" ref="aroundAdvice"/>
            <property name="Attribute"
                      value="ConfigAttribute.Attributes.ConsoleDebugAttribute, ConfigAttribute" />
          </object>
        <object id="aroundAdvice" type="Common.AroundAdvice, Common"/>

    准备好要注入的目标类,也就是业务类

    public class AttributeService : IService
        {
            [ConsoleDebug]
            public IList FindAll()
            {
                return new ArrayList();
            }

            public void Save(object entity)
            {
                Console.WriteLine("保存:" + entity);
            }
      }

    <object id="ProxyCreator" type="Spring.Aop.Framework.AutoProxy.ObjectNameAutoProxyCreator, Spring.Aop">
    <property name="ObjectNames">
    <list>
    <value>*Service</value>
    </list>
    </property>

    <property name="InterceptorNames">
    <list>
    <value>aroundAdvice</value>
    </list>
    </property>
    </object>


    <object id="aroundAdvice" type="Common.AroundAdvice, Common"/>

    <object id="categoryService" type="Service.ProductService, Service"/>
    <object id="productService" type="Service.ProductService, Service"/>


    使用ObjectNameAutoProxyCreator经常需要对要拦截的方法进行筛选,这时到Spring.Aop.Support.NameMatchMethodPointcutAdvisor,稍微修改一下配置:

    <object id="ProxyCreator" type="Spring.Aop.Framework.AutoProxy.ObjectNameAutoProxyCreator, Spring.Aop">
    <property name="ObjectNames">
    <list>
    <value>*Service</value>
    </list>
    </property>
    <property name="InterceptorNames">
    <list>
    <value>aroundAdvisor</value>
    </list>
    </property>
    </object>


    <object id="aroundAdvisor" type="Spring.Aop.Support.NameMatchMethodPointcutAdvisor, Spring.Aop">
    <property name="Advice" ref="aroundAdvice"/>
    <property name="MappedNames">
    <list>
    <value>Find*</value>
    </list>
    </property>
    </object>


    < objec t id="aroundAdvice" type="Common.AroundAdvice, Common"/>

    2.正则表达式切入点

    <object id="aroundAdvisor" type="Spring.Aop.Support.RegularExpressionMethodPointcutAdvisor, Spring.Aop">
    <property name="advice" ref="aroundAdvice"/>
    <property name="patterns">
    <list>
    <value>.*Find*.*</value>
    </list>
    </property>
    </object>

    <!-- 必须让Spring.NET容器管理DefaultAdvisorAutoProxyCreator类 -->
    <object id="ProxyCreator" type="Spring.Aop.Framework.AutoProxy.DefaultAdvisorAutoProxyCreator, Spring.Aop"/>

    <object id="aroundAdvice" type="Common.AroundAdvice, Common"/>


    <object id="categoryService" type="Service.ProductService, Service"/>
    <object id="productService" type="Service.ProductService, Service"/>

    以上配置相对复杂一点。使用SdkRegularExpressionMethodPointcut的配置就相对简单的多,而项目中SdkRegularExpressionMethodPointcut也经常用到。
    SdkRegularExpressionMethodPointcut只需要简单的配置一下通知和切入点就完成了。

    <object id="advisor" type="Spring.Aop.Support.SdkRegularExpressionMethodPointcut, Spring.Aop">
    <property name="pattern" value="Service.*"/>
    </object>


    <aop:config>
    <aop:advisor pointcut-ref="advisor" advice-ref="aroundAdvice"/>
    </aop:config>

    <object id="aroundAdvice" type="Common.AroundAdvice, Common"/>

    <object id="categoryService" type="Service.ProductService, Service"/>
    <object id="productService" type="Service.ProductService, Service"/>


    是不是和JAVA的Spring XML配置完全一样?:D

    3.属性切入点

    public   class ConsoleDebugAttribute : Attribute
    {

    }

    public   class AttributeService : IService
    {
    [ConsoleDebug]
    public IList FindAll()
    {
    return   new ArrayList();
    }

    public   void Save(object entity)
    {
    Console.WriteLine("保存:"   + entity);
    }
    }

    JAVA版的是注解,下面是配置文件

    <object id="aroundAdvisor" type="Spring.Aop.Support.AttributeMatchMethodPointcutAdvisor, Spring.Aop">
    <property name="Advice" ref="aroundAdvice"/>
    <property name="Attribute"
    value="ConfigAttribute.Attributes.ConsoleDebugAttribute, ConfigAttribute" />
    </object>

    <object id="proxyFactoryObject" type="Spring.Aop.Framework.ProxyFactoryObject">
    <property name="Target">
    <object type="ConfigAttribute.Service.AttributeService, ConfigAttribute" />
    </property>
    <property name="InterceptorNames">
    <list>
    <value>aroundAdvisor</value>
    </list>
    </property>
    </object>

    <object id="aroundAdvice" type="Common.AroundAdvice, Common"/>


    以上所有源码下载

    demo.zip

  • 相关阅读:
    【Python开发】python使用urllib2抓取防爬取链接
    【Python开发】python使用urllib2抓取防爬取链接
    【Python开发】Python之re模块 —— 正则表达式操作
    【Python开发】Python之re模块 —— 正则表达式操作
    【Python开发】Url中文字符时记得转码edcode("utf-8")
    【Python开发】Url中文字符时记得转码edcode("utf-8")
    【Python开发】urllib2异常处理
    【Python开发】urllib2异常处理
    【Python开发】urllib2.urlopen超时问题
    【Python开发】urllib2.urlopen超时问题
  • 原文地址:https://www.cnblogs.com/riskyer/p/3370790.html
Copyright © 2020-2023  润新知