IOC之Autofac

IOC之Autofac最近在研究 发现其内部 IOC 框架使用了 autofac 简单的了解了一下 记录一下简单的一些用法 初浅了解下来 有一个很直观的感受 就是对代码几乎没什么侵入性 其他性能之类没有测试 但据说性能极佳 1 构造函数注入 如下示例 后面所有说明都是在以下的示例基础上进行的 publicinterf voidUpdate

最近在研究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); IEnumerableGetServices(Type serviceType)来获取实现类。DependencyResolver默认的IDependencyResolver是DefaultDependcyResolver其内部简单的通过反射创建Type的实例。现在我们利用autofac来代替DefaultDependencyResolver。

首先先添加Autofac.Integration.Mvc程序集,其中定义了个实现了IDependencyResolver的类AutofacDependencyResolver,其内部就是使用autofac来实现控制反转的




///  /// 仓储接口 ///  public interface IRepository { IEnumerable Get(string id); bool Update(object entity); bool Save(object entity); bool Delete(object entity); } ///  /// 仓储实现 ///  public class ConcreteRepository : IRepository { public IEnumerable Get(string id) { return new List(); } public bool Update(object entity) { return true; } public bool Save(object entity) { return true; } public bool Delete(object entity) { return true; } } ///  /// 控制器 ///  public class HomeController : Controller { IRepository repository; public HomeController(IRepository repository) { this.repository = repository; } public ActionResult Index() { var list = repository.Get("");//repository为ConcreteRepository return View(); } } ///  /// Global ///  public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RouteConfig.RegisterRoutes(RouteTable.Routes); ContainerBuilder builder = new ContainerBuilder(); RegisterContainer(builder); //Autofac.Integration.Mvc中定义的扩展方法,注册所有控制器本身 //类似一个个去注册Controller,例如 builder.RegisterType 
                                     
                                       ().AsSelf(); builder.RegisterControllers(Assembly.GetExecutingAssembly()); IContainer container = builder.Build(); DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); } /// 
                                       /// 注册 ///  /// 
                                       private void RegisterContainer(ContainerBuilder builder) { //可以为所有接口注册实现类builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).AsImplementedInterfaces(); builder.RegisterType 
                                       
                                         ().As 
                                        
                                          (); } } 
                                         
                                         
                                     





























































版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/224343.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月17日 下午12:09
下一篇 2026年3月17日 下午12:09


相关推荐

  • psutil包

    psutil包psutil是python自带的包,用来监控内存使用率的importpsutilclassUtilize: def__init__(self): pass #CPU使用率defcpu_utilize(self):try:returnJsonRet(content=psutil.cpu_percent(interv…

    2022年6月11日
    38
  • Netty权威指南_netty官方中文文档

    Netty权威指南_netty官方中文文档如果链接不好使了,可以留言我在重新发一个链接:https://pan.baidu.com/s/1FJd2KWqzEQVVQ7jgeWViDg提取码:vgie

    2022年10月2日
    4
  • Bat注释符号

    Bat注释符号打开命令显示 echoon 关闭命令显示 echooff echooff 加 表示连 echooff 都不显示 不然会显示出 echooff 的命令 rem 注释 表示不执行 rem 后面的脚本 不能出现重定向符号和管道符号 双冒号注释内容 第一个冒号后也可以跟任何一个非字母数字的字符 echo 注释内容 不能出现重定向符号和管道符号 nulifnotexis

    2026年3月19日
    2
  • OKhttpClient 简单使用总结「建议收藏」

    http优化,由httpClient改为OKHttpClient,研究了一下,网上资料不多大部分是安卓的,就着httpClient的入参简单写了一个公共方法,因为上一层使用了hystrix,就没有使用异步调用。后期看业务需要增加OKHttp的拦截和其他特性。注意请求timeout报java.io.InterruptedIOException:threadinterrupted异常,希望最新…

    2022年4月16日
    55
  • spring data jpa 深入浅出的理解「建议收藏」

    spring data jpa 深入浅出的理解「建议收藏」文章来源于:https://www.cnblogs.com/cmfwm/p/8109433.html这是一篇写得很不错的关于spring-data-jpa的文章,转载到此,方便大家学习交流.本篇进行Spring-data-jpa的介绍,几乎涵盖该框架的所有方面,在日常的开发当中,基本上能满足所有需求。这里不讲解JPA和Spring-data-jpa单独使用,所有的内容都是在和Spri…

    2022年5月5日
    41
  • Java快排实现(java快速排序代码)

    快速排序:基本实现思路取一个标准位置的数字用其他位置的数字和标准数进行对比如果比标准数大则放到标准数的右边,如果比标准数小则放到标准数的左边然后使用递归进行持续比对(注意:递归要有入口如果当前数组有数据并且多个才进行排序),然后我们用代码实现packagesort;importjava.util.Arrays;/***Created…

    2022年4月16日
    50

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号