专栏首页架构技术Aop介绍及几种实现方式

Aop介绍及几种实现方式

Aop介绍 我们先看一下wiki百科的介绍 Traditional software development focuses on decomposing systems into units of primary functionality, while recognizing that there are other issues of concern that do not fit well into the primary decomposition. The traditional development process leaves it to the programmers to code modules corresponding to the primary functionality and to make sure that all other issues of concern are addressed in the code wherever appropriate. Programmers need to keep in mind all the things that need to be done, how to deal with each issue, the problems associated with the possible interactions, and the execution of the right behavior at the right time. These concerns span multiple primary functional units within the application, and often result in serious problems faced during application development and maintenance. The distribution of the code for realizing a concern becomes especially critical as the requirements for that concern evolve – a system maintainer must find and correctly update a variety of situations.

Aspect-oriented software development focuses on the identification, specification and representation of cross-cutting concerns and their modularization into separate functional units as well as their automated composition into a working system.

传统的软件开发关注将系统分解成为一个主要的功能单元,然而却发现一些问题并不适合分解方式。这种传统的开发过程让编码人员合作编写主要的功能模块,以此保证那些主要的关注点能够正确的被编码。编码人员需要记住所有需要被完成的事情,如何处理每个问题,问题可能的关联关系,以此确定代码在正确的时候以正确的方式被执行。这些关注点在应用里面跨域了多个主要供单元,这经常在开发和维护时引发一些严重的问题。这些分布式代码导致的问题变得越来越迫切得需要得到解决-一个系统维护人员必须解决这种问题。

面向切面软件开发需要关注这些识别的,详细的,具有代表性的切面问题,将其模块化到功能捣衣并且自动将这些代码组合到一个工作中的系统。

英语比较蹩脚,翻译比较涩,总结起来的意思就是,Aop是将一些已经识别的切面关注的功能封装,并能自动将该功能组合到需要的地方。

我对Aop的理解就是,一些被封装好的,通用的业务单元,真正的编程人员不需要关注的部分,并能动态或静态的将这部分功能组合到业务中去。举个简单的例子,我们在代码中,经常要判断是否用户登录,如果未登录,就需要跳转到指定的页面,伪代码如下:

public string GetNews(){
            /*判断是否登录,如果已经登录,则执行后面的业务代码
             如果没有登录,则跳转到登录页面*/
             
            //业务代码
}

我们可以来看一下简单的流程图

从图中我们可以将代码中的登录的判断业务分解成一个单独的业务单元,在需要的地方打上一个标签,告诉系统这里需要执行,那么其他编码人员就不需要再写重复类似的代码了。这就是Aop解决的问题。

Aop的基本概念

在介绍Aop的实现方式前,我们先了解一下Aop的几个知识点,这有助于我们理解Aop的实际技术。

1)aspect(切面):实现了cross-cutting功能,是针对切面的模块。最常见的是logging模块,这样,程序按功能被分为好几层,如果按传统的继承的话,商业模型继承日志模块的话根本没有什么意义,而通过创建一个logging切面就可以使用AOP来实现相同的功能了。

2)jointpoint(连接点):连接点是切面插入应用程序的地方,该点能被方法调用,而且也会被抛出意外。连接点是应用程序提供给切面插入的地方,可以添加新的方法。比如以上我们的切点可以认为是findInfo(String)方法。

3)advice(处理逻辑):advice是我们切面功能的实现,它通知程序新的行为。如在logging里,logging advice包括logging的实现代码,比如像写日志到一个文件中。advice在jointpoint处插入到应用程序中。以上我们在MyHandler.java中实现了advice的功能

4)pointcut(切点):pointcut可以控制你把哪些advice应用于jointpoint上去。决定了那个jointpoint会获得通知。

5)introduction:允许添加新的方法和属性到类中。

6)target(目标类):是指那些将使用advice的类,一般是指独立的那些商务模型。比如以上的StudentInfoServiceImpl.

7)proxy(代理类):使用了proxy的模式。是指应用了advice的对象,看起来和target对象很相似。

8)weaving(插入):是指应用aspects到一个target对象创建proxy对象的过程:complie time,classload time,runtime

以上这几点是从百度百科中找来的,如果暂时不理解没有关系,后面我会结合实际代码讲解,看完具体代码,再回来这里来对号入座,相信大家可以很快理解这接概念。

