精华内容
下载资源
问答
  • Java企业应用架构实践

    千人学习 2018-05-09 22:04:22
    基于Servlet、JSP、JDBC 一步一步敲代码的方式,实现一个企业应用架构实例,学员可以深层次理解Java架构的内涵
  • 当下微服务的实践方案中,Spring Cloud,Dubbo作为主流的落地方案,在企业应用架构中发挥越来越重要的作用。本文探讨企业应用架构如何从微服务架构向Service Mesh架构演化,并形成落地方案。需要特别说明:本文讨论...

    Alt
    作者:李宁

    来源:博云技术社区 / 博云研究院

    当下微服务的实践方案中,Spring Cloud,Dubbo作为主流的落地方案,在企业应用架构中发挥越来越重要的作用。本文探讨企业应用架构如何从微服务架构向Service Mesh架构演化,并形成落地方案。需要特别说明:本文讨论的架构目前适用于普通的企业级应用,其他行业(例如互联网)需要进一步扩展。

    在讨论之前,我们需要明确一个事实:企业应用一定是围绕业务进行的。 无论采用什么的架构落地,都是为了更好的为应用业务进行服务。从企业应用的特性考虑,主要包括:稳定性,安全性,扩展性,容错性。

    围绕着企业应用的这些特点,我们来看一个典型的微服务企业架构模型,如图所示:

    Alt

    • 服务接入层: 企业暴露到外部访问的入口,一般通过防火墙等。
    • 网关层: 服务网关是介于客户端和服务端的中间层,所有的外部请求会先经过服务网关,为企业应用提供统一的访问控制入口。服务网关是微服务架构下的服务拆分,聚合,路由,认证以及流控综合体现。
    • 支撑服务层: 为企业应用提供运行所需的支撑环境,包括注册发现,集中配置,容错限流,认证授权,日志聚合,监测告警,消息服务等
    • 业务服务层: 业务服务是企业应用的核心所在,为企业领域应用的具体实现,一般进一步拆分为基础服务(基础功能)和聚合服务(综合场景)。
    • 平台服务层: 为企业应用提供运行所需的软件资源,包括应用服务器,应用发布管理,应用镜像包管理,服务治理。
    • 基础设施层: 为企业应用提供运行所需的硬件资源,包括计算资源,网络资源,存储资源,基本的安全策略控制等。

    从这个典型的服务架构体系中,能够清晰的表明层级架构以及各层涵盖的职责说明。我们暂不考虑基础设施层和平台服务两层,重点关注网关服务,业务服务,支撑服务,突出其中的一些基础支撑功能组件,这也是我们本篇探讨的重点内容。如下图所示:

    Alt
    根据图中红色标识,我们会发现这样一个事实:在微服务架构下,无论是哪种落地实现方式,都集中在网关服务、支撑服务两个层面。 无论是Spring Cloud“套装组件”,Dubbo“套件”还是其他开源组件,都为支撑服务的实现提供了数量众多的选择。功能完整、选择性多这是业内喜闻乐见的事情,但是也无形中增加了开发,测试,运维人员的压力。大家需要掌握越来越多的“使用工具”以更“方便”、“快捷”地应对业务服务。有时候,可能为了实现单一功能,而必须引入一堆组件,这时候我们希望能够有一个完整的平台来为应用业务提供一体化的支撑服务,而不是一系列“套装组件”与业务的集成。

    那么如何基于一个平台来实现这些企业应用需要的能力呢?经过一定阶段的技术调研,我们认为Service Mesh能够帮助我们初步达到这个目标。

    我们都知道Service Mesh以解决“服务通信”的问题作为其设计初衷,聚焦基础设施“网络层”,并以此做技术基础,解决业务通信场景面临的问题。那么如何把它应用在企业应用架构中来取代“微服务套装组件”呢? 那接下来让我们针对网关服务,业务服务,支撑服务分别来看一下,如何从原来的微服务“套装组件”中抽离出来,实现Service Mesh方向的转变。

    网关服务

    前面提到过:服务网关是介于客户端和服务端的中间层。从功能上不难理解,对内屏蔽内部细节,对外提供统一服务接口。从场景聚焦角度考虑,网关根据不同的场景承载不同的职责,包括认证,授权,路由,流控,负载等。(之前我们也聊过网关组件的对比及具体实现,感兴趣的同学可点击微服务五种开源API网关实现组件对比)。

    由此可见,服务网关是企业应用架构下一些列功能的综合体现。那么在Service Mesh情况下如何处理网关服务呢?在展开之前首先需要说明一个前提:目前为止Service Mesh跟真正企业网关相比还存在一定的不足之处,例如“协议转化”,“安全策略”,“性能要求”等方面。在这里我们也是探讨这样的可能性。下面以Istio为例,我们来看一下,如何提供网关层面的服务。

    Istio在网关层面提供两种类型的网关服务:Ingress Gateway,Egress。

    Ingress Gateway

    Ingress Gateway用于接收传入的HTTP/TCP连接,它配置暴露端口,协议供外部统一接入,但是自身不提供任何的路由配置,而是完全依赖 Istio 的控制规则来进行流量路由。从而与内部服务请求统一到同一个控制层面上。

    Egress

    在企业应用与外部应用之间,有时候为了业务需要会出现内部服务调用外部服务的情况,此时一般会从企业内部接入第三方网关来获取服务数据。在 Isito 中你同样可以基于Egress来达到目的。Isito中提供两种方式:一种基于ServiceEntry + VirtualService的配置,实现第三方服务的访问,一种扩大sidecar的访问地址列表。(参考文档:https://preliminary.istio.io/zh/docs/tasks/traffic-management/egress/)。

    基于上述两种场景,我们可以看出,在 Service Mesh 的体系下,网关层面变成一个可以动态生成和销毁的组件,能够通过控制层面实现统一规则管理,并且实时生效

    基于Service Mesh的网关服务如下图所示:

    Alt
    从实现原理上分析,传统的网关实现基于 Servlet 的 filter 的模式,实现服务请求转移过程中的层层过滤和处理。区别在于采用同步或者异步处理机制,用来解决网关的性能瓶颈。而Service Mesh的网关完全是基于网络代理的请求转发与控制,本质上作用在服务的 Iptables 上,通过对 Iptables 的规则控制达到同样的效果。

    业务服务

    业务是企业应用的“重中之重”,无论哪种企业架构,最终都是为了更好地为业务提供服务,那么我们如何在Service Mesh的体系下,重构业务服务呢?我们以两个简化的服务调用来说明整个架构的转变过程。

    假如要实现服务A,服务B的相互调用,最原始的方式是服务A基于协议层直接调用服务B(这里暂时忽略高可用,多副本,负载均衡的方式),如图所示:

    Alt
    由图可见,服务A基于某种协议完成对服务B的请求,相对比较简单。但是我们知道这样虽然能够快速完成业务关联,但是无法确保业务正常稳定的运行,因此我们需要引入更多的服务来保证业务的稳定,可靠,可控。此时我们最容易想到的是引入微服务的支撑组件来达到目标。

    以Spring Cloud方案为例,我们来说明当前微服务架构的实现方式。为了满足企业应用对服务A,服务B的管理控制,需要额外引入“注册中心”,“网关”,“配置中心”,“服务监测”,“事件消息”,“链路跟踪”,“日志服务”等众多与直接业务无关的“旁路保障服务”,简化一下,如下图所示:

    Alt
    从图中可以看出,每个服务都引入了大量与业务无关的“保障服务”,这些“旁路保障服务”消耗的资源,与比业务本身消耗的资源成“倍数关系”。随着服务数目的增多,业务服务本身占用的资源比会越来越少,此时开发人员会把大量的精力花费在维护这些“旁路保障服务”上,而忽略业务本身。这对于企业应用而言,有些本末倒置的意思。

    我们再来看一下 Service Mesh 体系下,我们如何解决上述问题。Service Mesh为了解决企业应用的“通信问题”重点做了四个方面的工作,以 Istio 为代表,提供了包括流量管理,安全配置,策略控制以及外围组件支撑的遥测功能(需要的朋友,可以参考官方文档:https://preliminary.istio.io/zh/docs),在Service Mesh的架构下,服务A调用服务B的架构会变成下图所示:

    Alt
    通过上图我们可以发现,与Spring Cloud的实现方式相比,似乎简单了很多,我们不再需要在业务服务中引入众多的“旁路保障服务”,而是保障了业务服务本身的简单化。那么Service Mesh是如何处理的呢?

    第一,引入了Sidecar代理模型,作为服务流量控制的入口和出口,保证能够对网络层面数据做实时监控和调整;
    第二,控制器根据具体业务情况,分发控制状态和控制指令,Sidecar获取控制信息后,及时更新缓存信息,保证策略有效性。
    第三,Sidecar由于能够拦截所有请求的流量信息,定期把收集的数据向控制层进行上报,从而完成服务状态和应用链路的监测。
    第四,所有的这些都是在应用部署阶段完成,在开发层面不需要花费大量的精力。

    基于以上四点我们可以发现,Service Mesh 仅仅通过方式转变就达到了同样的效果,还极大的解放了开发人员。

    通过业务服务调用方式的实现转变,我们发现这样一个事实:Service Mesh在保证业务简化有效的同时,进一步屏蔽了多种开发语言带来的障碍。它完全基于网络层面和协议层面作为出发点,达到“以不变应万变”的效果。

    支撑服务

    从企业业务的价值角度考虑,其实支撑服务更多属于“资源消耗”品,虽然如此,它却是企业应用架构不可或缺的一部分。从单一的业务调用—>微服务体系业务调用—>Service Mesh 体系业务调用的转变方式中,可以看出支撑服务处于一个层级不断下降的过程。而依赖于ServiceMesh的定位,最终一些支撑服务会作为基础设施的形态呈现出来,这也是未来发展的趋势所在。支撑服务在企业架构的形态转变如图所示:

    Alt

    • 传统架构:传统架构下,支撑服务,业务服务基本上没有明确的边界区分,实现方式上都通过代码杂糅在一起。
    • 微服务架构:微服务架构下,支撑服务,业务服务能够初步分离,但是需要保证代码框架的统一性和依赖性,跨语言受限比较严重。
    • Service Mesh架构:Service Mesh架构下,支撑服务,业务服务能够彻底分离,不收语言限制,唯一需要考虑的是不同协议的支持情况。

    通过支撑服务的转变形态可以看出,支撑服务与业务服务分离是必然趋势,而最终的受限取决于多元化的网络协议的处理分析能力。 当然我们需要明确一个事实:就Service Mesh目前的发展趋势和定位而言,并不能够完全取代所有的支撑服务,例如事件消息,配置管理等。我们更多期望它能够帮助解决应用服务在网络层面需要面对的场景和问题。这也是它发挥价值的地方所在。

    通过对网关服务,业务服务,支撑服务在不同体系架构下的转变,我们清晰的认识到Service Mesh能够帮助我们重点解决微服务体系下繁琐的“旁路支撑”服务,保证业务服务的简单有效性。通过演化分析,最终基于Service Mesh的企业应用架构如下图:

    Alt
    从图中可以看到 Service Mesh 架构下重点做了三件事情:

    1.网关层的接入工作,无论是外部请求接入,还是内部服务请求转发,都可以基于 Service Mesh 提供的不同类型的 gateway 实现,同时还可以保证策略的统一控制和管理。省略了独立的网关管理控制台。

    2.针对业务服务,增加了 Sidecar 的代理模型,用来处理所有的入站和出站流量,并且配合支撑服务的控制策略,实现业务服务的旁路控制功能。

    3.统一面向网络的支撑服务,基于控制与数据分离的思想,根据业务的运行情况,提高企业应用运行过程中的动态控制能力。

    同样我们也意识到,利用 Service Mesh 处理服务通信的能力,替换需要不同组件支撑的“注册发现”,“容错限流”“认证授权”“日志搜集”,“监控告警”“流量控制”等功能。在减少组件代码开销的同时,将企业应用的支撑服务进一步下移。无论是开发人员,还是领域专家,可以集中精力用来处理应用业务,而不用在维护第三方的不同的功能组件上“浪费时间”。而业务运维人员,通过 Service Mesh 的控制平台,能够实时监测企业服务的运行状态,而不需要向之前那样花费精力维护不同的工具和组件。

    最后让我们一起讨论一下,Service Mesh是如何做到这些的。Service Mesh 本质上并没有采用任何技术上的创新,更多是思想层面的变革。 我们认为有几个转变是需要提出来的:

    • 层级转变:Service Mesh在设计思路上,把自己不再定位成企业应用组件,而是把自己下沉到基础设施层,成为基础设施的一部分,这样层级的转变就与企业业务本身划清楚界限。
    • 方式转变:Service Mesh在实现思路上,高度抽象,聚焦于通信链路本身,而不是聚焦于组件功能上,从网络层入手,抓住了服务通信交互的本质。
    • 控制转变:Service Mesh将控制和实现分离,提供统一,灵活的控制入口,能够快速方便的针对业务场景进行自定义处理。
      最后的最后需要说明 Service Mesh 还不完善,还有很多问题需要在实际的企业应用过程中逐步去解决,让我们一起拭目以待吧。

    点击【阅读】,立即体验微服务平台

    欢迎点击“京东云”了解更多精彩内容

    Alt
    Alt

    展开全文
  • 关系,本篇可能主要是简单的介绍下企业应用的几类模式,结合这几个分层直接的交互来完成系统功能的构建。我们还是先对我们学习的四个分层的职责和功能做个大 概的回顾,我们先来看看下图来回顾下我们讲述的内容。
    一、上篇回顾

          我们先来回顾下上篇讲解的内容,我们前面的几节分别讲述了,业务逻辑层、数据访问层、服务层、表现层,我们了解了这些分层的职责和分层之间的大概的关联

    关系,本篇可能主要是简单的介绍下企业应用的几类模式,结合这几个分层直接的交互来完成系统功能的构建。我们还是先对我们学习的四个分层的职责和功能做个大

    概的回顾,我们先来看看下图来回顾下我们讲述的内容。

          image

          我想通过上图,大家能回忆起我们讲述的相关内容,然后整理好自己的思路,我们本文将会针对这几个分层进行相应的模式的讲解,并且会结合实例来说明企业应

    用架构的简单应用。我想这也是大家关心的内容,我也希望大家能多提出宝贵意见,大家共同提高。

          之前说是提供PDF文件的下载的,以提供给需要学习的朋友更方便的形式,但是由于最近时间有限,没能整理好,等过阵子提供一个完整的整合好的PDF版本,提

    供给大家下载,还望大家见谅。

    二、开篇

          本篇我们将针对我们前面讲述的几个分层做个简单的整合,就是通过一个简单的实例代码来说明下,我们给出的一个可能的企业应用架构模式去完成企业应用,并

    且顺带分析下,企业应用中可能存在的瓶颈等,当然可能本章只是点出一些概念,然后在后面的章节中去完成,比如我们的性能优化,性能瓶颈等,这些我们后面通过

    这篇:系统架构师-基础到企业应用架构-性能优化(架构瓶颈)(后续篇)详细的分析。当然由于本章主要是基于前面讲述的内容的一个整合和分析,可能部分观点

    还有更好的改进方案,还希望大家多提出宝贵意见和您的建议,谢谢!那么我们先来看看我们本章讲述的内容吧:

          image

          本章主要讲述下各分层之间的交互方式及可行的设计方案,并且分析我们在每个分层采用什么样的模式,及给出相应的示例代码,当然这里可能讲述的不会是最好

    的实现方案,还期待大家提出更好的改进方案。

    三、本文提纲

          1、内容回顾

          2、开篇

          3、本文提纲

          4、企业应用架构实例

                4.1、企业应用架构中的物理分层

                4.2、数据访问层分析

                4.3、业务逻辑层分析

                4.4、服务层分析

                4.5、表现层分析

                4.6、分析总结

          5、结束语

          6、系列进度

          7、下篇预告

    四、企业应用架构实例
             4.1、企业应用架构的物理分层

          本节将会分层的相关介绍,并且分析分层的原因,为下篇:系统架构师-基础到企业应用架构-分层[上篇] 做铺垫。

         分层我想对大家来说都不会太陌生。因为我们平时在开发的过程中一般都是采用分层架构的方式,通过分层将系统的功能进行划分,我们将系

    统中的若干有共同特征的部分放在一个分层中,然后通过分层之间的交互去完成系统的功能。

         我们看看企业应用的几种应用程序模式:

          image

        1、单击应用程序就是一些本地服务的应用程序,这个应该没啥特别的难度,例如Office应用程序,当然现在有在线版本的Office,这个应用的太多了。

        2、客户端/服务器的形式,特别是在WinForm、WPF、SilverLight方面的应用等,例如我们的常用的OutLook,虽然不是采用.NET平台开发的,但是原理一样就

    是通过客户端通过通信服务来访问服务器,然后取回相应的邮件信息返回给客户端,相当于我们通过客户端的形式来访问Web服务。

        3、Web服务:通过将开发的Web服务器部署在服务器上,然后我们就可以通过输入HTTP网址的形式来访问web服务,我们这里是通过浏览器来完成的,其实这个

    模式也是客户端/服务器的形式。只不过这时的客户端变成浏览器+服务页面,然后通过浏览器来完成Web服务的访问。

        4、其他服务:我们这里主要是针对一些其他的设备,例如通信设备等方面的访问,比如说我们现在提供一个卫星定位的服务,那么通过设备调用服务来完成,告知

    用户的GPS定位信息等。很多这个方面的应用。这个方向应该是未来的一个主流吧。

        我们上面简单的讲述了系统的物理分层的形式,那么我们来看看如何对系统的功能进行分层,也就是我们下面要详细讲解的实例分析。

           4.2、数据访问层分析

         数据访问层我们知道是唯一一个可以与数据库之间直接进行交互的分层,数据访问层必须满足的几个功能和职责,我们这里就不复述了,我们本篇可能就是直接给

    出数据访问层的相关实现,并且分析出数据访问层与其他层之间的交互。

          image 大家可能一看就知道是什么意思,可能在您的眼中应该就和你们平时项目中采用的分层结构有点类似吧。

          我这里的数据访问层提供所有的数据库访问服务。我们来看看基本的服务功能吧

          我们这里定义了一个DDL语句操作的枚举,CUD的枚举,因为我们提供了一个统一的数据访问服务,通过Excute来完成。

    ?
    /// <summary>
    /// 数据访问操作类型
    /// </summary>
    public enum DDLType
    {
        Create,
        Update,
        Delete
    }

         这里用枚举来定义数据库字段与查询条件值之间的关系

    ?
    /// <summary>
    /// 数据字段与字段值之间的关系表达式
    /// </summary>
    public enum FieldExpressionType
    {
        EquleTo,
        BeginThen,
        LessThen,
        BetweenAnd,
        Like
    }

        我们再来看看我们定义的查询条件的接口

    ?
    /// <summary>
    /// 定义子查询接口
    /// </summary>
    public interface ICondition
    {
        int Add(string fieldName,object value,FieldExpressionType fetType);
        int Add(ICondition condition);
     
        string WhereCondition;
        string OrderCondition;
    }

        数据访问层接口代码

    ?
    /// <summary>
    /// 定义统一的数据访问服务
    /// </summary>
    public interface IDataAccess
    {
        #region CUD
     
        int Create<T>(T item);
     
         int Update<T>(T item);
     
         int Delete<T>(T item);
     
         int Execute<T>(T item,DDLType ddlType);
     
        #endregion
     
        #region Read服务
     
         List<T> Query<T>(ICondition condition);
     
         T GetModelByPrimaryKey<T>(object key);
     
         List<T> GetAll<T>();
     
         #endregion
     
        #region 事务操作
     
         void BeginTransaction();
     
         bool Commit();
     
         bool RollBack();
     
         bool IsTransaction;
     
         #endregion
    }

        我们知道具体的代码我们是通过反射+特性的形式来完成数据库操作语句的生成的,我们来看看可行的代码,属性项特性的定义。

    ?
    /// <summary>
    /// Model中的字段属性特性
    /// </summary>
    [AttributeUsage(AttributeTargets.All, AllowMultiple = false)]
    public class PropertyAttribute : Attribute
    {
        private string dbColumnName;
        private bool isPrimary;
        private DbType dbType;
        private object defaultValue;
        private bool isIdentify;
        private int length;
     
        public string DbColumnName
        {
            get
            {
                return this.dbColumnName;
            }
            set
            {
                this.dbColumnName = value;
            }
        }
     
        public bool IsPrimary
        {
            get
            {
                return this.isPrimary;
            }
            set
            {
                this.isPrimary = value;
            }
        }
     
        public bool IsIdentify
        {
            get
            {
                return this.isIdentify;
            }
            set
            {
                this.isIdentify = value;
            }
        }
     
        public DbType DbType
        {
            get
            {
                return this.dbType;
            }
            set
            {
                this.dbType = value;
            }
        }
     
        public object DefaultValue
        {
            get
            {
                return this.defaultValue;
            }
            set
            {
                this.defaultValue = value;
            }
        }
     
        public int DbLength
        {
            get
            {
                return this.length;
            }
            set
            {
                this.length = value;
            }
        }
        public PropertyAttribute(string dbName, bool isPrimery, DbType type,object dValue)
        {
            this.dbColumnName = dbName;
            this.isPrimary = isPrimery;
            this.dbType = type;
            this.defaultValue = this.GetDefaultValue();
        }
     
        private object GetDefaultValue()
        {
            return new object();
        }
     
        public PropertyAttribute(string dbName)
        {
            this.dbColumnName = dbName;
            this.isPrimary = false;
            this.dbType = DbType.String;
            this.defaultValue = this.GetDefaultValue();
        }
     
        public PropertyAttribute(string dbName,bool isPrimery)
        {
            this.dbColumnName = dbName;
            this.isPrimary = isPrimery;
            this.dbType = DbType.String;
            this.defaultValue = this.GetDefaultValue();
        }
     
        public PropertyAttribute(string dbName, bool isPrimery, DbType type)
        {
            this.dbColumnName = dbName;
            this.isPrimary = isPrimery;
            this.dbType = type;
            this.defaultValue = null;
        }
    }

        我们再来看看基于表上的特性

    ?
    /// <summary>
     /// 基于表的自定义特性类
     /// </summary>
    [AttributeUsage(AttributeTargets.All, AllowMultiple = false)]
    public class TableAttribute : Attribute
     {
         private string dbTableName;
         public TableAttribute(string dbName)
         {
             this.dbTableName = dbName;
         }
     
         public string TableName
         {
             get
             {
                 return this.dbTableName;
             }
             set
             {
                 this.dbTableName = value;
             }
         }
     }
     
         根据反射取出表名
     
         /// <summary>
         /// 返回Model对应的数据库表名
         /// </summary>
         /// <typeparam name="T"></typeparam>
         /// <param name="model"></param>
         /// <returns></returns>
         public string DbTableName<T>(T model)
         {
             string dbName = string.Empty;
             DPM.Common.TableAttribute attr = null;
     
             object[] attributes = model.GetType().GetCustomAttributes(typeof(DPM.Common.TableAttribute), true);
     
             if (attributes.Length > 0)
             {
                 attr = (DPM.Common.TableAttribute)attributes[0];
             }
     
             if (attr != null)
                 dbName = attr.TableName;
     
             return dbName;
         }
     
         根据反射取出表中的列
     
        /// <summary>
        /// 动态创建表中字段列表
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="model"></param>
        /// <returns></returns>
        public string InitDbColumns<T>(T model)
        {
            StringBuilder commandBuilder = new StringBuilder();
     
            DPM.Common.PropertyAttribute attr = null;
     
            foreach (PropertyInfo property in model.GetType().GetProperties())
            {
                object[] attributes = property.GetCustomAttributes(typeof(DPM.Common.PropertyAttribute), true);
                if (attributes.Length > 0)
                {
                    attr = (DPM.Common.PropertyAttribute)attributes[0];
                }
     
                if(commandBuilder.length>0)
     
                      commandBuilder.Append(“,”);
     
                commandBuilder.Append(attr.DbColumnName);
            }
     
            return commandBuilder.ToString();
        }

           当然这里只是给出了简单的示例,我们来看看生成的Insert 语句的格式吧

    ?
    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
    47
    48
      /// <summary>
      /// 动态创建表中字段更新列表
    /// </summary>
     /// <typeparam name="T"></typeparam>
     /// <param name="model"></param>
     /// <returns></returns>
     public string InitInsertColumns<T>(T model)
     {
         StringBuilder filedBuilder = new StringBuilder();
         StringBuilder valueBuilder = new StringBuilder();
         StringBuilder commandBuilder = new StringBuilder();
     
         DPM.Common.PropertyAttribute attr = null;
     
         foreach (PropertyInfo property in model.GetType().GetProperties())
         {
             object[] attributes = property.GetCustomAttributes(typeof(DPM.Common.PropertyAttribute), true);
             if (attributes.Length > 0)
             {
                 attr = (DPM.Common.PropertyAttribute)attributes[0];
             }
             if (attr.DbColumnName == "")
                 continue;
             if (attr.IsIdentify)
                 continue;
             if (filedBuilder.Length > 0)
             {
                 filedBuilder.Append("," + attr.DbColumnName);
                 valueBuilder.Append("," + property.GetValue(model, null));
             }
             else
             {
                 filedBuilder.Append(attr.DbColumnName);
                 valueBuilder.Append(property.GetValue(model, null));
             }
     
         }
     
         commandBuilder.Append(SqlDMLText.LKH);
         commandBuilder.Append(filedBuilder.ToString());
         commandBuilder.Append(SqlDMLText.RKH);
         commandBuilder.Append(SqlDMLText.VALUES);
         commandBuilder.Append(SqlDMLText.LKH);
         commandBuilder.Append(valueBuilder.ToString());
         commandBuilder.Append(SqlDMLText.RKH);
     
         return commandBuilder.ToString();
     }

           其他的语句类似了,这部分详细的代码我们会在ORM篇详细的讲述,并且对反射的性能通过优化来提供性能。

           我们来总结下数据访问层应该有的功能吧。我认为应该提供以下的功能

           image 这样的一个数据访问层基本上能够满足功能的需要。当然我这里就不把代码全部贴出来了,太多

    了,我们接下来讲讲业务逻辑层吧。

              4.3、业务逻辑层分析

          上篇我们贴出来了一些比较典型的数据访问层的代码,当然没有上全部的代码,本文将会提供demo实例下载,大家可以参考其中的部分代码。我们来看看业务层

    中的每个业务对象应该具有什么样的智能呢?我们知道我们这里不建议业务对象直接来访问数据访问层,那么我们如何实现持久化透明的方式呢?我记得之前我讲述业

    务逻辑层的时候也提到这个方面的要求了,还有不少热心的园友问我如何实现,我这里给出几个可能的办法,当然如果有更好的办法,还请大家多多提出。我这里给出

    个简单的业务对象的职责,当然我这里给出的职责是我理解的在我的这个实例中业务对象应该具有的职责。

          接下来我们看看业务逻辑层的职责吧,然后我们给出示例代码

          image

          我们来举例说明一个业务对象可能具有的功能吧?我们这里以还是以B2C系统为例吧,我们来说说订单的可能行为吧:

    ?
    /// <summary>
    /// 订单
    /// </summary>
     public class Order
    {
         /// <summary>
         /// 产品列表
         /// </summary>
         private List<Product> products = new List<Product>();
     
         /// <summary>
         /// 获取订单的金额
         /// </summary>
         /// <param name="order"></param>
         /// <returns></returns>
         public decimal GetOrderCount(Model.Order order)
         {
             decimal totalCount = 0;
             foreach (Product product in products)
             {
                 totalCount += product.Cash;
             }
     
             return totalCount;
         }
     
         /// <summary>
         /// 获取该订单下的所有产品
         /// </summary>
         /// <param name="order"></param>
         /// <returns></returns>
         public List<Product> GetProduct(Model.Order order)
         {
             return new List<Product>();
         }
    }

         当然这里面的取得与订单相关的产品列表,因为我们没有设计在DTO中默认包含这个属性,那么我们这里其实可以考虑增加延迟加载的形式,一旦加载后,也可以

    第一次加载后,缓存起来,下次使用的过程中直接取出来。

          我们来看看与这个订单业务逻辑相关的服务层代码

    ?
    /// <summary>
    /// 订单服务
    /// </summary>
    public class OrderService
    {
        private List<Model.Product> products = new List<Model.Product>();
     
        /// <summary>
        /// 持久化透明的方式
        /// </summary>
        /// <param name="order"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public int Execute(Model.Order order,DAL.DDLType type)
        {
            return DAL.SQLServer.Instance.Execute(order, type);
        }
     
        /// <summary>
        /// 获取订单的总金额
        /// </summary>
        /// <param name="order"></param>
        /// <returns></returns>
        public decimal GetOrderCash(Model.Order order)
        {
            BLL.Order = new BLL.Order(order);
     
            return order.GetOrderCount();
        }
     
        /// <summary>
        /// 保存订单信息
        /// </summary>
        /// <param name="order"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public int SaveOrder(Model.Order order, DAL.DDLType type)
        {
            int iAff = 0;
            //将与订单相关的产品信息也同步进行保存。
            //我们这里通过事务的方式进行处理
            try
            {
                DAL.SQLServer.Instance.BeginTransaction();
     
                //具体的逻辑
                //插入产品信息
                foreach (Model.Product product in products)
                {
                    DAL.SQLServer.Instance.Create(product);
                    iAff++;
                }
     
                //插入订单信息
               iAff+= DAL.SQLServer.Instance.Create(order);
     
                DAL.SQLServer.Instance.Commit();
                return iAff;
     
            }
            catch
            {
                DAL.SQLServer.Instance.RollBack();
                return 0;
            }
            finally
            {
                DAL.SQLServer.Instance.Dispose();
            }
        }
    }

          通过上面的代码我们可以看出,目前的业务逻辑层中的业务对象只是处理自身的相关业务逻辑,通过服务层与数据层进行交互,然后业务对象并不关系自身的

    CRUD的业务,而是通过服务层来完成的,那么对于CUD我想大家应该没有什么争议,因为持久化透明只是说把业务逻辑层原本通过继承或者是实现接口,或者依赖注

    入的形式来完成持久化的操作。

          我们这里则是将这样的持久化操作提出来放在服务层来完成,这样可以降低业务层与数据访问层的依赖,那么大家肯定关心如何实现业务逻辑对象的查询服务呢?

    我的思路是这样的,大家请看看是否合理,或者您有好的思路或者想法一定要告知我,不甚感激!

          image

          不知道我们上面的上图是否表述的很清楚,还请大家多多的批评指出。当然上述的模式,可能在实际的企业应用中还是有点困难的,还有一种业务逻辑层的持久化

    透明实现。就是通过AOP来完成,或者是通过代理类来实现。这里说心里话,我对AOP的具体实现并没有研究过,但是通过动态注入的方式的确可以实现这个功能。具

    体的实现方式就交给大家来完成了。

           4.4服务层分析

          服务层作为协调业务对象进行相应的业务流的控制,当然服务层只是协调业务对象之间的交互,并不是负责具体的业务逻辑,这里的服务层应该是只负责业务对象

    之间的交互。当然我这里还把部分对数据完整性,数据类型等方面的内容放在服务层来做。下面来说明服务层可能包含的功能:

          image

          我们在业务逻辑层中也贴出了部分代码。当然服务层中其实还可以有很多的内容,比如通过我们在前面的服务层讲解时介绍的几个模式,我们也都是可以在服务层

    中应用的。当然我们这里的实例代码可能就不会贴出这几个模式了,我们这里举例来说明服务层中的部分服务代码。例如我们前面经常举例说明的订单管理中的提醒功

    能。

          比如我们有时候我们的提醒功能,需要邮件提醒或者是短信提醒的几种服务方式,这时候我们可以通过提供统一的服务接口,然后不需要单独的在界面层去单独的

    定义,我们通过接口的形式为后期的变化方便扩展,我们来给出部分代码:

    ?
    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
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
        /// <summary>
        /// 提醒服务
        /// </summary>
        public interface IAlarmService
        {
            /// <summary>
            /// 发送消息的服务
            /// </summary>
            /// <param name="reciveObject">接收方</param>
            /// <param name="title">标题</param>
            /// <param name="content">消息内容</param>
            /// <returns></returns>
            bool SendMessage(string reciveObject,string title,string content);
        }
     
        我们来看看邮件提醒服务的简单实现代码
     
    public class EmailAlarm : IAlarmService
        {
            #region IAlarmService 成员
     
            /// <param name="strSmtpServer">邮件服务器(如果是163邮箱就写smtp.163.com)</param>
            /// strSmtpServer
            /// <param name="strFrom">发件人的帐号</param>
            /// strFrom
            /// <param name="strFromPass">发件人密码</param>
            /// strFromPass
            public bool SendMessage(string reciveObject, string title, string content)
            {
                bool isSuccess = true;
     
                MailSetting model = DAL.SQLServer.Instance.GetModelByPrimaryKey<MailSetting>();
     
                try
                {
                    System.Net.Mail.SmtpClient client = new SmtpClient(model.strSmtpServer);
     
                    client.UseDefaultCredentials = false;
     
                    client.Credentials = new System.Net.NetworkCredential(model.strFrom, model.strFromPass);
     
                    client.DeliveryMethod = SmtpDeliveryMethod.Network;
     
                    System.Net.Mail.MailMessage message = new System.Net.Mail.MailMessage(model.strFrom, reciveObject, title, content);
     
                    message.BodyEncoding = System.Text.Encoding.UTF8;
     
                    message.IsBodyHtml = true;
     
                    client.Send(message);
                }
                catch
                {
                    isSuccess = false;
                }
                return isSuccess;
            }
     
            #endregion
        }

          短信服务也和邮件服务类似,通过实现接口来提供服务,那么我们在比如说订单,或者其他的可能会提醒的地方,就可以通过自定义实现不同的格式服务,来完成

    调用。

           4.5、表现层分析

         我们都知道表现层是最终显示与用户交互的功能的,那么我们的表现层是怎么来调用服务层的呢?我们这里的实例是采用服务层调用的方式来完成。我们这里通过

    定义接口的形式,然后通过实现不同的用户UI,然后我们通过展示器来调用服务层,然后将服务层中的处理结果返回给展示器,展示器与视图之间进行交互,我们来看

    看表现层的层级关系。

          image 大家看到这个图请不要诧异,这里的展示器层可能还会直接访问业务逻辑层,也可能直接访问数据访问层,这

    些都是有可能的,我们需要根据项目具体的情况去决定。

         我们来看看UI层的视图代码:

         我们在视图中保持展示器的引用。

    ?
    /// <summary>
    /// 定义统一的视图接口代码
    /// </summary>
    public interface IView
    {
        /// <summary>
        /// 展示器访问器
        /// </summary>
        B2C.Presenter.IPresenter Presenter
        {
            get;
            set;
        }
    }
     
     我们来看看展示器中的部分代码
     
    /// <summary>
    /// 展示器接口
    /// </summary>
    public interface IPresenter