我心中的核心组件(可插拔的AOP)~第五回 消息组件

简介:

之所以把发消息拿出来,完全是因为微软的orchard项目,在这个项目里,将公用的与领域无关的功能模块进行抽象,形成了一个个的组件,这些组件通过引用和注入的方式进行工作,感觉对于应用程序的扩展性上有很大的提高,消息组件的提出是因为它的不固定性,从小方面说,项目模块的发消息的方式可能是不同的,有过模块是email,有的是数据库,有的是短信;而从大的方面说,对于项目与项目来说,它们发消息的方式也可能不同,所以,把它抽象出来,就显得很必要了。

对于一个消息来说,它的行为很固定,即发消息,Send,而考虑到网络阻塞问题,我们也同样提供了异常消息的发送,接口规范如下:

  /// <summary>
    /// Message Interface
    /// Author:Garrett
    /// </summary>
    public interface IMessageManager
    {
        /// <summary>
        /// Sends a message to a channel using a content item as the recipient
        /// </summary>
        /// <param name="recipient">A content item to send the message to.</param>
        /// <param name="type">A custom string specifying what type of message is sent. Used in even handlers to define the message.</param>
        /// <param name="service">The name of the channel to use, e.g. "email"</param>
        /// <param name="properties">A set of specific properties for the channel.</param>
        void Send(string recipient, MessageType type, string subject, string body);

        /// <summary>
        /// Sends a message to a channel using a set of content items as the recipients
        /// </summary>
        /// <param name="recipients">A set of content items to send the message to. Only one message may be sent if the channel manages it.</param>
        /// <param name="type">A custom string specifying what type of message is sent. Used in even handlers to define the message.</param>
        /// <param name="service">The name of the channel to use, e.g. "email"</param>
        /// <param name="properties">A set of specific properties for the channel.</param>
        void Send(IEnumerable<string> recipients, MessageType type, string subject, string body);

        /// <summary>
        /// Async Sends a message to a channel using a set of content items as the recipients
        /// </summary>
        /// <param name="recipients">A set of content items to send the message to. Only one message may be sent if the channel manages it.</param>
        /// <param name="type">A custom string specifying what type of message is sent. Used in even handlers to define the message.</param>
        /// <param name="service">The name of the channel to use, e.g. "email"</param>
        /// <param name="properties">A set of specific properties for the channel.</param>
        /// <param name="isAsync">is Async</param>
        void Send(IEnumerable<string> recipients, MessageType type, string subject, string body, bool isAsync);


    }

有了规范,就是实现这个规范,我们以Email为例,看一下 Email消息发送的实现代码:

/// <summary>
    /// 默认发消息服务
    /// </summary>
    public class EmailMessageManager : IMessageManager
    {

        #region Delegate & Event
        /// <summary>
        /// 加入发送队列中
        /// </summary>
        /// <param name="context"></param>
        public delegate void SendingEventHandler(MessageContext context);
        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="context"></param>
        public delegate void SentEventHandler(MessageContext context);
        /// <summary>
        /// 加入发送队列中
        /// </summary>
        public event SendingEventHandler Sending;
        /// <summary>
        /// 发送消息
        /// </summary>
        public event SentEventHandler Sent;

        void OnSending(MessageContext context)
        {
            if (Sending != null)
                Sending(context);
        }
        void OnSent(MessageContext context)
        {
            if (Sent != null)
                Sent(context);
        }
        #endregion

        #region IMessageManager 成员

        public void Send(string recipient, MessageType type, string subject, string body)
        {
            Send(new List<string> { recipient }, type, subject, body);
        }

        public void Send(IEnumerable<string> recipients, MessageType type, string subject, string body)
        {
            Send(recipients, type, subject, body, false);
        }

        public void Send(IEnumerable<string> recipients, MessageType type, string subject, string body, bool isAsync)
        {
            if (recipients != null && recipients.Any())
            {
                using (SmtpClient client = new SmtpClient()
                {
                    Host = MessageManager.Instance.Host,
                    Port = MessageManager.Instance.Port,
                    Credentials = new NetworkCredential(MessageManager.Instance.UserName, MessageManager.Instance.Password),
                    EnableSsl = false,//设置为true会出现"服务器不支持安全连接的错误"
                    DeliveryMethod = SmtpDeliveryMethod.Network,
                })
                {
                    #region Send Message
                    var mail = new MailMessage
                    {
                        From = new MailAddress(MessageManager.Instance.Address, MessageManager.Instance.DisplayName),
                        Subject = subject,
                        Body = body,
                        IsBodyHtml = true,
                    };
                    MailAddressCollection mailAddressCollection = new MailAddressCollection();
                    recipients.ToList().ForEach(i =>
                    {
                        mail.To.Add(i);
                    });
                    if (isAsync)
                    {
                        client.SendCompleted += new SendCompletedEventHandler(client_SendCompleted);
                        client.SendAsync(mail, recipients);
                    }
                    else
                    {
                        client.Send(mail);
                    }
                    #endregion
                }
            }
        }

        void client_SendCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
        {
            string arr = null;
            (e.UserState as List<string>).ToList().ForEach(i => { arr += i; });
            //发送完成后要做的事件,可能是写日志
        }

        #endregion
    }

