.Net中使用Castle进行IOC+AOP(动态注入+代理)

.Net中使用Castle进行IOC+AOP(动态注入+代理)

一、nuget安装Castle

Install-Package Castle.Windsor

二、定义空接口作为标识:所有实现改接口的,说明需要依赖注入或代理(AOP代理必须在IOC基础之上)

    public interface ILingbugService
    {

    }

三、定义特性:作用于服务类/接口上,说明要使用哪个Interceptor进行AOP代理(具体作用于服务类还是接口,看个人喜好)

    public class LingbugInterceptorAttribute : Attribute
    {
        public LingbugInterceptorAttribute(params Type[] interceptorTypes)
        {
            //初始化拦截器集合
            this.InterceptorTypes = new List();
            //将拦截器添加到集合中
            if (interceptorTypes != null && interceptorTypes.Length > 0) this.InterceptorTypes.AddRange(interceptorTypes);
        }

        public List InterceptorTypes { get; set; }
    }

四、定义注册类,实现注册接口(在该类中,编写核心逻辑将IOC和AOP注入到容器中)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Castle.Core;
using Castle.DynamicProxy;
using Castle.MicroKernel;
using Castle.MicroKernel.Registration;

namespace TestCastle
{
    public class CastleRegister : IRegistration
    {
        public void Register(IKernelInternal kernel)
        {
            //需要注入的集合
            var registerComponentList = new List();

            //当前程序集所有类
            var typeList = Assembly.GetExecutingAssembly().GetTypes().ToList();

            //所有拦截器
            var interceptorTypes = typeList.FindAll(r => typeof(IInterceptor).IsAssignableFrom(r));
            //将拦截器添加到集合
            registerComponentList.AddRange(interceptorTypes.Select(r => Component.For(r).ImplementedBy(r)));

            //所有服务类
            var serviceTypes = typeList.FindAll(r => typeof(ILingbugService).IsAssignableFrom(r) && !r.IsInterface);
            serviceTypes.ForEach(s =>
            {
                //要注入的接口类
                Type interfaceItemType = null;
                //该类实现的所有接口
                var interfaceItemTypes = s.GetInterfaces().Where(r => r.Name != typeof(ILingbugService).Name).ToArray();
                if (interfaceItemTypes.Length > 0)
                {
                    if (interfaceItemTypes.Length > 1)
                    {
                        //接口名
                        string interfaceItemTypeName = $"I{s.Name}";
                        //根据名称找接口
                        interfaceItemType = interfaceItemTypes.FirstOrDefault(r => r.Name == interfaceItemTypeName);
                        if (interfaceItemType == null)
                        {
                            //根据名称没找到,提示
                            Console.WriteLine($"服务类【{s.FullName}】未找到任何接口类【接口名:{interfaceItemTypeName}】");
                        }
                    }
                    else
                    {
                        //只实现了一个接口
                        interfaceItemType = interfaceItemTypes[0];
                    }
                }
                else
                {
                    //没有实现任何接口,提示
                    Console.WriteLine($"服务类【{s.FullName}】没有实现任何接口");
                }
                if (interfaceItemType != null)
                {
                    //依赖注入
                    var ioc = Component.For(interfaceItemType).ImplementedBy(s).LifestyleSingleton();
                    //获取到拦截器特性
                    var attrInterceptor = s.GetCustomAttribute();
                    if (attrInterceptor != null && attrInterceptor.InterceptorTypes != null && attrInterceptor.InterceptorTypes.Count > 0)
                    {
                        //拦截器集合
                        var interceptors = attrInterceptor.InterceptorTypes.Select(r => InterceptorReference.ForType(r)).ToArray();
                        //添加拦截器
                        ioc = ioc.Interceptors(interceptors).Anywhere;
                    }
                    //添加到注入集合中
                    registerComponentList.Add(ioc);
                }
            });

            //注入
            kernel.Register(registerComponentList.ToArray());
        }
    }
}

五、定义启动类,在该类中进行容器初始化,在Web开发中,一般在Global.cs中调用

    public class DependencyResolver
    {
        private static IWindsorContainer _container;

        public static void Init()
        {
            //初始化容器
            _container = new WindsorContainer();
            //动态注入到容器中
            _container.Register(new CastleRegister());
        }

        public static T Resolve()
        {
            //从容器中解析对象
            return _container.Resolve();
        }
    }

六、编写测试代码

6.1、根据需求定义拦截对象(实现接口IInterceptor)
using System;
using Castle.DynamicProxy;

