概述
采用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 <Service, IEnumerable<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 IList< IObserver> Handlers = new List< IObserver>();
        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 IEnumerable< IObserver> _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动态代理

Orchard源码分析(4.3):Orchard.Events.EventsModule类(Event Bus)的更多相关文章

  1. Orchard源码分析(5):Host相关(Orchard.Environment.DefaultOrchardHost类)

    概述 Host 是应用程序域级的单例,代表了Orchard应用程序.其处理应用程序生命周期中的初始化.BeginRequest事件.EndRequest事件等. 可以简单理解为HttpApplicat ...

  2. Spring源码分析——BeanFactory体系之抽象类、类分析(二)

    上一篇分析了BeanFactory体系的2个类,SimpleAliasRegistry和DefaultSingletonBeanRegistry——Spring源码分析——BeanFactory体系之 ...

  3. Orchard源码分析(1):Orchard架构

      本文主要参考官方文档"How Orchard works"以及Orchardch上的翻译.   源码分析应该做到庖丁解牛,而不是以管窥豹或瞎子摸象.所以先对Orchard架构有 ...

  4. Spring源码分析——BeanFactory体系之抽象类、类分析(一)

    上一篇介绍了BeanFactory体系的所有接口——Spring源码分析——BeanFactory体系之接口详细分析,本篇就接着介绍BeanFactory体系的抽象类和接口. 一.BeanFactor ...

  5. Thinkphp源码分析系列(二)–引导类

    在上一章我们说到,ThinkPHP.php在设置完框架所需要的变量和调教好环境后,在最后调用了  Think\Think::start();  即Think命名空间中的Think类的静态方法start ...

  6. spring源码分析系列 (5) spring BeanFactoryPostProcessor拓展类PropertyPlaceholderConfigurer、PropertySourcesPlaceholderConfigurer解析

    更多文章点击--spring源码分析系列 主要分析内容: 1.拓展类简述: 拓展类使用demo和自定义替换符号 2.继承图UML解析和源码分析 (源码基于spring 5.1.3.RELEASE分析) ...

  7. Netty源码分析第8章(高性能工具类FastThreadLocal和Recycler)---->第1节: FastThreadLocal的使用和创建

    Netty源码分析第八章: 高性能工具类FastThreadLocal和Recycler 概述: FastThreadLocal我们在剖析堆外内存分配的时候简单介绍过, 它类似于JDK的ThreadL ...

  8. Netty源码分析第8章(高性能工具类FastThreadLocal和Recycler)---->第2节: FastThreadLocal的set方法

    Netty源码分析第八章: 高性能工具类FastThreadLocal和Recycler 第二节: FastThreadLocal的set方法 上一小节我们学习了FastThreadLocal的创建和 ...

  9. Netty源码分析第8章(高性能工具类FastThreadLocal和Recycler)---->第3节: recycler的使用和创建

    Netty源码分析第八章: 高性能工具类FastThreadLocal和Recycler 第三节: recycler的使用和创建   这一小节开始学习recycler相关的知识, recycler是n ...

随机推荐

  1. Web性能测试基本指标

    Web性能测试基本指标 Web性能测试的部分概况一般来说,一个Web请求的处理包括以下步骤: (1)客户发送请求 (2)web server接受到请求,进行处理: (3)web server向DB获取 ...

  2. phpwind9.0模板制作教程——制作论坛风格

    由于论坛模板机制和门户等模板机制不同,所以今天我就先重点讲讲论坛模板制作的大概过程. 一.先来熟悉下phpwind9.0的论坛模板机制. 其实phpwind9.0的模板机制和discuzx2.5差不多 ...

  3. [vijos1427]机密信息

    Description 有个很奇怪的习惯,他把他所有的机密信息都存放在一个叫机密盘的磁盘分区里,然而这个机密盘中却没有一个文件,那他是怎么存放信息呢?聪明的你一定想到了,的信息都是以文件夹名称的形式保 ...

  4. 警告 “util.NativeCodeLoader: Unable to load native-hadoop library for your platform”

    http://blog.csdn.net/sagaryu/article/details/52137989 我的是2.6.4,用上面链接提供的编译好的资源覆盖原来的就好了. 不管也没事. 就是因为系统 ...

  5. 【bzoj3158】 千钧一发

    http://www.lydsy.com/JudgeOnline/problem.php?id=3158 (题目链接) 题意 给出n个装置,每个装置i有一个特征值a[i]和一个能量值b[i],要求选出 ...

  6. Linux列出安装过的程序

    命令行: dpkg -l apt-cache(模糊搜索apt-cache search 包名) pkgnames yum list(ubuntu下试了无效) rpm -aq(ubuntu下试了无效)

  7. oracle11g dataguard 安装手册(转)

    文章转自:http://www.cnblogs.com/tippoint/archive/2013/04/18/3029019.html 一.前言:   网络上关于dataguard的配置文章很多,但 ...

  8. hdu 4403 dfs

    巨麻烦的模拟题orz.... 先确定等号的位置,然后两层dfs,分别算等号前面的和后面的,再比较 话说这题竟然不开long long都能水过 #include <iostream> #in ...

  9. 关于captive portal

    portal是入口的意思,我的理解,在这里其实就是门户或者主页.captive portal,就是强制主页.校园网里面的验证通常都是通过一个网页验证来完成,不管你点要访问哪一个网站,它都会强制给你转到 ...

  10. HDU 1425 sort(堆排序/快排/最大堆/最小堆)

    传送门 Description 给你n个整数,请按从大到小的顺序输出其中前m大的数. Input 每组测试数据有两行,第一行有两个数n,m(0<n,m<1000000),第二行包含n个各不 ...