网站如何导流量,女生学建筑工程技术就业前景,烟台定制网站建设报价,excel做注册网站文章目录 工程结构的意义(一) 应用分层(二) 二方库依赖(三) 服务器 #x1f64a;前言#xff1a;本文章为瑞_系列专栏之《Java开发手册》的工程结构篇#xff0c;主要介绍应用分层、二方库依赖、服务器。由于博主是从阿里的《Java开发手册》学习到Java的编程规约#xff0c… 文章目录 工程结构的意义(一) 应用分层(二) 二方库依赖(三) 服务器 前言本文章为瑞_系列专栏之《Java开发手册》的工程结构篇主要介绍应用分层、二方库依赖、服务器。由于博主是从阿里的《Java开发手册》学习到Java的编程规约所以本系列专栏主要以这本书进行讲解和拓展有需要的小伙伴可以点击链接下载。本文仅供大家交流、学习及研究使用禁止用于商业用途违者必究 本系列第一篇链接一编程规约 本系列第二篇链接二异常日志 本系列第三篇链接三单元测试 本系列第四篇链接四安全规约 本系列第五篇链接五MySQL数据库 本系列第六篇链接六工程结构 本系列第七篇链接七设计规约 工程结构的意义 工程结构是整个软件开发过程中非常重要的一环它定义了各个模块之间的层次关系和依赖关系使得开发人员能够更加清晰地理解和管理复杂的软件系统。它通过合理的层次划分、模块化开发、减少冗余、提高可扩展性、便于测试和维护以及促进团队协作等方面工程结构有助于提高软件开发的效率和质量降低维护成本和风险。 工程结构就像一座城市的规划合理的规划可以让城市更加有序、美观和宜居。同样合理的工程结构可以让软件系统更加有序、可维护和可扩展可以为开发人员提供了一种组织和构建复杂软件系统的有效方法。当你开始认知到所有的代码不能全部写在main方法里的时候就是理解工程结构意义的开始。 工程结构主要的作用和意义如下
层次划分工程结构将整个软件系统划分为不同的层次每个层次都有明确的职责和功能。层次之间的划分使得开发人员能够更好地理解系统的整体结构并更好地组织代码和模块。模块化开发工程结构使得开发人员可以将复杂的软件系统划分为多个模块每个模块负责特定的功能或业务领域。这种模块化开发方式可以提高代码的可维护性和可重用性降低系统的耦合度使得开发更加高效和可靠。减少冗余工程结构通过合理的层次划分和模块化开发可以避免代码的冗余和重复。每个层次和模块只关注自己的业务逻辑和功能避免了代码的交叉和耦合减少了不必要的复杂度。提高可扩展性工程结构使得软件系统更加易于扩展。当需要增加新的功能或业务时只需要在相应的层次上添加新的模块或组件而不会对其他部分造成影响。这种可扩展性使得软件系统能够更好地适应变化和未来的发展。便于测试和维护工程结构使得测试和维护变得更加容易。每个层次和模块都有明确的接口和功能可以单独进行测试和调试。同时层次之间的依赖关系使得代码更加易于理解和维护降低了维护成本和难度。促进团队协作工程结构有助于促进团队协作和分工。不同开发人员可以负责不同的层次和模块共同完成复杂的软件系统。合理的分工可以提高开发效率和代码质量使得团队协作更加顺畅和高效。 (一) 应用分层
【推荐】图中默认上层依赖于下层箭头关系表示可直接依赖如开放接口层可以依赖于Web 层也可以直接依赖于 Service 层依此类推 开放接口层可直接封装 Service 方法暴露成 RPC 接口通过 Web 封装成 http 接口网关控制层等。终端显示层各个端的模板渲染并执行显示的层。当前主要是 velocity 渲染JS 渲染JSP 渲染移 动端展示等。Web 层主要是对访问控制进行转发各类基本参数校验或者不复用的业务简单处理等。Service 层相对具体的业务逻辑服务层。Manager 层通用业务处理层它有如下特征 1 对第三方平台封装的层预处理返回结果及转化异常信息。 2 对 Service 层通用能力的下沉如缓存方案、中间件通用处理。 3 与 DAO 层交互对多个 DAO 的组合复用。DAO 层数据访问层与底层 MySQL、Oracle、Hbase、OB 等进行数据交互。外部接口或第三方平台包括其它部门 RPC 开放接口基础平台其它公司的 HTTP 接口。 瑞本条需要长时间的经验累积才能理解如果是初学者的话请先记住本条即可后续慢慢形成自己的理解。 博主对应用分层的理解是要根据自己的业务结构去进行分层设计如常见的三层模型用户表示层、业务逻辑层、数据层。根据自身的业务面向接口编程进行N层架构设计每一层都各司其职互不干扰各层之间应当尽量解耦。 博主对DAO数据访问层的理解 DAO层只进行数据访问它只负责将SQL传给底层数据库进行数据交互而不进行其上层业务逻辑层的工作无论业务逻辑层传递什么参数进来都应传递至数据库进行执行操作而不进行业务判断校验处理。所以类似数据库连接对象不应当向上层即业务逻辑层传递这不仅违反了分层设计的意义也污染了接口。 所以按照博主的理解对于Java后端程序控制事务的回滚只能做到数据行级别的控制因为数据库访问对象不应当在service层传递和操作无法做到service调用多个dao层方法去回滚毕竟要用到同一个连接对象才可以回滚事务。 要用到同一个连接对象这在实际开发中是不切和实际的。既然dao层不去传递数据库连接对象那需要多个数据库操作的业务的事务如何处理呢想想场景比如支付宝充值话费支付宝扣款话费增加阿里和电信的接口不可能去传递数据库连接对象支持宝话费充值业务需要调用电信提供的接口但电信的接口会要求提供数据库连接对象吗这是绝对不可能的❗️ 对吧⁉️如果说支付宝扣款成功了但是话费充值失败比如正好断网演练而发生异常等情况那么电信的service层就会去判断只要确保支付宝转的钱到账了那应该是自己电信的业务出问题了它们可能会先垫付这笔钱再service生成核对的消息给人工审核然后巴拉巴拉总之电信会通过自己的异常流程进行处理解决是不可能电信去回滚支付宝的扣款的想一想是不是这个逻辑。所以在实际业务中数据库连接对象是无法传递的因为数据库连接对象早就被close了一旦被close那这辈子都取不到该连接对象了更不要说传递它进行回滚操作。所以我认为即使在service层处理了事务意义也不大除非就是个单体项目更多的应当靠service层的逻辑校验保护业务的正常的或异常的执行流程而dao层只需要管好自己与数据库的交互即保证dao中单个方法的回滚。 这是博主自身的经验分享可能和您认知的相悖但博主依然认为应当通过分析形成自己的理解而不是别人说什么就是什么。在程序设计上没有对错只有适合和更适合若有问题请在评论区中指正非常感谢 本条虽为推荐但在实际开发中非常重要请务必多花时间理解应用分层的意义及作用 【参考】分层异常处理规约在 DAO 层产生的异常类型有很多无法用细粒度的异常进行 catch使用 catch(Exception e)方式并 throw new DAOException(e)不需要打印日志因为日志在 Manager/Service 层一定需要捕获并打印到日志文件中去如果同台服务器再打日志浪费性能和存储。在 Service 层出现异常时必须记录出错日志到磁盘尽可能带上参数信息相当于保护案发现场。Manager 层与 Service 同机部署日志方式与 DAO 层处理一致如果是单独部署则采用与 Service 一致的处理方式。Web 层绝不应该继续往上抛异常因为已经处于顶层如果意识到这个异常将导致页面无法正常渲染那么就应该直接跳转到友好错误页面尽量加上友好的错误提示信息。开放接口层要将异常处理成错误码和错误信息方式返回。 瑞本条中提到了Web 层绝不应该继续往上抛异常因为已经处于顶层所以在Web层常见是Controller一定要对catch中的error_message进行转义处理后返回给前端。本条需结合本系列第二篇【异常日志】中的(二) 异常处理第4条【强制】捕获异常是为了处理它不要捕获了却什么都不处理而抛弃之如果不想处理它请将该异常抛给它的调用者。最外层的业务使用者必须处理异常将其转化为用户可以理解的内容以及【异常日志】中的(一) 错误码第7条、第8条。错误码不能直接输出给用户作为提示信息使用、错误码之外的业务独特信息由 error_message 来承载而不是让错误码本身涵盖过多具体业务属性。 【参考】分层领域模型规约 • DOData Object此对象与数据库表结构一一对应通过 DAO 层向上传输数据源对象。 • DTOData Transfer Object数据传输对象Service 或 Manager 向外传输的对象。 • BOBusiness Object业务对象可以由 Service 层输出的封装业务逻辑的对象。 • Query数据查询对象各层接收上层的查询请求。注意超过 2 个参数的查询封装禁止使用 Map 类来传输。 • VOView Object显示层对象通常是 Web 向模板渲染引擎层传输的对象。 瑞一定要分清楚 [ 数据对象类 ] 和 [ 业务对象类 ] 的职责区别。数据对象类只负责数据的存储和传递数据对象类内部不应该存在任何的业务逻辑的处理通常情况下只有 setter/getter/toString 如DTO数据传输对象。而业务对象类才是负责处理业务逻辑不应该去维护非业务的成员属性如Service层接口的具体实现类。 下图为博主的经验对应用分层的理解可能有误仅供学习参考若需搬运下图请标明出处 瑞 1️⃣PO/DO实体类根据阿里巴巴的开发手册中的定义DO可以认为等同于PO也就是Entity实体类与数据库表结构一一对应用来增删改查 2️⃣BO业务对象类BO是PO的需求结果组合同一类别的业务就会有一个BO与之对应除了getset方法以外还有业务逻辑的方法。如购物业务需要从商品DO、用户余额DO等获取需要的数据 3️⃣:DTO数据传输对象类DTO和BO、VO的区别主要是就是字段的删减DTO在BO的基础上获取需要的数据向上层传递。而VO是在DTO的基础上删减一些数据向前端展示 4️⃣VO展示对象类VO就是展示给前端用的数据一般情况下是从DTO中获取要展示的信息。如博主信息简略展示会从博主信息DTO中获取到名称、成就展示而DTO中的头像等数据不需要在简略展示的前端界面中出现 5️⃣所以在一些简单的业务中基础CRUD可以把DTO和BO合并为DTOVO也可以使用DTO代替即PO实体类-DTOResponse-前端 (二) 二方库依赖 【强制】定义 GAV 遵从以下规则 1 GroupID 格式com.{公司/BU }.业务线 [.子业务线]最多 4 级。 说明{公司/BU} 例如alibaba/taobao/tmall/aliexpress 等 BU 一级子业务线可选。 正例com.taobao.jstorm 或 com.alibaba.dubbo.register 2 ArtifactID 格式产品线名-模块名。语义不重复不遗漏先到中央仓库去查证一下。 正例dubbo-client / fastjson-api / jstorm-tool 3 Version详细规定参考下方。 【强制】二方库版本号命名方式主版本号.次版本号.修订号 1主版本号产品方向改变或者大规模 API 不兼容或者架构不兼容升级。 2 次版本号保持相对兼容性增加主要功能特性影响范围极小的 API 不兼容修改。 3 修订号保持完全兼容性修复 BUG、新增次要功能特性等。 说明注意起始版本号必须为1.0.0而不是 0.0.1。 反例仓库内某二方库版本号从 1.0.0.0 开始一直默默“升级”成 1.0.0.64完全失去版本的语义信息。 瑞在Maven中使用version如下所示 version1.0.0/version【强制】线上应用不要依赖 SNAPSHOT 版本安全包除外正式发布的类库必须先去中央仓库进行查证使 RELEASE 版本号有延续性且版本号不允许覆盖升级。 说明不依赖 SNAPSHOT 版本是保证应用发布的幂等性。另外也可以加快编译时的打包构建。 【强制】二方库的新增或升级保持除功能点之外的其它 jar 包仲裁结果不变。如果有改变必须明确评估和验证。 说明在升级时进行 dependency:resolve 前后信息比对如果仲裁结果完全不一致那么通过dependency:tree 命令找出差异点进行exclude排除 jar 包。 【强制】二方库里可以定义枚举类型参数可以使用枚举类型但是接口返回值不允许使用枚举类型或者包含枚举类型的 POJO 对象。 【强制】依赖于一个二方库群时必须定义一个统一的版本变量避免版本号不一致。 说明依赖 springframework-core,-context,-beans它们都是同一个版本可以定义一个变量来保存版本${spring.version}定义依赖的时候引用该版本。 瑞在Maven中如下所示 !-- 属性配置 --propertiesspring.version版本/spring.version/propertiesdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactIdversion${spring.version}/version/dependency/dependencies【强制】禁止在子项目的 pom 依赖中出现相同的 GroupId相同的 ArtifactId但是不同的Version。 说明在本地调试时会使用各子项目指定的版本号但是合并成一个 war只能有一个版本号出现在最后的lib 目录中。曾经出现过线下调试是正确的发布到线上却出故障的先例。 【推荐】底层基础技术框架、核心数据管理平台、或近硬件端系统谨慎引入第三方实现。 【推荐】所有 pom 文件中的依赖声明放在dependencies语句块中所有版本仲裁放在dependencyManagement语句块中。 说明dependencyManagement里只是声明版本并不实现引入因此子项目需要显式的声明依赖version 和 scope 都读取自父 pom。而dependencies所有声明在主 pom 的dependencies里的依赖都会自动引入并默认被所有的子项目继承。 瑞后续博主会专门写关于maven的使用博客先预留位置 【推荐】二方库不要有配置项最低限度不要再增加配置项。 【推荐】不要使用不稳定的工具包或者 Utils 类。 说明不稳定指的是提供方无法做到向下兼容在编译阶段正常但在运行时产生异常因此尽量使用业界稳定的二方工具包。 【参考】为避免应用二方库的依赖冲突问题二方库发布者应当遵循以下原则 1精简可控原则。移除一切不必要的 API 和依赖只包含 Service API、必要的领域模型对象、Utils 类、 常量、枚举等。如果依赖其它二方库尽量是 provided 引入让二方库使用者去依赖具体版本号无 log 具体实现只依赖日志框架。 2稳定可追溯原则。每个版本的变化应该被记录二方库由谁维护源码在哪里都需要能方便查到。除 非用户主动升级版本否则公共二方库的行为不应该发生变化。 (三) 服务器 【推荐】高并发服务器建议调小 TCP 协议的 time_wait 超时时间。 说明操作系统默认 240 秒后才会关闭处于 time_wait 状态的连接在高并发访问下服务器端会因为处于 time_wait 的连接数太多可能无法建立新的连接所以需要在服务器上调小此等待值。 正例在 linux 服务器上请通过变更/etc/sysctl.conf 文件去修改该缺省值秒 net.ipv4.tcp_fin_timeout 30 【推荐】调大服务器所支持的最大文件句柄数File Descriptor简写为 fd。 说明主流操作系统的设计是将 TCP/UDP 连接采用与文件一样的方式去管理即一个连接对应于一个 fd。主流的linux服务器默认所支持最大fd数量为1024当并发连接数很大时很容易因为fd不足而出现“opentoo many files”错误导致新的连接无法建立。建议将 linux 服务器所支持的最大句柄数调高数倍与服务器的内存数量相关。 【推荐】给 JVM 环境参数设置-XX:HeapDumpOnOutOfMemoryError 参数让 JVM 碰到 OOM场景时输出 dump 信息。 说明OOM 的发生是有概率的甚至相隔数月才出现一例出错时的堆内信息对解决问题非常有帮助。 瑞jvm虚拟机参数模版如下 设置说明-Xms1g初始堆内存大小-Xmx1g最大堆内存大小-Xss256k虚拟机栈内存大小-XX:MaxMetaspaceSize512m最大元空间大小-XX:DisableExplicitGC让System.gc()方法失效-XX:HeapDumpOnOutOfMemoryError在OutOfMemoryError的时候保存堆内存快照-XX:HeapDumpPath/opt/logs/my-service.hprof将堆内存快照保存到文件中-XX:PrintGCDetails打印垃圾回收的详细信息-XX:PrintGCDateStamps打印垃圾回收发生的时间-Xloggc:文件路径将垃圾回收信息保存保存到文件中
【推荐】在线上生产环境JVM 的 Xms 和 Xmx 设置一样大小的内存容量避免在 GC 后调整堆大小带来的压力。 瑞建议将-Xms设置的和-Xmx一样大有以下几点好处 1️⃣运行时性能更好堆的扩容是需要向操作系统申请内存的这样会导致程序性能短期下降。 2️⃣可用性问题如果在扩容时其他程序正在使用大量内存很容易因为操作系统内存不足分配失败。 3️⃣启动速度更快Oracle官方文档的原话如果初始堆太小Java 应用程序启动会变得很慢因为 JVM 被迫频繁执行垃圾收集直到堆增长到更合理的大小。 【参考】服务器内部重定向必须使用 forward外部重定向地址必须使用 URL Broker 生成否则因线上采用 HTTPS 协议而导致浏览器提示“不安全“。此外还会带来 URL 维护不一致的问题。 本文是博主的粗浅理解可能存在一些错误或不完善之处如有遗漏或错误欢迎各位补充谢谢 如果觉得这篇文章对您有所帮助的话请动动小手点波关注你的点赞收藏⭐️转发评论都是对博主最好的支持~