zoukankan      html  css  js  c++  java
  • AOP之Unity学习纪要

    AOP概念:

    • Aspect-Oriented Programming,面向切面的编程;
    • 比较专业的说法:它是可以通过预编译方式和运行期间动态代理实现,在不修改源代码的情况下给程序动态统一添加功能的一种技术。它是一种新的方法论,它是对传统OOP编程的一种补充。AOP是希望能够将通用需求功能从不相关的类当中分离出来,能够使得很多类共享一个行为,一旦发生变化,不必修改很多类,而只需要修改这个行为即可。实现跨越应用程序多个模块的功能需求;
    • 为了便于理解,通俗的说,在原业务流程中,新增一个功能(我理解为新增一个旁路,即便没有也不影响业务流转),这条旁路也可以附加在其他多个业务流程上,只需修改Unity配置文件以及业务实体的创建就能实现,比如新增或修改日志、验证用户权限、增加缓存、异常拦截功能等这些通用且可能变动的功能,不用挨个修改业务类,且这些通用的功能都是一个单独的模块;
    • Unity默认提供了三种拦截器来实现aop:TransparentProxyInterceptor、InterfaceInterceptor、VirtualMethodInterceptor,以InterfaceInterceptor为例,实现了这个接口的类里的所有方法,均新增了旁路;

    Unity运用步骤

    • 本文不仅用Unity创建对象,还是通过Unity实现AOP给方法增加一些通用功能,如仅用于创建对象,去掉XML配置文件里的interceptionBehavior节点(即不新增额外功能),也可以参考

    博客园的 IOC容器:Unity

    • NuGet引用UnityUnity.ConfigurationUnity.InterceptionUnity.Interception.Configuration
    • 新增一个XML配置文件(InterfaceInterceptor方式),通过配置文件定义某种接口由哪种类来实例化,同时定义功能(旁路)由哪个类提供,记得把属性改为始终复制;
    • 新增一个接口,再新增一个类实现接口,即实现接口里的业务逻辑;
    • 新增功能(旁路)类
      • 类名称通常用Behavior结尾,参考下面的范例,编写处理逻辑,比如日志功能、异常拦截功能等;
      • getNext().Invoke(input, getNext)指向配置文件下一个interceptionBehavior节点,如果是最后一个interceptionBehavior则指向实际业务对象的方法;
      • 另外可以根据方法特性来判断是否执行,如果特性标记在实例类里,则判断方法:bool re = input.Target.GetType().GetMethod(input.MethodBase.Name).IsDefined(typeof(NoAOPAttribute), true);
      • 如果特性标记在接口里,则用:input.MethodBase.IsDefined()来判断;
      • 如果需要抛出异常,就使用:return input.CreateExceptionMethodReturn(new Exception("错误描述"));比直接用new Exception("错误描述")更能准确反馈异常位置,记得要加 return;
      • 异常捕获interceptionBehavior节点,在XML配置文件里通常放第1个;
    • 主程序里
      • 初始化(new)一个IUnityContainer容器对象:
      • 容器对象通过Unity方式读取配置并配置对象;
      • 通过容器实例化(不可以直接new,而是某接口 对象名称 = container.Resolve<某接口>())具体的业务对象,此业务对象再调用方法;
      • 调用方法后,会先进入旁路,再回原函数执行(这个顺序可调整的);

    部分代码

    • XML配置文件样本,文件名起名:Unity.config,新增3个功能,对应3个类
    <configuration>
    	<configSections>
    		<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Unity.Configuration"/>
    	</configSections>
    	<unity>
    		<sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Unity.Interception.Configuration"/>
    		<containers>
    			<!--testContainer,这个名称,在主程序里会用到,用于读取配置-->
    			<container name="testContainer">
    				<extension type="Interception"/>
    				<!--定义某种接口由哪种类来实例化-->
    				<!--name用于一个接口,多个不同类实现的区分-->
    				<register type="Zoulei.AOP.DEMO.IStudy,Zoulei.AOP.DEMO" mapTo="Zoulei.AOP.DEMO.Student, Zoulei.AOP.DEMO" name="str">
    					<interceptor type="InterfaceInterceptor"/>
    					<!--逗号前面是接口类型的完全限定名:命名空间+接口名称,逗号后面是DLL文件的名称 name解决同一个接口不同实例问题-->
    					<!-- 异常处理功能(旁路) 1-->
    					<interceptionBehavior type="Zoulei.AOP.DEMO.ExceptionBehavior,Zoulei.AOP.DEMO"/>
    					<!-- 权限验证功能(旁路) 2-->
    					<interceptionBehavior type="Zoulei.AOP.DEMO.PermissionBehavior,Zoulei.AOP.DEMO"/>
    					<!-- 日志功能(旁路) 3-->
    					<interceptionBehavior type="Zoulei.AOP.DEMO.LogBehavior,Zoulei.AOP.DEMO"/>
    				</register>
    			</container>
    		</containers>
    	</unity>
    </configuration>
    
    • Unity读取配置步骤太多,特记下来:
    IUnityContainer container = new UnityContainer();
    ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
    fileMap.ExeConfigFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "Unityconfig/Unity.config");
    Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
    UnityConfigurationSection unityConfigurationSection = (UnityConfigurationSection)configuration.GetSection(UnityConfigurationSection.SectionName);
    unityConfigurationSection.Configure(container, "testContainer"); //testContainer在XML配置文件里有
    

    – 新增功能(旁路)类,日志类范例

        public class LogBehavior: IInterceptionBehavior
        {
            public IEnumerable<Type> GetRequiredInterfaces()
            {
                return Type.EmptyTypes;
            }
    		//处理逻辑在此方法内,其他方法时框架自有的,不用修改
            public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
            {
            	//是否执行逻辑的判断,即如果类方法有NoAOPAttribute特性,则不执行逻辑
                bool re = input.Target.GetType().GetMethod(input.MethodBase.Name).IsDefined(typeof(NoAOPAttribute), true);
                if (!re)
                {
                    //新增日志处理逻辑 do something...
                }
                //下一步,依据XML配置上下顺序,流转到下一个interceptionBehavior节点,如果已经是最后一个interceptionBehavior节点,则getNext().Invoke(input, getNext)指向业务实体的方法
                return getNext().Invoke(input, getNext);
            }
            
            public bool WillExecute
            {
                get { return true; }
            }
        }
    
    • 异常类范例
        public class ExceptionBehavior : IInterceptionBehavior
        {
            public IEnumerable<Type> GetRequiredInterfaces()
            {
                return Type.EmptyTypes;
            }
            public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
            {
                //先执行其他所有逻辑,如果有异常,则程序再异常处return,中断执行,返回到这里
                IMethodReturn methodReturn = getNext().Invoke(input, getNext);
    
                //最后判断是否有无异常
                Console.WriteLine("ExceptionBehavior 异常");
                if (methodReturn.Exception == null)
                {
                    Console.WriteLine("无异常");
                }
                else
                {
                    Console.WriteLine($"异常信息:{methodReturn.Exception.Message }");
                }
                return methodReturn;
            }
            
            public bool WillExecute
            {
                get { return true; }
            }
        }
    
    • 主程序调用
    //前面是读取XML配置文件,上面有现成范例
    //实现接口IStudy的对象创建,而不是直接`new`,通过容器创建对象
    //IStudy study = container.Resolve<IStudy>("str");这里的str要与XML配置文件里的register节点定义的一致;
    IStudy study = container.Resolve<IStudy>("");
    Student student = new Student { Id = 100, Name = "张三" };
    //调用方法,会先执行XML配置文件里的interceptionBehavior定义的功能,再执行此方法
    study.Study(student);
    
  • 相关阅读:
    对象继承习题
    Java访问修饰符(转)
    对Java JVM中类加载几点解释
    1006 最长公共子序列Lcs
    算法训练 成绩的等级输出
    算法训练 统计字符次数
    算法训练 连接字符串
    算法训练 筛选号码
    算法训练 十进制数转八进制数
    算法训练 斜率计算
  • 原文地址:https://www.cnblogs.com/zoulei0718/p/14315580.html
Copyright © 2011-2022 走看看