最近在研究Nopcommerce,发现其内部IOC框架使用了autofac,简单的了解了一下,记录一下简单的一些用法。初浅了解下来,有一个很直观的感受,就是对代码几乎没什么侵入性,其他性能之类没有测试,但据说性能极佳。
1. 构造函数注入,如下示例(后面所有说明都是在以下的示例基础上进行的)
public interface IRepository { void Update(); } public class FirstRepository : IRepository { public void Update() { Console.WriteLine("FirstRepository.Update"); } } public class SecondRepository : IRepository { public void Update() { Console.WriteLine("SecondRepository.Update"); } } public class ConcreteController { private IRepository _repository; public ConcreteController(IRepository repository) { _repository = repository; } public void Update() { _repository.Update(); } }
调用如下:
ContainerBuilder builder = new ContainerBuilder(); builder.RegisterType
().AsSelf();//也可直接写成builder.RegisterType
() builder.RegisterType
().As
();//注册FirstRepository为IRespository默认实现 IContainer container = builder.Build(); //默认以类参数最多的构造方法进行构造,构造参数根据类型从注册列表中寻找注册的实现类,此处获取的是FirstRepository ConcreteController controller = container.Resolve
(); controller.Update();//FirstRepository.Update
对同一接口多次注册时,会以最后一次注册的实现类为默认实现类,如下:
ContainerBuilder builder = new ContainerBuilder(); builder.RegisterType
().AsSelf();//也可直接写成builder.RegisterType
() builder.RegisterType
().As
(); builder.RegisterType
().As
();//IRepository的默认实现类为SecondRepository IContainer container = builder.Build(); ConcreteController controller = container.Resolve
(); controller.Update();//SecondRepository.Update
如果有这样的一个需求:如果前面注册过了接口的实现类,则以之前注册过的实现类为默认值,如果没有注册过则以此次注册的实现类为默认值,可以通过以下方式实现:
//如果之前存在默认值则以之前注册的值为默认值,否则以此次注册的实现类为默认值 builder.RegisterType
().As
().PreserveExistingDefaults(); ... controller.Update();//FirstRepository.Update
在注册时,可以通过委托的方式,预先指定一个接口的实现类。即采用ContainerBuilder.Register
(委托)的方式来进行实现,如下:
ContainerBuilder builder = new ContainerBuilder(); //通过Register方法,预先指定一个获取实现的委托来注册接口的实现类 builder.Register
(r => new FirstRepository()); IContainer container = builder.Build(); IRepository repository = container.Resolve
();//FirstRepository repository.Update();//FirstRepository.Update
autofac构造注入时,在通过无参的IContainer.resolver获取实现类时,会默认通过实现类最多的参数进行构造,构造方法的参数值为该参数类型注册的实现类。如果该参数值的Type没有在autofac进行注册,则直接报错,如下:
ContainerBuilder builder = new ContainerBuilder(); builder.RegisterType
(); IContainer container = builder.Build(); //ConcreteController的构造方法中需要一个IRepository参数,但autofac检索不到IRepository的实现类,注入失败,报错 //此处不光是针对参数值是引用类型,对于值类型同样存在这样的问题,例如把参数换成int i,同样注入失败,报错 ConcreteController controller = container.Resolve
();//error
对于这样的问题,可以通过在Resolve
时,自行装配,如下:
ContainerBuilder builder = new ContainerBuilder(); builder.RegisterType
(); IContainer container = builder.Build(); //之前没有对ConcreteController构造方法中的参数repository类型IRepository进行注册,但可在解析提取时自动注入 //通过NamedParameter,第一个参数指定参数名称,第二个参数为传递的参数值 ConcreteController controller = container.Resolve
(new NamedParameter("repository", new FirstRepository())); controller.Update();//FirstRepository.Update
除了在Resolver时指定参数值,也可以注册该类时默认指定构造方法的参数值。
ContainerBuilder builder = new ContainerBuilder(); //在注册时就先指定构造方法参数的默认值,即为SecondRepository builder.RegisterType
().WithParameter(new NamedParameter("repository", new SecondRepository())); IContainer container = builder.Build(); ConcreteController controller = container.Resolve
(); controller.Update();//SecondRepository.Update
如果在注册时指定了参数默认值,在Resolver时指定了参数值,则以Resolver指定的参数值为准,如下:
ContainerBuilder builder = new ContainerBuilder(); //在注册时就先指定构造方法参数的默认值,即为SecondRepository builder.RegisterType
().WithParameter(new NamedParameter("repository", new SecondRepository())); IContainer container = builder.Build(); ConcreteController controller = container.Resolve
(new NamedParameter("repository", new FirstRepository())); controller.Update();//FirstRepository.Update
我们之前谈的一直是一个构造方法的情况,假如现在一个实现类有多个构造方法时,autofac会以构造方法参数最多的那个进行注入构造,如下,我们在ConcreteController中新增一个构造方法:
public ConcreteController(IRepository repository, string name) { Console.WriteLine("two args contructor was invoked"); _repository = repository; } ConcreteController controller = container.Resolve
();//没有调用两个参数的构造方法
运行后,发现autofac并没有调用ConcreteController中的两个参数的构造方法进行构造,奇怪了?仔细一瞧,问题就出现在第二个参数string name,还记得之前我们说过,在构造注入时,会检索string在autofac注册的实现类,但此时显示是检索不到(且该参数没有设置默认值例如 string name=null),如果ConcreteController只存在这个构造方法,则会报错,但是还有另一个单个参数的构造方法,因此在试图通过最多参数方法注入不成功时,会转向另一个构造方法,依次下去,直至成功注入为止。我们再换一个方法,新增一个接口,一个类,同时修改ConcreteController第二个构造方法,如下:
public interface IAnotherRepository { } public class AnotherRepository : IAnotherRepository { } public class ConcreteController { ... public ConcreteController(IRepository repository, IAnotherRepository another) { Console.WriteLine("two args contructor was invoked"); _repository = repository; } ... } ContainerBuilder builder = new ContainerBuilder(); builder.RegisterType
(); builder.RegisterType
().As
(); builder.RegisterType
().As
(); IContainer container = builder.Build(); ConcreteController controller = container.Resolve
();//此处调用了两个参数的构造方法
autofac默认调用实现类参数最多的构造方法进行构造,但如果实现类中有好几个参数个数相同的构造方法,autofac如何调用? 例如ConcreteController中有两个如下的构造方法:
public class ConcreteController { ... public ConcreteController(IRepository repository, string name = null) { } public ConcreteController(IRepository repository, IAnotherRepository another) { Console.WriteLine("two args contructor was invoked"); _repository = repository; } ... } ContainerBuilder builder = new ContainerBuilder(); builder.RegisterType
(); builder.RegisterType
().As
(); builder.RegisterType
().As
(); IContainer container = builder.Build(); ConcreteController controller = container.Resolve
();//error报错,autofac不知道调用哪个构造方法
这种情况如何解决?可以通过在注册实现类时,显示指定哪个构造方法(当存在不同参数的构造方法时,亦可通过此方法来指定不同参数的构造方法作为默认值),如下:
ContainerBuilder builder = new ContainerBuilder(); //默认指定了public ConcreteController(IRepository repository, string name = null){} 为注入时调用的构造方法 builder.RegisterType
().UsingConstructor(typeof(IRepository), typeof(string)); builder.RegisterType
().As
(); builder.RegisterType
().As
(); IContainer container = builder.Build(); //调用public ConcreteController(IRepository repository, string name = null){} ConcreteController controller = container.Resolve
();
2. 一个接口存在多个实现类时
a、ContainerManager.Register
<实现类>
().Named
<接口>
(名称) / IContainer.ResolveNamed
<接口>
(名称)
接口>
接口>
实现类>
ContainerBuilder builder = new ContainerBuilder(); builder.RegisterType
().Named
("first"); builder.RegisterType
().Named
("second"); IContainer container = builder.Build(); IRepository first = container.ResolveNamed
("first");//FirstRepository IRepository second = container.ResolveNamed
("second");//SecondRepository
当普通注册和Name方式共同存在时,如下:
ContainerBuilder builder = new ContainerBuilder(); builder.RegisterType
().As
(); builder.RegisterType
().Named
("first"); builder.RegisterType
().Named
("second"); IContainer container = builder.Build(); IRepository first = container.ResolveNamed
("first");//FirstRepository IRepository second = container.ResolveNamed
("second");//SecondRepository //普通方式和Named方式并不矛盾 IRepository third = container.Resolve
();//ThirdRepository
b、ContainerManager.Register
<实现类>
().Keyed
<接口>
(object) / IContainer.ResolveKeyed
<接口>
(object)
public enum RepositoryStyle : int { First, Seond, Third } ContainerBuilder builder = new ContainerBuilder(); builder.RegisterType
().As
(); builder.RegisterType
().Keyed
(RepositoryStyle.First); builder.RegisterType
().Keyed
(RepositoryStyle.Seond); IContainer container = builder.Build(); IRepository first = container.ResolveKeyed
(RepositoryStyle.First);//FirstRepository IRepository second = container.ResolveKeyed
(RepositoryStyle.Seond);//SecondRepository IRepository third = container.Resolve
();//ThirdRepository
3. 属性注入
autofac不提倡属性注入,属性注入意味着该属性的set必须向外公开,同时就意味着属性可人为的在外部进行修改。这样就可能造成一些隐患(被外部非本意的更改),autofac提倡构造方法进行注入,注入后在外部都不可更改。意味着干净,无污染
把ConcreteController的类修改一下,如下:
public class ConcreteController { public IRepository Repository { get; set; } public void Update() { Repository.Update(); } } ContainerBuilder builder = new ContainerBuilder(); builder.RegisterType
().AsSelf(); builder.RegisterType
().As
(); IContainer container = builder.Build(); //autofac默认不会进行属性注入 ConcreteController controller = container.Resolve
(); controller.Update();//NullReferenceException,提示Repository为null
方法1:在注册实现类时,通过委托指定实现类的构造(构造过程中显示进行注入),如下:
ContainerBuilder builder = new ContainerBuilder(); //在注册ConcreteController时,通过委托创建实现类时显式的对属性进行注入 builder.Register
(r => new ConcreteController() { Repository = r.Resolve
() }); builder.RegisterType
().As
(); IContainer container = builder.Build(); ConcreteController controller = container.Resolve
(); controller.Update();//FirstRepository.Update
方法2:使用PropertiesAutowired为所有属性自动注入
ContainerBuilder builder = new ContainerBuilder(); //自动注入所有属性 builder.RegisterType
().PropertiesAutowired(); builder.RegisterType
().As
(); IContainer container = builder.Build(); ConcreteController controller = container.Resolve
(); controller.Update();//FirstRepository.Update
方法3:手工为指定属性注入
ContainerBuilder builder = new ContainerBuilder(); //手工显示为指定属性名称的属性注入 builder.RegisterType
().WithProperty("Repository", new FirstRepository()); builder.RegisterType
().As
(); IContainer container = builder.Build(); ConcreteController controller = container.Resolve
(); controller.Update();//FirstRepository.Update
4. 非泛形的Register/RegisterType/Resolve方法,RegisterType
<接口>
() 相当于 RegisterType(typeof(接口))
接口>
ContainerBuilder builder = new ContainerBuilder(); //作用和builder.RegisterType
().As
();一样的 builder.RegisterType(typeof(FirstRepository)).As(typeof(IRepository)); IContainer container = builder.Build(); //作用和IRepository obj = container.Resolve
(); IRepository obj = container.Resolve(typeof(IRepository)) as IRepository; //FirstRepository
5. 开放的泛型类型
public interface IRepository
{ void Update(T t); } public class FirstRepository
: IRepository
{ public void Update(T t) { Console.WriteLine("FirstRepository.Update " + t.ToString()); } } public class SeondRepository
: IRepository
{ public void Update(T t) { Console.WriteLine("SeondRepository.Update " + t.ToString()); } } ContainerBuilder builder = new ContainerBuilder(); //注册泛形接口时,使用RgisterGeneric方法 builder.RegisterGeneric(typeof(FirstRepository<>)).As(typeof(IRepository<>)); IContainer container = builder.Build(); IRepository
repository = container.Resolve
>(); repository.Update("hehe");
6. 自动装配
a、自动为某个接口注册实现类
ContainerBuilder builder = new ContainerBuilder(); //自动扫描当前程序集中实现了IRepository的实现类,将其中一个实现了IRepository的实现类进行装配 builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).As
(); IContainer container = builder.Build(); IRepository r = container.Resolve
();//返回其中一个实现了IRepository的类实例
b、自动为所有接口注册实现类
ContainerBuilder builder = new ContainerBuilder(); //自动扫描当前程序集,并将所有实现了接口的类注册为该接口的实现类(选取其中的一个实现类) builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).AsImplementedInterfaces(); IContainer container = builder.Build(); IRepository obj1 = container.Resolve
();//返回其中一个实现了IRepository的类实例,此处返回ThirdRepository IAnotherRepository obj2 = container.Resolve
();//返回其中一个实现了IAnotherRepository的类实例,此处返回AnotherRepositor
c、筛选注册
ContainerBuilder builder = new ContainerBuilder(); //自动扫描当前程序集,搜索所有实现了IRepository且类名包含"Second"的类,并将其中的一个注册为IRepository的实现类 builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).Where(t => t.Name.Contains("Second")).As
(); IContainer container = builder.Build(); IRepository obj1 = container.Resolve
();//返回SecondRepository IAnotherRepository obj2 = container.Resolve
();//error,因为没有为IAnotherRepository注册实现类
7. 实例生命周期
a、ContainerBuilder.Register
<实现类>
().As
<接口>
().InstancePerDependency() 用于控制对象的生命周期,每次加载实例时都是新建一个实例,默认就是这种方式
接口>
实现类>
ContainerBuilder builder = new ContainerBuilder(); //返回的都是新建的实例 builder.RegisterType
().As
().InstancePerDependency(); IContainer container = builder.Build(); IRepository obj1 = container.Resolve
(); IRepository obj2 = container.Resolve
(); Console.WriteLine(ReferenceEquals(obj1, obj2));//false
b、ContainerBuilder.Register
<实现类>
().As
<接口>
().InstancePerDependency()
ContainerBuilder builder = new ContainerBuilder(); //返回的都是同一个实例 builder.RegisterType
().As
().SingleInstance(); IContainer container = builder.Build(); IRepository obj1 = container.Resolve
(); IRepository obj2 = container.Resolve
(); Console.WriteLine(ReferenceEquals(obj1, obj2));//true
8. MVC中的应用
MVC用于DI的一个类是DependencyResolver,其中的Current和CurrentCache的静态属性是一个IDependencyResolver。通过该接口的两个方法 object GetService(Type serviceType); IEnumerable
首先先添加Autofac.Integration.Mvc程序集,其中定义了个实现了IDependencyResolver的类AutofacDependencyResolver,其内部就是使用autofac来实现控制反转的
/// /// 仓储接口 /// public interface IRepository { IEnumerable
接口>
实现类>
接口>
接口>
实现类>
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/224343.html原文链接:https://javaforall.net