Aop的俩种织入方式

目前在.NET平台中,支持的织入方式有俩中,一种是静态织入,即编译时织入,另外一个是动态织入,即运行时织入。俩中方式各有优缺点,使用静态织入,可以不破坏代码结构,这里的破坏代码结构是你需要使用多余配置,写一些多余的代码,或必须依赖某种方式(这里大家也许也还不太明白,可以看完后面俩种方式的具体代码比较,再回头来看,会比较好理解)。使用动态织入的优点就是可以动态调试。俩中织入方式是互补的,即动态织入的优点也是静态织入的缺点,同理,静态织入的优点亦是动态织入的缺点。大家在做技术选型时可以根据自己的实际情况进行选择。

Aop之静态织入

目前成熟的框架有PostSharp,这个框架是商业框架,意思就是需要付费,这里就不具体介绍了,需要了解的土豪请到官网查看,具体如何使用请查阅文档

BSF.Aop .Net 免费开源,静态Aop织入(直接修改IL中间语言)框架,类似PostSharp(收费),实现前后Aop切面和INotifyPropertyChanged注入方式。其原理是在编译生成IL后,借助Mono.Cecil的AssemblyDefinition读取程序集,并检测需要注入的点,并将指定的代码注入到程序集中。有想具体深入研究的同学,可以到 BSF.Aop中下载源码进行研究。遗憾的是这个只实现了俩个切入点,并没有在异常时提供切入点。

我们模拟一个日志记录的例子,我们先建一个项目。

1. 在项目中引用BSF.Aop.dll,Mono.Cecil.dll,Mono.Cecil.Pdb.dll,Microsoft.Build.dll;

2. 添加一个类LogAttribute并继承Aop.Attributes.Around.AroundAopAttribute(切面);

3. 重写AroundAopAttribute的Before和After方法,并写入逻辑代码;

4. 新建一个测试类LogTest,并添加Execute方法,并在Execute方法上面添加LogAttribute标签;

5. 我们在main里面new一个LogTest对象并调用看看输出结果;

具体的代码如下:

 public class LogTest
    {
        [LogAttribute]
        public void Execute(int a)
        {
            a = a * 100;
            System.Console.WriteLine("Hello world!" + a);
        }
    }

    public class LogAttribute : AroundAopAttribute
    {
        public virtual void Before(AroundInfo info)
        {
            System.Console.WriteLine("Log before executed value is" + info.Params["a"]);
        }
        
        public virtual void After(AroundInfo info)
        {
            System.Console.WriteLine("Log after executed value is" + info.Params["a"]);
        }
    }
 static void Main(string[] args)
{
    Aop.AopStartLoader.Start(null);

     new LogTest().Execute(2);

     Console.ReadLine();
}    

执行代码输出:

上例代码中

  • aspect 日志
  • join point 即AroundAopAttribute中的Before和After,即方法执行前和方法执行后
  • advice 即日志的逻辑部分
  • pointcut 即我们LogAttribute中的Before(上面的例子故意没有重写After,是因为怕大家误解)
  • target 这里我们是对方法进行切入的,即Execute方法
  • weaving 这个例子中我们采用的是编译时的织入

Aop之动态织入

使用.NET提供的远程代理,即RealProxies来实现。

1.先建一个Aop代理类AopClassAttribute继承于ProxyAttribute,这个标签会告诉代理,这个类需要被代理创建调用;

/// <summary>
    /// 标记一个类为Aop类,表示该类可以被代理注入
    /// </summary>
    public class AopClassAttribute : ProxyAttribute
    {
        public override MarshalByRefObject CreateInstance(Type serverType)
        {
            AopProxy realProxy = new AopProxy(serverType);
            return realProxy.GetTransparentProxy() as MarshalByRefObject;
        }
    }

2.定义Aop的属性,并定义织入点