OK,有了消息的行业,我们再来看它的主体,即消息体,一般由发送者,接受者集合,主题,正文,是否立即发送等几个参数组成,下面来分享一下:补充一句,如果消息体的实现是单一的,我们可以把行为写在消息体里,形成一个消息上下文,微软很多架构都是这样做的,如EF数据上下文DbContext,ObjectContext,linq to sql上下文DataContext,Http上下文HttpContext等等。

/// <summary>
    /// 消息实体
    /// </summary>
    public class MessageContext
    {
        /// <summary>
        /// 消息类型
        /// </summary>
        public MessageType Type { get; set; }
        /// <summary>
        /// 消息头
        /// </summary>
        public string Subject { get; set; }
        /// <summary>
        /// 消息正文
        /// </summary>
        public string Body { get; set; }
        /// <summary>
        /// 接受方地址列表
        /// </summary>
        public IEnumerable<string> Addresses { get; set; }
        /// <summary>
        /// 是否处于准备发送状态
        /// </summary>
        public bool MessagePrepared { get; set; }

        public MessageContext()
        {
            Addresses = Enumerable.Empty<string>();
        }
    }

有了主体,再来看一下主要消息的设置,我们可以使用config中的section节点来做这事,如下:

    /// <summary>
    /// Message块,在web.config中提供Message块定义
    /// </summary>
    internal class MessageSection : ConfigurationSection
    {
        /// <summary>
        /// 账号
        /// </summary>
        [ConfigurationProperty("UserName", DefaultValue = "bfyxzls")]
        public string UserName
        {
            get { return (string)this["UserName"]; }
            set { this["UserName"] = value; }
        }
        /// <summary>
        /// 密码
        /// </summary>
        [ConfigurationProperty("Password", DefaultValue = "gemini123")]
        public string Password
        {
            get { return (string)this["Password"]; }
            set { this["Password"] = value; }
        }
        /// <summary>
        /// 邮件服务器地址
        /// </summary>
        [ConfigurationProperty("Host", DefaultValue = "smtp.sina.com")]
        public string Host
        {
            get { return (string)this["Host"]; }
            set { this["Host"] = value; }
        }
        /// <summary>
        /// 端口号
        /// </summary>
        [ConfigurationProperty("Port", DefaultValue = "25")]
        public int Port
        {
            get { return (int)this["Port"]; }
            set { this["Port"] = value; }
        }
        /// <summary>
        /// 发件人的email地址
        /// </summary>
        [ConfigurationProperty("Address", DefaultValue = "bfyxzls@sina.com")]
        public string Address
        {
            get { return (string)this["Address"]; }
            set { this["Address"] = value; }
        }
        /// <summary>
        /// 发送之后,在收件人端显示的名称
        /// </summary>
        [ConfigurationProperty("DisplayName", DefaultValue = "占占")]
        public string DisplayName
        {
            get { return (string)this["DisplayName"]; }
            set { this["DisplayName"] = value; }
        }

    }

config中的配置信息如下:

<configuration>
  <configSections>
    <section name="MessageSection" type="Messaging.MessageSection"/>
  </configSections>
  <MessageSection UserName="853066980" Password="gemini123" Host="smtp.qq.com" Port="25" Address="853066980@qq.com" DisplayName="大占"></MessageSection>
</configuration>

OK,我们再来看一下,消息的类型,目前可以定义两种消息,当然,为了组件的扩展性,你的类型可以通过IoC去代替它,这样可以避免程序中的硬编码,维护更方便

    /// <summary>
    /// 消息类型
    /// </summary>
    public enum MessageType
    {
        /// <summary>
        /// 电子邮件
        /// </summary>
        Email,
        /// <summary>
        /// 短信息
        /// </summary>
        ShortMessage,

    }

最后来看一下生产消息的对象,使用了简单工厂模式,当然这只是个例子,实现中,它应该是使用IoC动态生产的,呵呵。

    /// <summary>
    /// 消息生产者
    /// </summary>
    public class MessageFactory
    {
        /// <summary>
        /// 消息对象
        /// </summary>
        public static IMessageManager Instance
        {
            get
            {
                MessageType messageType = MessageType.Email;//通过配置产生
                switch (messageType)
                {
                    case MessageType.Email:
                        return new EmailMessageManager();
                    case MessageType.ShortMessage:
                        throw new NotImplementedException("没实现呢,呵呵");
                    default:
                        throw new ArgumentException("您输入的参数有误");
                }
            }
        }
    }

