• Orchard源码分析(4.3):Orchard.Events.EventsModule类(Event Bus)


    概述
    采用Event Bus模式(事件总线),可以使观察者模式中的观察者和被观察者实现解耦。
    在.Net 中使用观察者模式,可以使用事件(委托)和接口(类)。Orchard Event  Bus使用的是接口的形式,这样方便将“观察者”注册到Autofac容器中。EventsModule模块是构成Orchard Event  Bus的一部分。这里先分开分析Orchard Event Bus涉及的类型和知识点,然后在将他们组合起来分析Orchard Event  Bus的机制。
    一、Registration Source 首先EventsModule也是一个Autofac模块。其注册了一个EventsRegistrationSource对象,所以分析的重点是在EventsRegistrationSource类上。
    Registration Source可以用于检索容器要使用的服务(Service,Autofac概念)。
    下面用一个简单的例子来说明: 1、创建如下接口和类:
        public interface ICustomerService { }
        public class DefaultCustomerService ICustomerService { }
      
    2、测试:
            [Test]
            public void TestRegistrationSource()
            {
                ContainerBuilder builder = new ContainerBuilder();
                //builder.RegisterSource(new MyRegistrationSource());         
                IContainer container = builder.Build();
                ICustomerService customerService = container.Resolve<ICustomerService>();
                Assert.That(customerService,Is .Not.Null);
            }
    3、ICustomerService没有在容器中进行注册,很显然上面的代码不能测试通过,现在创建一个MyRegistrationSource类,实现IRegistrationSource接口:
         public class MyRegistrationSource IRegistrationSource
        {
            public IEnumerable <IComponentRegistration> RegistrationsFor( Service service, Func <ServiceIEnumerable<IComponentRegistration >> registrationAccessor)
            {
     
                var serviceWithType = service as IServiceWithType;
                if (serviceWithType == null )
                    yield break ;
     
                var serviceType = serviceWithType.ServiceType;
                if (!serviceType.IsInterface || !typeof (ICustomerService).IsAssignableFrom(serviceType))
                    yield break ;
                /*
                //使用RegistrationBuilder.ForType方法
                var rb = RegistrationBuilder
                    .ForType<DefaultCustomerService>()
                    .As(service);
                */
     
                //使用RegistrationBuilder.ForDelegate方法
                var rb = RegistrationBuilder
                   .ForDelegate((ctx, parameters) =>
                   {
                       return new DefaultCustomerService();
                   })
                   .As(service);
     
                yield return rb.CreateRegistration();
            }
        }
    4、测试: 取消//builder.RegisterSource(new MyRegistrationSource());行的注释,再次测试通过。
       上面的例子看起来有点小题大作或者说完全没必要,因为我们已经明确知道接口及其实现类,有两种替代方式可以实现相同的目的。首先可以创建一个 Autofac模块(继承自Module类或IModule接口皆可),在其Load方法中进行注册;或者直接在创建ContainerBuilder对 象的地方注册,比如:
            [Test]
            public void TestRegistrationSource()
            {
                ContainerBuilder builder = new ContainerBuilder();
                builder.RegisterType< DefaultCustomerService>().As<ICustomerService >();
                IContainer container = builder.Build();
                ICustomerService customerService = container.Resolve<ICustomerService>();
                Assert.That(customerService,Is.Not.Null);
            }
    但是Registration Source其实是为了提供一种延迟注册机制,比如我们马上要分析的EventsRegistrationSource类。
    二、Dynamic Proxy和EventsRegistrationSourceCastls 动态代理可以根据某个类或接口动态创建代理类。当然通过接口实现的代理类的方法中没有任何可用实现。但是可以通过使用注入器(AOP,详见 IInterceptor接口),在代理类的方法执行前、执行后注入一定的逻辑。关于动态代理这里不详述。不过可以想象一下ASP.NET  MVC中的Filter。
     
    EventsRegistrationSource类中就使用了接口代 理。对于IEventHandler的子接口的类型(注意不是IEventHandler的直接子类型),则创建一个对应代理类。当方法执行时,交由 DefaultOrchardEventBus来处理。
    三、DefaultOrchardEventBus DefaultOrchardEventBus类的作用是通过反射动态调用一组对象的某一个方法,它提供了缓存机制弥补反射的低性能,并提供了异常处理接口。    该类的构造函数接受两个参数,类型分别是Func<IEnumerable<IEventHandler>>委托和 IExceptionPolicy类型。通过第一个参数可以获取包含调用方法的类序列。通过第二个参数提供异常处理策 略,IExceptionPolicy可以处理异常(比如日志记录),还可以决定是否把异常继续由DefaultOrchardEventBus向外抛 出。 该类还有唯一的公开方法Notify,接受一个字符串和IDictionary<string,  object>型参数。字符串由"."号分隔,"."前面是接口短名称(注意不是类名称),后面是被调用方法名称。 IDictionary<string,  object>型参数包含调用方法时要传递的参数。通过接口名称和方法名称查找某一个类型的方法并调用。Notify返回IEnumeralbe型 参数,是所有被调用方法返回值的集合。 在某个角度上看,DefaultOrchardEventBus类似于WPF的ObjectDataProvider。
    四、Orchard Event Bus机制 有了上面的知识积累,下面通过一个简单的实例来分析Orchard的Event Bus机制。 我们来实现一个简单的观察者模式。 1、新建一个控制台项目,并准备一些接口和类
        public interface IObserver void Notify(string message);}
        public class Observer1 IObserver public void Notify( string message) { Console .WriteLine("Call Observer1.Notify Method:{0}", message); } }
        public class Observer2 IObserver public void Notify( string message) { Console .WriteLine("Call Observer2.Notify Method:{0}", message); } }
        public interface ISubject void DoSomething(); }
        public class Subject ISubject
        {
            public readonly IListIObserver> Handlers = new ListIObserver>();
            public void DoSomething()
             {
                 Console.WriteLine("Call DoSomething method." );
                 foreach(var item in Handlers)
                 {
                     item.Notify( "Hello");
                 }
             }
        }
        public class Tester
        {
            static void Main(string[] args)
            {
                Subject subject = new Subject();
                subject.Handlers.Add( new Observer1 ());
                subject.Handlers.Add( new Observer2 ());
                subject.DoSomething();
            }
        }
    (通过事件或委托也能实现观察者模式,这里不进行讨论) 2、第三方程序集也想得到通知 新建一个类库项目,创建Observer3类
         public class Observer3 IObserver public void Notify( string message) { Console .WriteLine("Call Observer3.Notify Method:{0}", message); } }
      
    在不修改控制台的项目源码的前提下,目前来说是没有办法的。我们可以通过反射,在所有程序集中搜索所有观察者,创建其实例并添加到Subject的Handlers中。Orchard Event Bus就用类似的方式很好地解决了这些问题。
     
    修改IObserver接口,使之实现Orchard.Events.IEventHandler接口:
         public interface IObserver : Orchard.Events. IEventHandler void Notify(string message);}
     
    在Orchard中,所有实现了IEventHandler接口的类型都将被注册到Autofac容器中(更准确地说,是所有实现了Orchard.IDependency接口的类型)。
     
    然后修改在Subject增加一个接收IEnumeralbe<IObserver>或IObserver类型的构造函数:
         public class Subject ISubject
         {
             private readonly IEnumerableIObserver> _handlers;
             public Subject(IEnumerable <IObserver> handlers)
             {
                 _handlers = handlers;
             }
             public void DoSomething()
             {
                 Console.WriteLine("Call DoSomething method." );
                 foreach(var item in _handlers)
                 {
                     item.Notify( "Hello");
                 }
             }
        }
     
    再修改控制台程序:
         public class Tester
        {
            static void Main(string[] args)
            {
                var builder = new ContainerBuilder();
                builder.RegisterModule( new EventsModule ());
                builder.RegisterType< DefaultOrchardEventBus>().As<IEventBus >().SingleInstance();
                builder.RegisterType< DefaultExceptionPolicy>().As<IExceptionPolicy >().SingleInstance();
                builder.RegisterType< Subject>().As<ISubject >();
                builder.RegisterType< Observer1>().As<IEventHandler >();
                builder.RegisterType< Observer2>().As<IEventHandler >();
                //...(通过Autofac自动装配机制,扫描程序集,对所有IEventHandler实现进行注册。此处代码略)
                var container = builder.Build();
     
                var testEventHandler = container.Resolve<ISubject>();
                testEventHandler.DoSomething();
     
            }
        }
    以上代码略去了自动注册IEventHandler的代码,Orchard是在Orchard.Environment.ShellBuilders.ShellContainerFactory的CreateContainer方法中完成这个工作的。
    相关类型:
    Orchard.Events.EventsRegistrationSource : Castle.Core.IRegistrationSource Orchard.Events.IEventHandler : Orchard.IDependency Orchard.Events.DefaultOrchardEventBus : Orchard.Events.IEventBus Orchard.Events.EventsInterceptor : Castle.Core.Interceptor.IInterceptor(Dynamic Proxy)
    Orchard.Environment.ShellBuilders.ShellContainerFactory
     
    参考资料: 观察者模式 Castle动态代理
  • 相关阅读:
    ubuntu下ssh服务相关操作
    搜索引擎的基础-倒排索引
    mysql重置密码
    mysql 服务器配置
    Activiti如何替换已部署流程图
    循环select查询结果集
    存储过程范例,有输入输出参数,带回滚
    sql server中字符串无法替换空格的问题
    Myeclipse实用快捷键总结
    Linux中添加用户与删除用户
  • 原文地址:https://www.cnblogs.com/lhxsoft/p/5322526.html
Copyright © 2020-2023  润新知