/// <summary>
    /// Attribute基类,通过实现该类来实现切面的处理工作
    /// </summary>
    public abstract class AopAttribute : Attribute
    {
        /// <summary>
        /// 调用之前会调用的方法
        /// 1.如果不需要修改输出结果,请返回null
        /// 2.如果返回值不为null,则不会再调用原始方法执行,而是直接将返回的参数作为结果
        /// </summary>
        /// <param name="args">方法的输入参数列表</param>
        /// <param name="resultType">方法的返回值类型</param>
        public abstract object PreCall(object[] args, Type resultType);
        /// <summary>
        /// 调用之后会调用的方法
        /// </summary>
        /// <param name="resultValue">方法的返回值</param>
        /// <param name="args">方法的输入参数列表</param>
        public abstract void Called(object resultValue, object[] args);
        /// <summary>
        /// 调用出现异常时会调用的方法
        /// </summary>
        /// <param name="e">异常值</param>
        /// <param name="args">方法的输入参数列表</param>
        public abstract void OnException(Exception e, object[] args);
    }

3.定义代理的逻辑过程,这里我对returnvalue做了判断,是为了实现缓存更新和添加的切面代码做的,在这里我实现了三个切入点的调用,具体可看注释部分

/// <summary>
    /// 主要代理处理类
    /// </summary>
    internal class AopProxy : RealProxy
    {
        public AopProxy(Type serverType)
            : base(serverType)
        {
        }

        public override IMessage Invoke(IMessage msg)
        {
            if (msg is IConstructionCallMessage) return InvokeConstruction(msg);
            else return InvokeMethod(msg);
        }

        private IMessage InvokeMethod(IMessage msg)
        {
            IMethodCallMessage callMsg = msg as IMethodCallMessage;
            IMessage returnMessage;
            object[] args = callMsg.Args;
            var returnType = (callMsg.MethodBase as System.Reflection.MethodInfo).ReturnType;//方法返回类型
            object returnValue = null;//方法返回值
            AopAttribute[] attributes = callMsg.MethodBase.GetCustomAttributes(typeof(AopAttribute), false) as AopAttribute[];
            try
            {
                if (attributes == null || attributes.Length == 0) return InvokeActualMethod(callMsg);
          //前切点
                foreach (AopAttribute attribute in attributes)
                    returnValue = attribute.PreCall(args, returnType);
                //如果以前切面属性都没有返回值,则调用原始的方法;否则不调用
                //主要是做缓存类似的业务
                if (returnValue == null)
                {
                    returnMessage = InvokeActualMethod(callMsg);
                    returnValue = (returnMessage as ReturnMessage).ReturnValue;
                }
                else returnMessage = new ReturnMessage(returnValue, args, args.Length, callMsg.LogicalCallContext, callMsg);
          //后切点
                foreach (AopAttribute attribute in attributes)
                    attribute.Called(returnValue,args);
            }
            catch (Exception e)
            {
         //异常切入点
                foreach (AopAttribute attribute in attributes)
                    attribute.OnException(e, args);

                returnMessage = new ReturnMessage(e, callMsg);
            }
            return returnMessage;
        }

        private IMessage InvokeActualMethod(IMessage msg)
        {
            IMethodCallMessage callMsg = msg as IMethodCallMessage;
            object[] args = callMsg.Args;
            object o = callMsg.MethodBase.Invoke(GetUnwrappedServer(), args);
            return new ReturnMessage(o, args, args.Length, callMsg.LogicalCallContext, callMsg);
        }

        private IMessage InvokeConstruction(IMessage msg)
        {
            IConstructionCallMessage constructCallMsg = msg as IConstructionCallMessage;
            IConstructionReturnMessage constructionReturnMessage = this.InitializeServerObject((IConstructionCallMessage)msg);
            RealProxy.SetStubData(this, constructionReturnMessage.ReturnValue);
            return constructionReturnMessage;
        }
    }

4.定义上下文边界对象,想要使用Aop的类需要继承此类(这个是这种Aop方式破坏性最大的地方,因为需要继承一个类,而面向对象单继承的特性导致了业务类不能再继承其他的类。可以想象一下你有一个查询基类,然后另一个查询类想要继承查询基类,而又想使用Aop,这时就尴尬了);

/// <summary>
    /// Aop基类,需要注入的类需要继承该类
    /// 对代码继承有要求,后续可以改进一下
    /// 注意,需要记录的不支持上下文绑定,如果需要记录,使用代理模式解决
    /// </summary>
    public abstract class BaseAopObject : ContextBoundObject
    {
    }

5.定义Advice部分,即实际的业务逻辑,继承于AopAttribute