本文转自博客园张占岭(仓储大叔)的博客,原文链接:我心中的核心组件(可插拔的AOP)~第五回 消息组件,如需转载请自行联系原博主。

目录
相关文章
|
3月前
|
Java Spring
Spring的AOP组件详解
该文章主要介绍了Spring AOP(面向切面编程)组件的实现原理,包括Spring AOP的基础概念、动态代理模式、AOP组件的实现以及Spring选择JDK动态代理或CGLIB动态代理的依据。
Spring的AOP组件详解
|
JSON API 数据格式
.net core工具组件系列之Autofac—— 第一篇:Autofac系列Autofac的几种常见注册方式、生命周期和AOP
使用Autofac进行服务注册实践:新建三个项目,分别是webapi项目 Wesky.Core.Autofac以及两个类库项目 Wesky.Core.Interface和Wesky.Core.Service。在Webapi项目下,引用Autofac的三个包:Autofac、Autofac.Configuration和Autofac.Extensions.DependencyInjection 。
1328 1
.net core工具组件系列之Autofac—— 第一篇:Autofac系列Autofac的几种常见注册方式、生命周期和AOP
|
缓存 Java Spring
【小家Spring】Spring AOP各个组件概述与总结【Pointcut、Advice、Advisor、Advised、TargetSource、AdvisorChainFactory...】(上)
【小家Spring】Spring AOP各个组件概述与总结【Pointcut、Advice、Advisor、Advised、TargetSource、AdvisorChainFactory...】(上)
【小家Spring】Spring AOP各个组件概述与总结【Pointcut、Advice、Advisor、Advised、TargetSource、AdvisorChainFactory...】(上)
|
Java Spring 容器
【小家Spring】Spring AOP各个组件概述与总结【Pointcut、Advice、Advisor、Advised、TargetSource、AdvisorChainFactory...】(下)
【小家Spring】Spring AOP各个组件概述与总结【Pointcut、Advice、Advisor、Advised、TargetSource、AdvisorChainFactory...】(下)
|
安全 Java Spring
【小家Spring】Spring AOP各个组件概述与总结【Pointcut、Advice、Advisor、Advised、TargetSource、AdvisorChainFactory...】(中)
【小家Spring】Spring AOP各个组件概述与总结【Pointcut、Advice、Advisor、Advised、TargetSource、AdvisorChainFactory...】(中)
|
中间件 Java Spring
在.NET Core中三种实现“可插拔”AOP编程方式(附源码)
一看标题肯定会联想到使用动态编织的方式实现AOP编程,不过这不是作者本文讨论的重点。 本文讨论另外三种在netcore中可实现的方式,Filter(过滤器,严格意义上它算是AOP方式),DynamicProxy(动态代理方式,JAVA上早已不是新鲜事),Middleware(netcore中间件所实现的AOP方式)   什么是AOP编程 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
5736 0
|
微服务
【微服务No.3】AOP组件ASPectCore简单使用
介绍: AspectCore是.NET标准的基于AOP的跨平台框架【github解释】。主要支持:对方面拦截器,依赖注入集成,Web应用程序,数据验证等的核心支持。 使用实例: 首先安装dll: Install-Package AspectCore.
2027 0
|
SQL XML 前端开发
bboss aop/ioc组件配置语法诠释
     这篇文章全面介绍bboss ioc 配置语法,希望对正在使用bboss或者即将使用bboss或者想了解bboss的朋友有所帮助;希望有助于大家了解和认识bboss,如果有不妥请大家批评指正。
1146 0
|
2月前
Micronaut AOP与代理机制:实现应用功能增强,无需侵入式编程的秘诀
AOP(面向切面编程)能够帮助我们在不修改现有代码的前提下,为应用程序添加新的功能或行为。Micronaut框架中的AOP模块通过动态代理机制实现了这一目标。AOP将横切关注点(如日志记录、事务管理等)从业务逻辑中分离出来,提高模块化程度。在Micronaut中,带有特定注解的类会在启动时生成代理对象,在运行时拦截方法调用并执行额外逻辑。例如,可以通过创建切面类并在目标类上添加注解来记录方法调用信息,从而在不侵入原有代码的情况下增强应用功能,提高代码的可维护性和可扩展性。
61 1
|
15天前
|
安全 Java 编译器
什么是AOP面向切面编程?怎么简单理解?
本文介绍了面向切面编程(AOP)的基本概念和原理,解释了如何通过分离横切关注点(如日志、事务管理等)来增强代码的模块化和可维护性。AOP的核心概念包括切面、连接点、切入点、通知和织入。文章还提供了一个使用Spring AOP的简单示例,展示了如何定义和应用切面。
50 1
什么是AOP面向切面编程?怎么简单理解?