C# Autofac学习笔记
一、为什么使用Autofac?
Autofac是.NET领域最为流行的IoC框架之一,传说是速度最快的一个。
1.1、性能
有人专门做了测试:
1.2、优点
1)与C#语言联系很紧密。C#里的很多编程方式都可以为Autofac使用,例如可以使用Lambda表达式注册组件。
2)较低的学习曲线。学习它非常的简单,只要你理解了IoC和DI的概念以及在何时需要使用它们。
3)支持JSON/XML配置。
4)自动装配。
5)与Asp.Net MVC集成。
6)微软的Orchad开源程序使用的就是Autofac,可以看出它的方便和强大。
1.3、资源
官方网站:http://autofac.org/
GitHub网址:https://github.com/autofac/Autofac
学习资料:Autofac中文文档
二、数据准备
2.1、新建项目
IService下的接口类:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace LinkTo.Test.Autofac.IService { /// <summary> /// 动物吠声接口类 /// </summary> public interface IAnimalBark { /// <summary> /// 吠叫 /// </summary> void Bark(); } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace LinkTo.Test.Autofac.IService { /// <summary> /// 动物睡眠接口类 /// </summary> public interface IAnimalSleep { /// <summary> /// 睡眠 /// </summary> void Sleep(); } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace LinkTo.Test.Autofac.IService { /// <summary> /// 学校接口类 /// </summary> public interface ISchool { /// <summary> /// 放学 /// </summary> void LeaveSchool(); } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace LinkTo.Test.Autofac.IService { /// <summary> /// 学生接口类 /// </summary> public interface IStudent { /// <summary> /// 增加学生 /// </summary> /// <param name="studentID">学生ID</param> /// <param name="studentName">学生姓名</param> void Add(string studentID, string studentName); } }
Service下的接口实现类:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using LinkTo.Test.Autofac.IService; namespace LinkTo.Test.Autofac.Service { /// <summary> /// 猫类 /// </summary> public class Cat : IAnimalSleep { /// <summary> /// 睡眠 /// </summary> public void Sleep() { Console.WriteLine("小猫咪睡着了zZ"); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using LinkTo.Test.Autofac.IService; namespace LinkTo.Test.Autofac.Service { /// <summary> /// 狗类 /// </summary> public class Dog : IAnimalBark, IAnimalSleep { /// <summary> /// 吠叫 /// </summary> public void Bark() { Console.WriteLine("汪汪汪"); } /// <summary> /// 睡眠 /// </summary> public void Sleep() { Console.WriteLine("小狗狗睡着了zZ"); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using LinkTo.Test.Autofac.IService; namespace LinkTo.Test.Autofac.Service { /// <summary> /// 学校类 /// </summary> public class School : ISchool { /// <summary> /// IAnimalBark属性 /// </summary> public IAnimalBark AnimalBark { get; set; } /// <summary> /// 放学 /// </summary> public void LeaveSchool() { AnimalBark.Bark(); Console.WriteLine("你家的熊孩子放学了⊙o⊙"); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using LinkTo.Test.Autofac.IService; namespace LinkTo.Test.Autofac.Service { /// <summary> /// 学生类 /// </summary> public class Student : IStudent { /// <summary> /// 无参构造函数 /// </summary> public Student() { } /// <summary> /// 有参构造函数 /// </summary> /// <param name="studentID">学生ID</param> /// <param name="studentName">学生姓名</param> public Student(string studentID, string studentName) { Add(studentID, studentName); } /// <summary> /// 增加学生 /// </summary> /// <param name="studentID">学生ID</param> /// <param name="studentName">学生姓名</param> public void Add(string studentID, string studentName) { Console.WriteLine($"新增的学生是:{studentName}"); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using LinkTo.Test.Autofac.IService; namespace LinkTo.Test.Autofac.Service { /// <summary> /// 动物摇尾巴 /// </summary> public class AnimalWagging { /// <summary> /// IAnimalBark属性 /// </summary> IAnimalBark animalBark; /// <summary> /// 有参构造函数 /// </summary> /// <param name="bark">IAnimalBark变量</param> public AnimalWagging(IAnimalBark bark) { animalBark = bark; } /// <summary> /// 摇尾巴 /// </summary> public virtual void Wagging() { animalBark.Bark(); Console.WriteLine("摇尾巴"); } /// <summary> /// 计数 /// </summary> /// <returns></returns> public static int Count() { return 6; } /// <summary> /// 任务 /// </summary> /// <param name="name">动物名称</param> /// <returns></returns> public virtual async Task<string> WaggingAsync(string name) { var result = await Task.Run(() => Count()); return $"{name}摇了{result}下尾巴"; } } }
2.2、Autofac安装
Client项目右键->管理 NuGet 程序包->Autofac。
三、IoC-注册
3.1、类型注册
a)类型注册:使用RegisterType进行注册。
//注册Autofac组件 ContainerBuilder builder = new ContainerBuilder(); //注册实现类Student,当我们请求IStudent接口的时候,返回的是类Student的对象。 builder.RegisterType<Student>().As<IStudent>(); //上面这句也可改成下面这句,这样请求Student实现了的任何接口的时候,都会返回Student对象。 //builder.RegisterType<Student>().AsImplementedInterfaces(); IContainer container = builder.Build(); //请求IStudent接口 IStudent student = container.Resolve<IStudent>(); student.Add("1001", "Hello");
b)类型注册(别名):假如一个接口有多个实现类,可以在注册时起别名。
ContainerBuilder builder = new ContainerBuilder(); builder.RegisterType<Dog>().Named<IAnimalSleep>("Dog"); builder.RegisterType<Cat>().Named<IAnimalSleep>("Cat"); IContainer container = builder.Build(); var dog = container.ResolveNamed<IAnimalSleep>("Dog"); dog.Sleep(); var cat = container.ResolveNamed<IAnimalSleep>("Cat"); cat.Sleep();
c)类型注册(枚举):假如一个接口有多个实现类,也可以使用枚举的方式注册。
public enum AnimalType { Dog, Cat }
ContainerBuilder builder = new ContainerBuilder(); builder.RegisterType<Dog>().Keyed<IAnimalSleep>(AnimalType.Dog); builder.RegisterType<Cat>().Keyed<IAnimalSleep>(AnimalType.Cat); IContainer container = builder.Build(); var dog = container.ResolveKeyed<IAnimalSleep>(AnimalType.Dog); dog.Sleep(); var cat = container.ResolveKeyed<IAnimalSleep>(AnimalType.Cat); cat.Sleep();
3.2、实例注册
ContainerBuilder builder = new ContainerBuilder(); builder.RegisterInstance<IStudent>(new Student()); IContainer container = builder.Build(); IStudent student = container.Resolve<IStudent>(); student.Add("1001", "Hello");
3.3、Lambda注册
a)Lambda注册
ContainerBuilder builder = new ContainerBuilder(); builder.Register(c => new Student()).As<IStudent>(); IContainer container = builder.Build(); IStudent student = container.Resolve<IStudent>(); student.Add("1001", "Hello");
b)Lambda注册(NamedParameter)
ContainerBuilder builder = new ContainerBuilder(); builder.Register<IAnimalSleep>((c, p) => { var type = p.Named<string>("type"); if (type == "Dog") { return new Dog(); } else { return new Cat(); } }).As<IAnimalSleep>(); IContainer container = builder.Build(); var dog = container.Resolve<IAnimalSleep>(new NamedParameter("type", "Dog")); dog.Sleep();
3.4、程序集注册
如果有很多接口及实现类,假如觉得这种一一注册很麻烦的话,可以一次性全部注册,当然也可以加筛选条件。
ContainerBuilder builder = new ContainerBuilder(); Assembly assembly = Assembly.Load("LinkTo.Test.Autofac.Service"); //实现类所在的程序集名称 builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces(); //常用 //builder.RegisterAssemblyTypes(assembly).Where(t=>t.Name.StartsWith("S")).AsImplementedInterfaces(); //带筛选 //builder.RegisterAssemblyTypes(assembly).Except<School>().AsImplementedInterfaces(); //带筛选 IContainer container = builder.Build(); //单实现类的用法 IStudent student = container.Resolve<IStudent>(); student.Add("1001", "Hello"); //多实现类的用法 IEnumerable<IAnimalSleep> animals = container.Resolve<IEnumerable<IAnimalSleep>>(); foreach (var item in animals) { item.Sleep(); }
3.5、泛型注册
ContainerBuilder builder = new ContainerBuilder(); builder.RegisterGeneric(typeof(List<>)).As(typeof(IList<>)); IContainer container = builder.Build(); IList<string> list = container.Resolve<IList<string>>();
3.6、默认注册
ContainerBuilder builder = new ContainerBuilder(); //对于同一个接口,后面注册的实现会覆盖之前的实现。 //如果不想覆盖的话,可以用PreserveExistingDefaults,这样会保留原来注册的实现。 builder.RegisterType<Dog>().As<IAnimalSleep>(); builder.RegisterType<Cat>().As<IAnimalSleep>().PreserveExistingDefaults(); //指定为非默认值 IContainer container = builder.Build(); var dog = container.Resolve<IAnimalSleep>(); dog.Sleep();
四、IoC-注入
4.1、构造函数注入
ContainerBuilder builder = new ContainerBuilder(); builder.RegisterType<AnimalWagging>(); builder.RegisterType<Dog>().As<IAnimalBark>(); IContainer container = builder.Build(); AnimalWagging animal = container.Resolve<AnimalWagging>(); animal.Wagging();
4.2、属性注入
ContainerBuilder builder = new ContainerBuilder(); Assembly assembly = Assembly.Load("LinkTo.Test.Autofac.Service"); //实现类所在的程序集名称 builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces().PropertiesAutowired(); //常用 IContainer container = builder.Build(); ISchool school = container.Resolve<ISchool>(); school.LeaveSchool();
五、IoC-事件
Autofac在组件生命周期的不同阶段,共对应了5个事件,执行顺序如下所示:
1.OnRegistered->2.OnPreparing->3.OnActivating->4.OnActivated->5.OnRelease
ContainerBuilder builder = new ContainerBuilder(); builder.RegisterType<Student>().As<IStudent>() .OnRegistered(e => Console.WriteLine("OnRegistered:在注册的时候调用")) .OnPreparing(e => Console.WriteLine("OnPreparing:在准备创建的时候调用")) .OnActivating(e => Console.WriteLine("OnActivating:在创建之前调用")) //.OnActivating(e => e.ReplaceInstance(new Student("1000", "Test"))) .OnActivated(e => Console.WriteLine("OnActivated:在创建之后调用")) .OnRelease(e => Console.WriteLine("OnRelease:在释放占用的资源之前调用")); using (IContainer container = builder.Build()) { IStudent student = container.Resolve<IStudent>(); student.Add("1001", "Hello"); }
六、IoC-生命周期
6.1、Per Dependency
Per Dependency:为默认的生命周期,也被称为"transient"或"factory",其实就是每次请求都创建一个新的对象。
ContainerBuilder builder = new ContainerBuilder(); Assembly assembly = Assembly.Load("LinkTo.Test.Autofac.Service"); //实现类所在的程序集名称 builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces().PropertiesAutowired().InstancePerDependency(); //常用 IContainer container = builder.Build(); ISchool school1 = container.Resolve<ISchool>(); ISchool school2 = container.Resolve<ISchool>(); Console.WriteLine(school1.Equals(school2));
6.2、Single Instance
Single Instance:就是每次都用同一个对象。
ContainerBuilder builder = new ContainerBuilder(); Assembly assembly = Assembly.Load("LinkTo.Test.Autofac.Service"); //实现类所在的程序集名称 builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces().PropertiesAutowired().SingleInstance(); //常用 IContainer container = builder.Build(); ISchool school1 = container.Resolve<ISchool>(); ISchool school2 = container.Resolve<ISchool>(); Console.WriteLine(ReferenceEquals(school1, school2));
6.3、Per Lifetime Scope
Per Lifetime Scope:同一个Lifetime生成的对象是同一个实例。
ContainerBuilder builder = new ContainerBuilder(); builder.RegisterType<School>().As<ISchool>().InstancePerLifetimeScope(); IContainer container = builder.Build(); ISchool school1 = container.Resolve<ISchool>(); ISchool school2 = container.Resolve<ISchool>(); Console.WriteLine(school1.Equals(school2)); using (ILifetimeScope lifetime = container.BeginLifetimeScope()) { ISchool school3 = lifetime.Resolve<ISchool>(); ISchool school4 = lifetime.Resolve<ISchool>(); Console.WriteLine(school3.Equals(school4)); Console.WriteLine(school2.Equals(school3)); }
七、IoC-通过配置文件使用Autofac
7.1、组件安装
Client项目右键->管理 NuGet 程序包->Autofac.Configuration及Microsoft.Extensions.Configuration.Xml。
7.2、配置文件
新建一个AutofacConfigIoC.xml文件,在其属性的复制到输出目录项下选择始终复制。
<?xml version="1.0" encoding="utf-8" ?> <autofac defaultAssembly="LinkTo.Test.Autofac.IService"> <!--无注入--> <components name="1001"> <type>LinkTo.Test.Autofac.Service.Student, LinkTo.Test.Autofac.Service</type> <services name="0" type="LinkTo.Test.Autofac.IService.IStudent" /> <injectProperties>true</injectProperties> </components> <components name="1002"> <type>LinkTo.Test.Autofac.Service.Dog, LinkTo.Test.Autofac.Service</type> <services name="0" type="LinkTo.Test.Autofac.IService.IAnimalBark" /> <injectProperties>true</injectProperties> </components> <!--构造函数注入--> <components name="2001"> <type>LinkTo.Test.Autofac.Service.AnimalWagging, LinkTo.Test.Autofac.Service</type> <services name="0" type="LinkTo.Test.Autofac.Service.AnimalWagging, LinkTo.Test.Autofac.Service" /> <injectProperties>true</injectProperties> </components> <!--属性注入--> <components name="3001"> <type>LinkTo.Test.Autofac.Service.School, LinkTo.Test.Autofac.Service</type> <services name="0" type="LinkTo.Test.Autofac.IService.ISchool" /> <injectProperties>true</injectProperties> </components> </autofac>
7.3、测试代码
//加载配置 ContainerBuilder builder = new ContainerBuilder(); var config = new ConfigurationBuilder(); config.AddXmlFile("AutofacConfigIoC.xml"); var module = new ConfigurationModule(config.Build()); builder.RegisterModule(module); IContainer container = builder.Build(); //无注入测试 IStudent student = container.Resolve<IStudent>(); student.Add("1002", "World"); //构造函数注入测试 AnimalWagging animal = container.Resolve<AnimalWagging>(); animal.Wagging(); //属性注入测试 ISchool school = container.Resolve<ISchool>(); school.LeaveSchool();
八、AOP
8.1、组件安装
Client项目右键->管理 NuGet 程序包->Autofac.Extras.DynamicProxy。
8.2、拦截器
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using Castle.DynamicProxy; namespace LinkTo.Test.Autofac.Client { /// <summary> /// 拦截器:需实现IInterceptor接口。 /// </summary> public class CallLogger : IInterceptor { private readonly TextWriter _output; public CallLogger(TextWriter output) { _output = output; } /// <summary> /// 拦截方法:打印被拦截的方法--执行前的名称、参数以及执行后的返回结果。 /// </summary> /// <param name="invocation">被拦截方法的信息</param> public void Intercept(IInvocation invocation) { //空白行 _output.WriteLine(); //在下一个拦截器或目标方法处理之前的处理 _output.WriteLine($"调用方法:{invocation.Method.Name}"); if (invocation.Arguments.Length > 0) { _output.WriteLine($"参数:{string.Join(", ", invocation.Arguments.Select(a => (a ?? "").ToString()).ToArray())}"); } //调用下一个拦截器(若存在),直到最终的目标方法(Target Method)。 invocation.Proceed(); //获取被代理方法的返回类型 var returnType = invocation.Method.ReturnType; //异步方法 if (IsAsyncMethod(invocation.Method)) { //Task:返回值是固定类型 if (returnType != null && returnType == typeof(Task)) { //定义一个异步方法来等待目标方法返回的Task async Task Continuation() => await (Task)invocation.ReturnValue; //Continuation()中并没有使用await,所以Continuation()就如同步方法一样是阻塞的。 invocation.ReturnValue = Continuation(); } //Task<T>:返回值是泛型类型 else { //获取被代理方法的返回类型 var returnTypeT = invocation.Method.ReflectedType; if (returnTypeT != null) { //获取泛型参数集合,集合中的第一个元素等价于typeof(Class)。 var resultType = invocation.Method.ReturnType.GetGenericArguments()[0]; //利用反射获得等待返回值的异步方法 MethodInfo methodInfo = typeof(CallLogger).GetMethod("HandleAsync", BindingFlags.Public | BindingFlags.Instance); //调用methodInfo类的MakeGenericMethod()方法,用获得的类型T(<resultType>)来重新构造HandleAsync()方法。 var mi = methodInfo.MakeGenericMethod(resultType); //Invoke:使用指定参数调用由当前实例表示的方法或构造函数。 invocation.ReturnValue = mi.Invoke(this, new[] { invocation.ReturnValue }); } } var type = invocation.Method.ReturnType; var resultProperty = type.GetProperty("Result"); if (resultProperty != null) _output.WriteLine($"方法结果:{resultProperty.GetValue(invocation.ReturnValue)}"); } //同步方法 else { if (returnType != null && returnType != typeof(void)) _output.WriteLine($"方法结果:{invocation.ReturnValue}"); } } /// <summary> /// 判断是否异步方法 /// </summary> public static bool IsAsyncMethod(MethodInfo method) { return ( method.ReturnType == typeof(Task) || (method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)) ); } /// <summary> /// 构造等待返回值的异步方法 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="task"></param> /// <returns></returns> public async Task<T> HandleAsync<T>(Task<T> task) { var t = await task; return t; } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Castle.DynamicProxy; namespace LinkTo.Test.Autofac.Client { public class CallTester: IInterceptor { public void Intercept(IInvocation invocation) { Console.WriteLine("啥也不干"); invocation.Proceed(); Console.WriteLine("也不干啥"); } } }
8.3、测试代码
注意:对于以类方式的注入,Autofac Interceptor要求类的方法必须为virtual方法。如AnimalWagging类的Wagging()、WaggingAsync(string name)都加了virtual修饰符。
ContainerBuilder builder = new ContainerBuilder(); //注册拦截器 builder.Register(c => new CallLogger(Console.Out)); builder.Register(c => new CallTester()); //动态注入拦截器 //这里定义了两个拦截器,注意它们的顺序。 builder.RegisterType<Student>().As<IStudent>().InterceptedBy(typeof(CallLogger), typeof(CallTester)).EnableInterfaceInterceptors(); //这里定义了一个拦截器 builder.RegisterType<AnimalWagging>().InterceptedBy(typeof(CallLogger)).EnableClassInterceptors(); builder.RegisterType<Dog>().As<IAnimalBark>(); IContainer container = builder.Build(); IStudent student = container.Resolve<IStudent>(); student.Add("1003", "Kobe"); AnimalWagging animal = container.Resolve<AnimalWagging>(); animal.Wagging(); Task<string> task = animal.WaggingAsync("哈士奇"); Console.WriteLine($"{task.Result}");
IoC参考自:
https://www.xin3721.com/ArticlecSharp/c14013.html
https://www.cnblogs.com/GoogleGetZ/p/10218721.html
http://niuyi.github.io/blog/2012/04/06/autofac-by-unit-test/
https://www.cnblogs.com/kissdodog/p/3611799.html
AOP参考自:
https://www.cnblogs.com/stulzq/p/6880394.html
https://blog.csdn.net/weixin_38211198/article/details/105925821