zoukankan      html  css  js  c++  java
  • 在ABP项目的应用Castle Windsor

    Castle Windsor常用介绍以及其在ABP项目的应用介绍

    最近在研究ABP项目,有关ABP的介绍请看阳光铭睿 博客,ABP的DI和AOP框架用的是Castle Windsor
    下面就对Castle Windsor项目常用方法介绍和关于ABP的使用总结

    1、下载Castle.Windsor所需要的dll,在程序包管理器控制台 运行Install-Package Castle.Windsor

    下面先看个简单的例子

    1
    2
    3
    4
    5
    6
    7
    8
    var container = new WindsorContainer();
     
    container.Register(
    Component.For(typeof(IMyService)
    .ImplementedBy(typeof(MyServiceImpl)
    );
    //控制反转 得到实例
    var myService= container.Resolve<IMyService>();

    我们首先创建了WindsorContainer然后注册了MyServiceImpl以及它的接口,然后我们用容器创建了一个MyServiceImpl的实例

    2、注册 Castle.Windsor有很多方法来注册你的类,下面一一介绍几种注册方法

    常规注册 

    我们可以使用Castle.MicroKernel.Registration.Component这个静态类,的For方法进行注册,返回一个 ComponentRegistration,就可以用他来进一步注册
    注册一个类到容器,默认的注册类型是Singleton也就是单例

    1
    2
    3
    container.Register(
        Component.For<MyServiceImpl>()
    );

    给接口注册一个默认实例,这种做abp项目中应用很多

    1
    2
    3
    4
    container.Register(
        Component.For(typeof(IMyService)
            .ImplementedBy(typeof(MyServiceImpl)
    );

    当然我们也可以指定注册的实例方式,主要有Transient和Singleton,Transient是每次请求都创建一个新实例,Singleton是单例,他们都是LifeStyle的属性

    1
    2
    3
    4
    5
    container.Register(
       Component.For<IMyService>()
          .ImplementedBy<MyServiceImpl>()
          .LifeStyle.Transient
    );

    当注册一个接口有多个实例的时候,我们可以以命名的方式来注册,下面这个是没有重命名的情况下,默认是注册第一个MyServiceImpl的

    1
    2
    3
    4
    container.Register(
        Component.For<IMyService>().ImplementedBy<MyServiceImpl>(),
        Component.For<IMyService>().ImplementedBy<OtherServiceImpl>()
    );

    比如Nop项目中的缓存,但是Nop项目是用Autofac,那么你反转的时候就可以根据名字进行反转了

    1
    2
    builder.RegisterType<MemoryCacheManager>().As<ICacheManager>().Named<ICacheManager>("nop_cache_static").SingleInstance();
    builder.RegisterType<PerRequestCacheManager>().As<ICacheManager>().Named<ICacheManager>("nop_cache_per_request").InstancePerLifetimeScope();

    在Castle Windsor我们可以

    1
    2
    3
    container.Register(
        Component.For<IMyService>().Named("OtherServiceImpl").ImplementedBy<OtherServiceImpl>()
    );

    以上讲到了windsor项目中常用的最简单的注册方式,那么我们也可以按照程序集进行注册,比如根据当前程序集注册以IController为接口的实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public WindsorControllerFactory(IWindsorContainer container)
    {
       this.container = container;
       var controllerTypes =
           from in Assembly.GetExecutingAssembly().GetTypes()
           where typeof(IController).IsAssignableFrom(t)
           select t;
           foreach (var in controllerTypes)
              container.Register(Component.For(t).LifeStyle.Transient);
    }

    Assembly.GetExecutingAssembly()是获取当前运行的程序集,或者你也可以这样,下面是ABP代码

    1
    2
    3
    4
    5
    6
    7
    8
    context.IocManager.IocContainer.Register(
                    Classes.FromAssembly(context.Assembly)
                        .IncludeNonPublicTypes()
                        .BasedOn<ISingletonDependency>()
                        .WithService.Self()
                        .WithService.DefaultInterfaces()
                        .LifestyleSingleton()
                    );

      

    自定义注册

    你也可以重写IWindsorInstaller方法Install进行统一注册 

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class RepositoriesInstaller : IWindsorInstaller
    {
        public void Install(IWindsorContainer container, IConfigurationStore store)
        {
            container.Register(Classes.FromThisAssembly()
                                .Where(Component.IsInSameNamespaceAs<King>())
                                .WithService.DefaultInterfaces()
                                .LifestyleTransient());
        }
    }

    构造函数&属性注入

    构造函数和属性注入是项目开发的最佳实践,你可以使用去获取你的类的依赖关系。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public class PersonAppService
    {
        public ILogger Logger { getset; }
        private IPersonRepository _personRepository;
     
        public PersonAppService(IPersonRepository personRepository)
        {
           _personRepository = personRepository;
           Logger = NullLogger.Instance;
        }
     
        public void CreatePerson(string name, int age)
        {
           Logger.Debug("Inserting a new person to database with name = " + name);
           var person = new Person { Name = name, Age = age };
           _personRepository.Insert(person);
           Logger.Debug("Successfully inserted!");
        }
    }

    IPersonRepository 从构造函数注入, ILogger 实例从公共属性注入。这样, 你的代码不会体现依赖注入系统。这是使用 DI 系统最适当的方式。

    一般的控制器的话我们会统一注册,下面是ABP注册控制器的代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public class WindsorControllerFactory : DefaultControllerFactory
    {
        private readonly IKernel kernel;
        public WindsorControllerFactory(IKernel kernel)
        {
            this.kernel = kernel;
        }
        public override void ReleaseController(IController controller)
        {
            kernel.ReleaseComponent(controller);
        }
        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            if (controllerType == null)
            {
                throw new HttpException(404, string.Format("The controller for path '{0}' could not be found.", requestContext.HttpContext.Request.Path));
            }
            return (IController)kernel.Resolve(controllerType);
        }
    }

    采用构造函数的注入模式是一个完美的提供类的依赖关系的方式。通过这种方式, 只有提供了依赖你才能创建类的实例。 同时这也是一个强大的方式显式地声明,类需要什么样的
    依赖才能正确的工作。但是,在有些情况下,该类依赖于另一个类,但也可以没有它。这通常是适用于横切关注点(如日志记录)。一个类可以没有工作日志,但它可以写日志如果你提供一个日志对象。
    在这种情况下, 你可以定义依赖为公共属性,而不是让他们放在构造函数。---摘自abp中文文档

    好了,到了终于把Castle Windsor一些常用的注册写完了,上面主要还是讲依赖注入,下面开始ABP的相关介绍

    ABP定义了一个统一的注册类IocManager,主要提供注册、反转、注销等操作

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    public class IocManager : IIocManager
    {
        public static IocManager Instance { getprivate set; }
        public IWindsorContainer IocContainer { getprivate set; }
        private readonly List<IConventionalDependencyRegistrar> _conventionalRegistrars;
     
        static IocManager()
        {
            Instance = new IocManager();
        }
     
        public IocManager()
        {
            IocContainer = new WindsorContainer();
            _conventionalRegistrars = new List<IConventionalDependencyRegistrar>();
     
            //Register self!
            IocContainer.Register(
                Component.For<IocManager, IIocManager, IIocRegistrar, IIocResolver>().UsingFactoryMethod(() => this)
                );
        }
     
        /// <summary>
        /// Registers types of given assembly by all conventional registrars. See <see cref="AddConventionalRegistrar"/> method.
        /// </summary>
        /// <param name="assembly">Assembly to register</param>
        /// <param name="config">Additional configuration</param>
        public void RegisterAssemblyByConvention(Assembly assembly, ConventionalRegistrationConfig config)
        {
            var context = new ConventionalRegistrationContext(assembly, this, config);
     
            //这个循环还是要进行四个注册,
            foreach (var registerer in _conventionalRegistrars)
            {
                registerer.RegisterAssembly(context);
            }
     
            if (config.InstallInstallers)
            {
                IocContainer.Install(FromAssembly.Instance(assembly));
            }
        }

    有个重要的方法是RegisterAssemblyByConvention会循环遍历继承IConventionalDependencyRegistrar接口的所有类,并进行注册

    查看ABP的代码发现,实现该接口的主要有四个类,分别注册DbContex类型,控制器和ApiController和注册基于接口ITransientDependency和ISingletonDependency和IInterceptor 实现的类

    由于篇幅关系 我就只展示注册控制器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    /// <summary>
    /// Registers all MVC Controllers derived from <see cref="Controller"/>.
    /// </summary>
    public class ControllerConventionalRegistrar : IConventionalDependencyRegistrar
    {
        /// <inheritdoc/>
        public void RegisterAssembly(IConventionalRegistrationContext context)
        {
            context.IocManager.IocContainer.Register(
                Classes.FromAssembly(context.Assembly)
                    .BasedOn<Controller>()
                    .LifestyleTransient()
                );
        }
    }

    关于Castle.Windsor注册有一个实体,一般我们常用的有Singleton(单例)和Transient(每次创建新对象)那么我们看下ABP是怎么做的吧

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public enum DependencyLifeStyle
    {
        /// <summary>
        /// Singleton object. Created a single object on first resolving
        /// and same instance is used for subsequent resolves.
        /// </summary>
        Singleton,
        /// <summary>
        /// Transient object. Created one object for every resolving.
        /// </summary>
        Transient
    }

    定义了一个关于LifeStyle的枚举进行相关注册

    1
    2
    3
    4
    public void Register(Type type, DependencyLifeStyle lifeStyle = DependencyLifeStyle.Singleton)
    {
        IocContainer.Register(ApplyLifestyle(Component.For(type), lifeStyle));
    }

      

    另外Castle Windsor还支持日志,这点在ABP也有应用到,支持log4net等 
    首先需要下载相应的dll ,Install-Package Castle.Windsor-log4net
    其次自动注册一个Log实例
    1
    2
    3
    4
    5
    6
    7
    public class LoggerInstaller : IWindsorInstaller
    {
        public void Install(IWindsorContainer container, IConfigurationStore store)
        {
            container.AddFacility<LoggingFacility>(f => f.UseLog4Net());
        }
    }

    第三配置下log4net.config文件,下面是我的简单配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <configSections>
        <!--日志配置部分-->
        <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
      </configSections>
      <log4net>
        <root>
          <priority value="All" />
          <appender-ref ref="FileAppender" />
          <appender-ref ref="InfoLoging" />
        </root>
        <appender name="FileAppender" type="log4net.Appender.RollingFileAppender">
          <file value="log\log.txt" />
          <appendToFile value="true" />
          <maxSizeRollBackups value="10" />
          <maximumFileSize value="10000KB" />
          <rollingStyle value="Size" />
          <staticLogFileName value="true" />
          <filter type="log4net.Filter.LevelRangeFilter">
            <levelMin value="ERROR" />
            <levelMax value="ERROR" />
          </filter>
          <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
          <layout type="log4net.Layout.PatternLayout">
            <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
          </layout>
        </appender>
        <appender name="InfoLoging" type="log4net.Appender.RollingFileAppender">
          <file value="log\logData.txt" />
          <appendToFile value="true" />
          <maxSizeRollBackups value="10" />
          <maximumFileSize value="10000KB" />
          <rollingStyle value="Size" />
          <staticLogFileName value="true" />
          <filter type="log4net.Filter.LevelRangeFilter">
            <levelMin value="INFO" />
            <levelMax value="INFO" />
          </filter>
          <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
          <layout type="log4net.Layout.PatternLayout">
            <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
          </layout>
        </appender>
      </log4net>
    </configuration>

    文章最后会附源码,包含配置等

    最后我们就可以使用了,下面我用的是属性注入方式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class AccountController : Controller
    {
        public ILogger Logger { getset; }
        public AccountController()
        {
            Logger = NullLogger.Instance;
        }
        public ActionResult LogOn()
        {
            Logger.Error("test");
            return View();
        }
    }

    当然ABP也是提供这种方式用log4net日志的,但是它驱动的时候是在Global中配置

    上面是Castle Windsor的IOC的应用,当然在ABP中也有用到其AOP方法,我们可以继承IInterceptor的Intercept方法来进行拦截

    1
    2
    3
    4
    5
    6
    7
    8
    9
    namespace Castle.DynamicProxy
    {
        using System;
         
        public interface IInterceptor
        {
            void Intercept(IInvocation invocation);
        }
    }

    下面看下ABP最重要的一个拦截方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    internal class UnitOfWorkInterceptor : IInterceptor
    {
        private readonly IUnitOfWorkManager _unitOfWorkManager;
     
        public UnitOfWorkInterceptor(IUnitOfWorkManager unitOfWorkManager)
        {
            _unitOfWorkManager = unitOfWorkManager;
        }
     
        /// <summary>
        /// Intercepts a method.
        /// </summary>
        /// <param name="invocation">Method invocation arguments</param>
        public void Intercept(IInvocation invocation)
        {
            if (_unitOfWorkManager.Current != null)
            {
                //Continue with current uow
                invocation.Proceed();
                return;
            }
     
            var unitOfWorkAttr = UnitOfWorkAttribute.GetUnitOfWorkAttributeOrNull(invocation.MethodInvocationTarget);
            if (unitOfWorkAttr == null || unitOfWorkAttr.IsDisabled)
            {
                //No need to a uow
                invocation.Proceed();
                return;
            }
     
            //No current uow, run a new one
            PerformUow(invocation, unitOfWorkAttr.CreateOptions());
        }

    它的注册是在模块中进行注册的,注册主要包含IRepository和IApplicationService和UnitOfWorkAttribute,所以包含[UnitOfWork]的方法和继承IRepository和IApplicationService的方法都会被拦截

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    /// <summary>
    /// 拦截注册事件
    /// </summary>
    /// <param name="key"></param>
    /// <param name="handler"></param>
    private static void ComponentRegistered(string key, IHandler handler)
    {
        if (UnitOfWorkHelper.IsConventionalUowClass(handler.ComponentModel.Implementation))
        {
            //Intercept all methods of all repositories.
            handler.ComponentModel.Interceptors.Add(new InterceptorReference(typeof(UnitOfWorkInterceptor)));
        }
        else if (handler.ComponentModel.Implementation.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Any(UnitOfWorkHelper.HasUnitOfWorkAttribute))
        {
            //Intercept all methods of classes those have at least one method that has UnitOfWork attribute.
            //TODO: Intecept only UnitOfWork methods, not other methods!
            handler.ComponentModel.Interceptors.Add(new InterceptorReference(typeof(UnitOfWorkInterceptor)));
        }
    }

    以上就把Castle Windsor的常用功能和ABP项目中的使用简单的讲完了。主要参考的几个项目ABP、NOP、Prodinner

    简单的源码地址:http://pan.baidu.com/s/1kTCNpQZ

    参考文章:

    https://github.com/ABPFrameWorkGroup/AbpDocument2Chinese

    https://github.com/castleproject/Windsor/blob/master/docs/README.md

    http://www.cnblogs.com/wucg/archive/2012/03/09/2387946.html 

  • 相关阅读:
    iOS重签名及问题总结
    安装class-dump
    UISearchController 很坑
    hashmap 之哈希冲突
    wait()与sleep()的区别
    docker之es+es-head+kibana+ik分词器安装
    MySQL很有用的命令
    分布式事务执行逻辑
    索引 创建原则
    合理使用存储引擎
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/4836918.html
Copyright © 2011-2022 走看看