public class IncreaseAttribute : AopAttribute
    {
        private int Max = 10;
        public IncreaseAttribute(int max)
        {
            Max = max;
        }
        public override object PreCall(object[] args, Type resultType)
        {
            if (args == null || args.Count() == 0 || !(args[0] is ExampleData)) return null;

            var data = args[0] as ExampleData;
            string numString = args[0].ToString();
            data.Num = data.Num * 100;
            Console.WriteLine(data.Num);
            return null;
        }

        public override void Called(object resultValue, object[] args)
        {
            if (args == null || args.Count() == 0 || !(args[0] is ExampleData)) return;

            var data = args[0] as ExampleData;
            string numString = args[0].ToString();
            data.Num = data.Num * 100;
            Console.WriteLine(data.Num);
        }

        public override void OnException(Exception e, object[] args)
        {

        }
    }

    public class ExampleData
    {
        public int Num { get; set; }
    }

6.完成了上面的部分,我们就可以来使用Aop了,定义一个需要使用Aop的类,继承于BaseAopObject,并在类上面加上[AopClass],在需要切入的方法上加上刚才定义的[IncreaseAttribute]

[AopClass]
    public class Example : BaseAopObject
    {
        [IncreaseAttribute(10)]
        public static void Do(ExampleData data)
        {
            Add(data);
        }

        [IncreaseAttribute(10)]
        public static ExampleData Add(ExampleData data)
        {
            return new ExampleData { Num = ++data.Num };
        }
    }

可以看到,使用上面这种织入方式,对代码的侵入性太大,会限制代码的可扩展性。所以我比较不建议使用。

另一种方式是借助Ioc的代理来做Aop切面注入,这里我们以Unity作为Ioc容器,以之前写的关于Unity Ioc中的例子来介绍Aop。

1.添加AopAttribute(定义连接点),这里有个循环引用,就是AopHandler和AopAttribute之间,不过并不影响使用,如有需要大家可以自己解决一下;