namespace TestCastle
{
    /// 
    /// 拦截器一
    /// 
    public class EatInterceptor : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            //标识名
            string name = nameof(EatInterceptor);
            try
            {
                //参数
                string paraStr = string.Join(",", invocation.Arguments);
                //执行前
                Console.WriteLine($"【{name}】开始执行方法:{invocation.Method.Name},参数:{paraStr}");
                //获取到第一个参数
                string food = invocation.Arguments[0].ToString();
                if (food == "香蕉")
                {
                    //价格
                    var money = Convert.ToDecimal(invocation.Arguments[1]);
                    //打折后的价格
                    var newMoney = money * Convert.ToDecimal(0.8);
                    //打印
                    Console.WriteLine($"今天{food}打折,原价【{money}】,折后价【{newMoney}】");
                    //修改参数
                    invocation.SetArgumentValue(1, newMoney);
                }
                //执行代理的方法逻辑
                invocation.Proceed();
                //执行后
                Console.WriteLine($"【{name}】方法【{invocation.Method.Name}】执行完毕,参数:{paraStr},返回值:{invocation.ReturnValue}");
            }
            catch (Exception ex)
            {
                //异常
                Console.WriteLine($"【{name}】执行方法【{invocation.Method.Name}】发生异常:{ex.Message}");
            }
        }
    }
}

using System;
using Castle.DynamicProxy;

namespace TestCastle
{
    /// 
    /// 拦截器二
    /// 
    public class PersonInterceptor : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            //标识名
            string name = nameof(PersonInterceptor);
            try
            {
                //参数
                string paraStr = string.Join(",", invocation.Arguments);
                //执行前
                Console.WriteLine($"【{name}】开始执行方法:{invocation.Method.Name},参数:{paraStr}");
                //执行代理的方法逻辑
                invocation.Proceed();
                //执行后
                Console.WriteLine($"【{name}】方法【{invocation.Method.Name}】执行完毕,参数:{paraStr},返回值:{invocation.ReturnValue}");
            }
            catch (Exception ex)
            {
                //异常
                Console.WriteLine($"【{name}】执行方法【{invocation.Method.Name}】发生异常:{ex.Message}");
            }
        }
    }
}
6.2、编写服务接口和服务实现类
    /// 
    /// 服务接口
    /// 
    public interface IPerson : ILingbugService
    {
        string Eat(string food, decimal money);
    }

    /// 
    /// 服务实现类
    /// 
    [LingbugInterceptor(typeof(EatInterceptor), typeof(PersonInterceptor))]
    public class Person : IPerson
    {
        public string Name { get; set; }

        public int? Age { get; set; }

        public string Sex { get; set; }

        public string Eat(string food, decimal money)
        {
            //准备吃
            Console.WriteLine($"{this.Name}花了【{money}】元买了【{food}】,准备吃饭");
            //正在吃
            for (int i = 0; i < 3; i++)
            {
                //打印消息
                Console.WriteLine($"【{i + 1}】{this.Name}正在吃{food}...");
                //等待
                Thread.Sleep(1000);
            }
            //吃完了
            Console.WriteLine($"{this.Name}吃完了{food}");
            //返回
            return $"姓名:{this.Name},年龄:{this.Age},性别:{this.Sex}";
        }
    }
6.3、测试代码
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("程序启动...");

            //初始化容器
            DependencyResolver.Init();

            //初始化服务类
            var person = DependencyResolver.Resolve();

            try
            {
                //调用方法
                var eatResult = person.Eat("香蕉", Convert.ToDecimal(9.5));
                //打印运行结果
                Console.WriteLine("运行结果:" + eatResult);
            }
            catch (Exception ex)
            {
                //异常
                Console.WriteLine("Error:" + ex.Message);
            }

            Console.WriteLine("运行结束...");
            Console.ReadKey();
        }
    }
6.4、运行测试
程序启动...
【EatInterceptor】开始执行方法:Eat,参数:香蕉,9.5
今天香蕉打折,原价【9.5】,折后价【7.60】
【PersonInterceptor】开始执行方法:Eat,参数:香蕉,7.60
花了【7.60】元买了【香蕉】,准备吃饭
【1】正在吃香蕉...
【2】正在吃香蕉...
【3】正在吃香蕉...
吃完了香蕉
【PersonInterceptor】方法【Eat】执行完毕,参数:香蕉,7.60,返回值:姓名:,年龄:,性别:
【EatInterceptor】方法【Eat】执行完毕,参数:香蕉,9.5,返回值:姓名:,年龄:,性别:
运行结果:姓名:,年龄:,性别:
运行结束...

补充:


说明:依赖注入的三种生命周期
  • Singleton:单例
  • Scoped:线程内单例(一次请求内单例)
  • Transient:相当于New

Named:用字符串给定别名,当一个接口多实现时可以使用该字符串确定需要的是哪个具体服务类

Ending~

你可能感兴趣的:(.NetCore,C#/.NET)