/// <summary>
    /// 标记一个类或方法为代理,表示该类或方法可以被代理
    /// </summary>
    [AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
    public abstract class AopAttribute : HandlerAttribute
    {
        /// <summary>
        /// 请勿重写该方法
        /// </summary>
        /// <param name="container"></param>
        /// <returns></returns>
        public override ICallHandler CreateHandler(IUnityContainer container)
        {
            return new AopHandler();
        }
        /// <summary>
        /// 调用之前会调用的方法
        /// 1.如果不需要修改输出结果,请返回null,ouputs返回new object[0]
        /// 2.如果返回值不为null,则不会再调用原始方法执行,而是直接将返回的参数作为结果
        /// </summary>
        /// <param name="inputArgs">方法的输入参数列表</param>
        /// <param name="outputs">方法中的out值,如果没有请返回null</param>
        /// <returns>返回值</returns>
        public abstract object PreCall(object[] inputArgs, out object[] outputs);
        /// <summary>
        /// 调用之后会调用的方法
        /// </summary>
        /// <param name="resultValue">方法的返回值</param>
        /// <param name="inputArgs">方法的输入参数列表</param>
        /// <param name="outputs">方法中的out值,如果没有则该参数值为null</param>
        public abstract void Called(object resultValue, object[] inputArgs, object[] outputs);
        /// <summary>
        /// 调用出现异常时会调用的方法
        /// </summary>
        /// <param name="e">异常值</param>
        /// <param name="inputArgs">方法的输入参数列表,键为参数名,值为参数值</param>
        public abstract void OnException(Exception e, Dictionary<string, object> inputArgs);
    }

2.添加AopHandler(代理类);

/// <summary>
    /// 主要代理处理类
    /// </summary>
    internal class AopHandler : ICallHandler
    {
        public int Order { get; set; } = 1;

        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
        {
            IMethodReturn returnValue = null;
            object attrReturnValue = null;
            object[] outputs = null;
            Dictionary<string, object> inputs = new Dictionary<string, object>();
            //假如有忽略特性,直接忽略,不进行AOP代理
            IgnoreAttribute[] ignoreAttributes = input.MethodBase.GetCustomAttributes(typeof(IgnoreAttribute), true) as IgnoreAttribute[];
            if (ignoreAttributes != null && ignoreAttributes.Length > 0) return input.CreateMethodReturn(attrReturnValue, outputs);

            AopAttribute[] attributes = input.MethodBase.GetCustomAttributes(typeof(AopAttribute), true) as AopAttribute[];
            try
            {
                if (attributes == null || attributes.Length == 0) return getNext()(input, getNext);

                for (var i = 0; i < input.Arguments.Count; i++) inputs.Add(input.Inputs.ParameterName(i), input.Inputs[i]);

                foreach (AopAttribute attribute in attributes)
                    attrReturnValue = attribute.PreCall(inputs.Values.ToArray(), out outputs);
                //如果以前切面属性都没有返回值,则调用原始的方法;否则不调用
                //主要是做缓存类似的业务
                if (attrReturnValue == null)
                {
                    returnValue = getNext()(input, getNext);
                    outputs = new object[returnValue.Outputs.Count];
                    for (var i = 0; i < returnValue.Outputs.Count; i++) outputs[i] = returnValue.Outputs[i];
                }
                else returnValue = input.CreateMethodReturn(attrReturnValue, outputs);

                if (returnValue.Exception != null) throw returnValue.Exception;

                foreach (AopAttribute attribute in attributes)
                    attribute.Called(returnValue.ReturnValue, inputs.Values.ToArray(), outputs);
            }
            catch (Exception e)
            {
                foreach (AopAttribute attribute in attributes)
                    attribute.OnException(e, inputs);
                returnValue = input.CreateExceptionMethodReturn(e);
            }
            return returnValue;
        }
    }

3..定义一个我们自己的功能块(业务逻辑),这里还是以日志为例;

public class LogAttribute : AopAttribute
    {
        public override void Called(object resultValue, object[] inputArgs, object[] outputs)
        {
            Console.WriteLine("Called");
        }

        public override void OnException(Exception e, Dictionary<string, object> inputArgs)
        {
            Console.WriteLine("exception:" + e.Message);
        }

        public override object PreCall(object[] inputArgs, out object[] outputs)
        {
            Console.WriteLine("PreCall");
            outputs = new object[0];
            return null;
        }
    }

5.接下来我们稍微改造一下我们的印钞机;

 /// <summary>
    /// 印钞机
    /// </summary>
    public class CashMachine
    {
        public CashMachine() { }

        public void Print(ICashTemplate template)
        {
            string templateContent = template.GetTemplate("人民币");

            System.Console.WriteLine(templateContent);
        }
    }
    /// <summary>
    /// 印钞模块
    /// </summary>
    public interface ICashTemplate
    {
        /// <summary>
        /// 获取钞票模板
        /// </summary>
        /// <returns></returns>
        [Log]
        string GetTemplate(string flag);
    }

    /// <summary>
    /// 人民币钞票模板
    /// </summary>
    public class CNYCashTemplate : ICashTemplate
    {
        public CNYCashTemplate() { }
        public string GetTemplate(string flag)
        {
            return "这是人民币模板!" + flag + "  这是返回值。";
        }
    }
    /// <summary>
    /// 美钞钞票模板
    /// </summary>
    public class USDCashTemplate : ICashTemplate
    {
        public USDCashTemplate() { }

        public string GetTemplate(string flag)
        {
            throw new Exception("哎呀,美钞模板有问题呀!");
        }
    }

6.然后我们在命令行的Main里改造一下;

static void Main(string[] args)
        {
            try
            {
                ICashTemplate usdTemplate = new USDCashTemplate();
                ICashTemplate rmbTemplate = new CNYCashTemplate();
                new CashMachine().Print(rmbTemplate);
                new CashMachine().Print(usdTemplate);
            }
            catch (Exception)
            {
            }
            Console.ReadLine();
        }

7.启动一下看看结果

8.可以看到,只输出了GetTemplate方法的输出,并没有输出日志,我们要使用Ioc来注册对象才能使用,继续改造Main方法;

 static void Main(string[] args)
        {
            UnityContainer container = new UnityContainer();
            container.AddNewExtension<Interception>().RegisterType<ICashTemplate, CNYCashTemplate>("cny");
            container.Configure<Interception>().SetInterceptorFor<ICashTemplate>("cny", new InterfaceInterceptor());
            container.AddNewExtension<Interception>().RegisterType<ICashTemplate, USDCashTemplate>("usd");
            container.Configure<Interception>().SetInterceptorFor<ICashTemplate>("usd", new InterfaceInterceptor());

            try
            {new CashMachine().Print(container.Resolve<ICashTemplate>("cny"));
                new CashMachine().Print(container.Resolve<ICashTemplate>("usd"));
            }
            catch (Exception)
            {
            }
            Console.ReadLine();
        }

9.启动运行,看一下结果;

可以看到,三个方法都执行了,而在抛出异常时是不会执行Called的方法的;

10.上面我们是直接使用了UnityContainer来注册对象,而没有使用我们之前封装的Ioc,我们还有更简单的方式,就是采用配置的方式来注册对象和拦截器实现Aop。在实际,使用一个单独的文件来配置ioc会更易于维护。我们先添加一个unity.config文件;

<?xml version="1.0" encoding="utf-8" ?>
<unity xmlns= "http://schemas.microsoft.com/practices/2010/unity ">
  <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Unity.Interception.Configuration"/>
  <!--注入对象-->
  <typeAliases>
    <!--表示单例-->
    <typeAlias alias="singleton" type="Unity.Lifetime.ContainerControlledLifetimeManager,Unity.Abstractions" />
    <!--表示每次使用都进行创建-->
    <typeAlias alias="transient" type="Unity.Lifetime.TransientLifetimeManager,Unity.Abstractions" />
  </typeAliases>
  <container name= "Default">
    <extension type="Interception"/>
    <!--type表示接口 格式为 带命名空间的接口,程序集名 mapTo表示需要注入的实体类 name表示注入实体的name-->
    <register type= "IocWithUnity.ICashTemplate,IocWithUnity" mapTo= "IocWithUnity.CNYCashTemplate,IocWithUnity" name="cny">
      <!--定义拦截器-->
      <interceptor type="InterfaceInterceptor"/>
      <policyInjection/>
      <!--定义对象生命周期-->
      <lifetime type="singleton" />
    </register>
    <!--type表示接口 格式为 带命名空间的接口,程序集名 mapTo表示需要注入的实体类 name表示注入实体的name-->
    <register type= "IocWithUnity.ICashTemplate,IocWithUnity" mapTo= "IocWithUnity.USDCashTemplate,IocWithUnity" name="usd">
      <!--定义拦截器-->
      <interceptor type="InterfaceInterceptor"/>
      <policyInjection/>
      <!--定义对象生命周期-->
      <lifetime type="singleton" />
    </register>
  </container>
</unity>

11.再配置app.config(WEB项目应该是web.config);

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Unity.Configuration"/>
  </configSections>
  <unity configSource="unity.config"/>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
</configuration>

12.将我们之前写的IocContainer修改一下读取配置;

 public static class IocContainer
    {
        private static IUnityContainer _container = null;
        static IocContainer()
        {
            _container = new UnityContainer();

            object unitySection = ConfigurationManager.GetSection("unity");
            if (unitySection == null) return;

            UnityConfigurationSection section = (UnityConfigurationSection)unitySection;
            section.Configure(_container, "Default");
        }
        /// <summary>
        /// 注册一个实例作为T的类型
        /// </summary>
        /// <typeparam name="T">需要注册的类型</typeparam>
        /// <param name="instance">需要注册的实例</param>
        public static void Register<T>(T instance)
        {
            _container.RegisterInstance<T>(instance);
        }
        /// <summary>
        /// 注册一个名为name的T类型的实例
        /// </summary>
        /// <typeparam name="T">需要注册的类型</typeparam>
        /// <param name="name">关键字名称</param>
        /// <param name="instance">实例</param>
        public static void Register<T>(string name, T instance)
        {
            _container.RegisterInstance(name, instance);
        }
        /// <summary>
        /// 将类型TFrom注册为类型TTo
        /// </summary>
        /// <typeparam name="TFrom"></typeparam>
        /// <typeparam name="TTo"></typeparam>
        public static void Register<TFrom, TTo>() where TTo : TFrom
        {
            _container.RegisterType<TFrom, TTo>();
        }
        /// <summary>
        /// 将类型TFrom注册为类型TTo
        /// </summary>
        /// <typeparam name="TFrom"></typeparam>
        /// <typeparam name="TTo"></typeparam>
        /// <typeparam name="lifetime"></typeparam>
        public static void Register<TFrom, TTo>(LifetimeManager lifetime) where TTo : TFrom
        {
            _container.RegisterType<TFrom, TTo>(lifetime);
        }
        /// <summary>
        /// 将类型TFrom注册名为name类型TTo
        /// </summary>
        /// <typeparam name="TFrom"></typeparam>
        /// <typeparam name="TTo"></typeparam>
        public static void Register<TFrom, TTo>(string name) where TTo : TFrom
        {
            _container.RegisterType<TFrom, TTo>(name);
        }
        /// <summary>
        /// 通过关键字name来获取一个实例对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="name"></param>
        /// <returns></returns>
        public static T Resolve<T>(string name)
        {
            return _container.Resolve<T>(name);
        }
        /// <summary>
        /// 获取一个为T类型的对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static T Resolve<T>()
        {
            return _container.Resolve<T>();
        }
        /// <summary>
        /// 获取所有注册类型为T的对象实例
        /// </summary>
        /// <typeparam name="T">需要获取的类型的对象</typeparam>
        /// <returns></returns>
        public static IEnumerable<T> ResolveAll<T>()
        {
            return _container.ResolveAll<T>();
        }
    }

注意:配置时有一个坑 <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Unity.Configuration"/> 这句话在5.0版本后dll的名称改了,以前是Microsoft.Practices.Unity.Configuration,现在是Unity.Configuration,如果你在运行时碰到找不到文件或程序集xxx时,可以注意看一下你的具体的dll的文件名。包括后面的unity.config里面的lifetime的配置也是,大家需要注意一下自己的版本,然后找到对应的命名空间和dll文件进行配置。

13.接下来我们运行一下看看结果如何;

总结:可以看到,静态织入方式相对较简单,对代码破坏性近乎于0,其原理大致是在编译前,将需要的代码添加到我们添加了Attribute的地方,如果用反编译工具反编译生成的dll就可以看到实际编译后的代码。这种织入方式的缺点是不易于调试工作,因为生成的pdb文件与我们的源代码文件实际上是不一样的。而采用真实代理的方式进行织入,这种方式比较原生,但对代码侵入性较大,而且效率也较低。使用ioc框架的拦截器进行拦截织入的方式,是当下比较好的一种方式,但是也是有一个约束,就是对象必须经过ioc容器来委托创建。基于这些比较,各位看官可以选择适合自己的织入方式。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 控制反转-Ioc之Unity

    本篇幅主要介绍控制反转的一些概念,和如何使用Unity实现Ioc。在介绍的时候,会尽量结合代码来讲解一些概念。

    少羽大怪兽
  • C#重试公用类

    少羽大怪兽
  • 做管理最没用的三件事:讲道理、发脾气和刻意感动

    实际上,员工激励的重要性,几乎所有管理者都认识到了。但是,往往劲儿没使对,导致管理者有巨大的无力感,而员工也不领情。

    少羽大怪兽
  • 基于DotNet构件技术的企业级敏捷软件开发平台 - AgileEAS.NET - ORM访问器

        上一篇文章AgileEAS.NET之数据关系映射ORM简单介绍了一下AgileEAS.NET平台中ORM对象的组织机构体系,但并没有对其所执行的数据存取...

    魏琼东
  • 委托示例(利用委托对不同类型的对象数组排序)

    using System; using System.Collections.Generic; using System.Text; namespac...

    菩提树下的杨过
  • T4生成整理

    将一些公用的东西弄到一个类库DM.T4里面,哪个项目需要用到t4生成的话,将DM.T4的生成事件中将dll和ModelAuto.ttinclude复制到需要使用...

    冰封一夏
  • Java设计模式之模板方法模式

    假设我们现在要造一批悍马汽车,悍马汽车有两个系列H1和H2,首先不考虑任何设计模式,看看设计的类图:

    CoderJed
  • ABP入门系列(4)——创建应用服务

    一、解释下应用服务层 应用服务用于将领域(业务)逻辑暴露给展现层。展现层通过传入DTO(数据传输对象)参数来调用应用服务,而应用服务通过领域对象来执行相应的业务...

    圣杰
  • Android谈谈封装那些事--BaseActivity和BaseFragment(二)

    版权声明:本文为博主原创文章,转载请标明出处。 https://blog.csdn.net/lyhhj/article/details/53...

    Hankkin
  • Silverlight 2 应用程序部署到任意HTML页面

    Silverlight 2 应用程序部署到任意HTML页面,可以采用ActiveX的方法,在页面中放入object标签,设置相应的属性即可。使用 object ...

    张善友

扫码关注云+社区

领取腾讯云代金券