`
herman_liu76
  • 浏览: 99957 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

如何设计一个结构合理的java项目

    博客分类:
  • java
阅读更多
## 1、前言

最近写一个Java处理工具,是一个springboot的非web项目,正好借这个机会总结一下自己的经验,当开发一个Java应用时,应该全局考虑哪些方面,包括如何划分功能包,如果建立对象关联,以及如果串起流程;另一方面在这个过程中,作为喜新厌旧的程序员,全面应用Jdk8的新特征,比如其中的语法糖还是蛮甜的。


## 2、类划分功能包
web项目是很多人接触最多的项目,与工具项目有相似点,也有不同点。一般的单体的web项目通常的包,表面上按这样划分:

多层次业务模块->controller->service->dao这么几层。而从实现了一个请求的单向处理过程看,服务端实际上完整过程是这样:通讯协议处理->filter->dispatchservlet->controller->service->dao。由于框架做了很多事情,开发人员很可能只需要固定的套路,而不需要深入研究,基本上技术是简单的CURD操作。

我这个工具过程功能比较少:解析excel->输出txt->合并txt三个步骤,虽然不多,但尽可能让结构合理,这样想增加功能也方便,也可以扩展出的很复杂功能。

  •     1. 为什么分层分包,而不是铁板一块?因为把不变的和可变的进行区分,有利于修改,有利于重用,有利于加塞...缺点嘛,要建立关联关系,把分散的东西有机结合起来,这个难度略大,不理解的人不方便定位功能点,更不好修改,易打乱结构。
  •     2. 为什么做软件强调抽象思维,这个过程就是有效的划分块。实现高聚合低耦合软件的也必要能力。
  •     3. 通常写web的,需要的包只是处理框架的一部分,所以不用不完整的过程。自己写工具就是全过程了,所以写web不利于提高java开发水平,重点是业务理解了。
  •     4. 看到过有代码,把web层的东西写到service中,比如httpServletRequest传了过去,是对分层缺少理解。危害是什么呢?如果换了通讯协议,比如http换grpc,service也要改,本来可以不改的。大多数情况下不会换,但可能service会组合,也是潜在问题。VO/DTO/BO这样的对象区别,这在于它们处于不同的层次。
  •     5. 同一层次的东西放在一个包里,包里面一般分两层,包中的根目录放接口,抽象类与参数对象等。包里的其它目录一般是接口的多种实现类。web项目的controller层中,接口在框架里了,都是实现类,所以一个业务模块只一层。
  •     6. 包里可以有包,类似组织架构,那调用层与被调用层是同级还是内部?service为何不放controller包里,dao为何也独立出来?一是因为service理论上可以被其它协议调用,不用controller这个http框架。dao可能因为调整多,放太深也不好。
  •     7. 包划分示例:常见的dubbo,划分了很多层,最核心的远程rpc功能只有rpc通讯层,remote传输层两个。其它是辅助或者是另外的功能块,比如serialize,monitor是公共的,registry是另一功能了。理解源码就要先有全局思路,比如理解功能与多层次目录的划分,就知道作者怎么想的,各个功能在点哪点。remote层里有多种传输,比如http/p2p/telnet,也有自己的。自己的长连接还要心跳机制,也有重连机制,所以里面还有head处理等更深的层次。一个大项目,排放的条理性,科学性很重要,写起来思路连贯,找起来一歩定位。
  •     8. 再比如rocketmq,模块有业务块与name块,最核心的业务功能是接收消息并存下来,所以有broker包与store包。其它还有remote块,通讯肯定是公共的。
  •     9. 包内组织示例:dubbo为例,它是微核心,几乎所有的东西都是接口化,甚至对象仓库也是,有两种仓库,一种是spring库,一种是自己写的。前面说了,包内分接口公共类与各个实现的子包,有些实现是自己的,有些是用第三方的。每种方式就一个目录,第三方功能所在的目录里一般都是对第三方接口类的包装类,包装过程也是对多种第三方实现的的抽象过程,因为第三方功能可能差别不少,要兼容适配。这里面可能用到代理、装饰,门面等设计模式。


## 3、类(包)之间关联

划包与包内组织好了,下面就是建立包之间调用关联了,主要是调用的对象之间,通过属性引用起来的。比如@autowired。还得不说spring容器的强大了。它可以按照你提供的配置蓝图与零件,给你组装生成出一个可以运转的机器。它的依赖注入,多个过程(比如前置,后置,生成,初始化,销毁等)中插入你的处理逻辑,比如factorybean产生代理对象,它的延时加载,条件加载,让你的这个配置蓝图可以非常的复杂。前面提到dubbo微核心,自己实现了一个小的容器,也有依赖注入,最有特色的是,用接口类型获取对象时,特殊注解的接口,它可以用代码动态生成接口的代理类对象给你,而这个代理对象根据请求参数不同,代理不同的实现类,具体代理哪个,又是从容器中取的。这就厉害了,一方面减少了spring容器的冗余功能,另一方面实现了自己特殊的需求,再着体现了作者的实力。

  •     1. 一般就不要自己处理引用了,就用spring的,除非比较简单的。如果是引用多个实现,可以@Autowired Map,用的时候可以按名字取真正的实现类。我的项目中启动类就是引用了不同excel的解析类,根据特征名字选择调用。
  •     2. 引用可以是单向的,也可以是双向的。用@Autowired不用管了,自己写一般就是构造函数或者Init()方法中引入被调用对象,双向就是之后再把自己(this)传给调用对象的设置方法。
  •     3. 引用还可以是间接的,间接时就要有人穿针引线,通常就是listenner对象。双向引用就是方便反馈结果,用间接则增加了灵活性,反馈可以给自己,也可以给别人,也可以广播,更灵活。至于反馈给谁,调用方产生监听器时会指明的。比如领导调用员工,双向引用(留下手机号)就可以直接反馈结果给领导,也可以间接方式,把秘书手机给员工,并明确秘书收到结果反馈自己和另一领导。
  •     4. 我的工具中,【解析类】处理时,使用了easyexcel包。先产生一个配置【数据收集类】的【监听器】,把一个【监听器】给easyexcel,监听到数据时,会通知【数据收集类】存起来。
  •     5. 这里说的关联关系,都是spring默认的singleton类。至于【监听器】,虽然可能也只是一个,但一般不当成@Component,可以说是它的地位太低吧。而是用的时候new一个,复杂的用工厂临时造一个对象。【监听器】通知谁,不是自己@Autowired的,而是由安排它的类指派的。
  •     6. 还有一种业务关联关系,类之间本身没有任何联系,而是由业务配置联系起来的,这个最典型的就是工作流了,特别是条件节点,根据特征数据不同,调用不同的处理类。还有就是责任链模式,一个个独立的处理节点类,是由另外的数组或者链表前面关联起来的,最常见的是filterChain。特别注意的是filterChain不是单例,每一个处理建一个新的filterChain,因为内部要记录当前起到哪一步了,而链条上的节点filter是单例。这个生活中的例子就是大家参加体检,每人一个体检单chain,而各个科室就是节点,每个人的体检单上打过勾的都可能不同。更复杂的后面的节点,由前面的节点根据情况产生。
  •     7. 责任链的例子非常多,除了servelet的filter外,druid中的监控功能也是,所以相关的sql类都被代理,代理蹭就插入了一个短chain,其中一个节点功能就是输出监控信息。springaop中也在Invocation中建了一个链,因为一个方法上可以有多个advise,要组织成链条。我以前也用过,一个请求要经过多次处理,如果中间因故断了,还可以再次请求,找到后续处理的索引,继续后续的处理。
  •     8. 有了spring,类的引用太容易了,但要避免网状引用。而应该用树状引用。最典型的就是rocketmq中,非常多的类都是通过核心协调的controller类调用的,这个不是web中的controller。部门间调用都通过领导。


## 4、类(包)关系的变更

类的关系建好了,也不是一成不变,用户需求会变,功能要增加,低耦合的目的就是应对这种变化,如果事先考虑这种变化,可以有一定的机制,把变成留给用户,并规范用户的代码。如果没有,变化也可以限制在一定的变动量下。而用户插入的功能可能多个,所以经常发现用责任链模式串起来。

  •     1. filter机制就是插在http的请求中的过程,可有可无,用户自己来实现具体的filter。但是这限制在特定协议中的灵活,所以spring有自己的interceptor机制。我参与的项目中,平台框架自己弄了一个hander机制。
  •     2. 如果没有考虑到这个变化,spring提供了aop机制。
  •     3. 如果原有的关系中插一层关系,而且没有spring,就要用代理的思路,比如druid把所有的数据库操作都包装了一下。中间插入自己的日志处理chain节点,想增加也是很方便的。
  •     4. 比如我现在做的多个excel解析,可以进一步搞的很复杂,前面可以增加多种协议处理远程excel,也可以解析其它格式文件,解析好的数据可以放内存,也可以放数据文件暂存,输出片断可以是txt,也可以是其它类型。想要日志的话,可以找个地方插入责任链。想切换easyexcel到poi,也可以分别包装一下,上层调用包装接口,具体实现可以在配置中写,也可以从参数中拿。如果要增加加解密操作,可以增加处理层,也可以包装已有的加密方式。如果要增加数据查询,可以在暂存层上増加查询功能,而查询可以是http的请求方式,也可以弄成部分支持sql格式的查询。总之,高聚合的东西不太变,低耦合的设计就是考虑变化的。
  •     5. 设想,如果没有很好的抽象思维,合理科学的分层 ,改动量与改动难度就越来越大。当然大多数工作都是简单的,专注于业务的,所以乱一点问题不大。这也就是可能做过很多业务,但真正的技术水平还不高的原因。


## 5、类的自我管理

大型项目,越看越感觉象生活中的场景,比如象构建一个医院,服务一个个病人。过程中有保安进门,有挂号产生一次服务链,每个科进行本科室的处理,人多了排队,再多限流。当然也有行政部门与科内部进行自我的管理,比如事先准备好设备,内部排班,整理统计数据等。发明面向对象编程的人太伟大了,贴合生活,易于理解并设计非常复杂的项目。

  • 1. spring中产生一个新的对象,有init(),有distroy()进行统一处理,可以做准备动作,也可以优雅结束,说明对象有完整的生命周期。前面提到的业务对象,一般是用完自己销毁了,不考虑 这个。
  • 2. 一般的自我管理是通过内部的管理线程进行的,比如线程池内处理线程的增加与减少。连接池内连接数的增加与减少。微服务中可用服务的从zookeeper上更新等等。远程连接的重连机制,心跳机制。
  • 3. 这个小工具没有这方面的管理需求,只是init()时,把配置文件中的数据,设置给static变量。因为之前很多地方按static中取的,不想改。


## 6、全面使用jdk8新特征

8已经老了,但很多项目更老,而且老程序员习惯不容易变。我却喜欢尽可能用点新的,了解新特征产生的原因,才能更好的用好。

- 多线程控制


多个地方用了CompletableFuture,使用这个和lambda表达式,由于可以把方法作为参数进行传递。编程思路要有一定的变化。这就是把大的处理过程,拆分成多个子过程,过程之间有串、并等组合关系,最后可以异步获取最终结果。下面这个是主线程等待多个子线程完成。

```java
		List<CompletableFuture<Void>> outputCfList = new ArrayList<CompletableFuture<Void>>();
		txtFileMap.forEach((k,v)->{
			outputCfLst.add(CompletableFuture.runAsync(()->v.createTxt(filePath)));//甜甜的语法糖
		});
		CompletableFuture.allOf(outputCfList.toArray(new CompletableFuture[txtFileMap.size()])).join();
```
```java
		new Thread(() -> {
			String[] fileList = argsFile.list((dir, name) -> name.toUpperCase().endsWith("XLSX"));//甜甜的语法糖
			LOGGER.info("the directory's total xlsx files number is [{}]", fileList.length);
			String rawExcelFile = "";
			for (String fileName : fileList) {
。。。。。。。。。。。
			}
			if (StringUtils.isNotEmpty(rawExcelFile)) {
				LOGGER.info("find [Ma wenjie] raw excel file and begin generate new excel file...");
				convertMap.get("rawConverter").read(argsFile.getPath() + File.separator + rawExcelFile);
			}
			dealRawExcelLock.countDown();
		}).start();
		// 主线程等待前一步生成excel
		dealRawExcelLock.await();

```

上面这段是用传统的CountDownLatch,因为额外加了一个前置处理过程。

- stream

这是一个把配置的string转成map的语句,也是因为习惯使用springboot配置的方法,后来这个没用了,改成配置类了。不过这样的语句写的很简洁吧,也许有人认为不好理解,其实用几次,你就会喜欢上。

```
Map zone2provinceMap = Arrays.stream(zone2province.split(";")).filter(kv -> kv.contains("=")).map(kv -> kv.split("=")).collect(Collectors.toMap(x -> x[0], x -> x[1]));
```

其它简单的lambda表达式,optional就不提了,简单,好用,甜,上面代码里都有。

- 额外提一下spring的yml配置文件

以前一直用properties文件,因为这个工具有很多字典项,以前用DB存的多,现在用配置文件转map存了,而且有中文。所以开始用yml配置文件了。省的properties的中文还是unicode编码,不方便。

写的过程中,进一步体验并规范了项目的层次感。所有的业务字典map,全都在bizmap下。用一个统一的配置类解析持有,类很简单,增加一个map也很简单,如下,其它类用的地方就@Autowried一下。

```
@Configuration
@Data
@ConfigurationProperties(prefix="bizmap")
public class ConfigBizMap {
	public Map<String,String> zone2province;
	public Map<String,String> ...;
	public Map<String,String> ...;
	public Map<String,String> ...;
	public Map<String,String> ...;
	public Map<String,String> ...;
}

```

## 7、总结

最近看到银行的大项目中的很多基础平台代码,总的来说,都逃不出上面总结的内容。当看过复杂的,著名的工程代码,掌握了上面的规律,再看这些都相对简单多了。这样可以很快掌握作者的思路,方便用好现有框架,也方便查问题,方便做功能完善。目前问题是大项目功能内容太多了,广度问题,比较花时间。
分享到:
评论

相关推荐

    树形结构设计总结java demo

    在IT领域,特别是软件开发中,树形结构是一种常见的数据结构,它被广泛应用于各种场景,如文件系统、计算机科学中的编译器、图形...对于想要提升Java编程能力,特别是数据结构知识的开发者,这是一个很好的实践项目。

    java毕业项目设计

    1. **Java基础**:这是所有Java项目的基石,包括变量、数据类型、运算符、控制结构(如if-else、switch、for、while)、函数、类与对象等。理解这些基本概念并能熟练运用是构建项目的基础。 2. **面向对象编程(OOP...

    Java项目设计与开发范例

    《Java项目设计与开发范例》是一本深入探讨Java编程在实际项目中的应用的教材,其配套源代码提供了丰富的实例,旨在帮助读者更好地理解和掌握Java技术在软件开发过程中的运用。这本书涵盖了Java语言的基础知识、面向...

    JAVA项目开发需求

    JAVA项目开发需求 JAVA项目开发需求是指使用JAVA语言开发一个考勤管理系统,要求...JAVA项目开发需求是一个系统的设计任务,需要学生具备良好的JAVA基础知识和图形开发能力,同时也需要学生具备良好的设计和实现能力。

    5个简单的Java web商城项目,毕业设计

    6. **毕业设计指导**:对于毕业设计而言,这些项目提供了一个良好的起点,学生可以通过理解代码结构、修改功能或添加新特性来锻炼自己的编程能力。例如,可以尝试优化性能、增加支付接口、实现用户权限管理或构建...

    java数据结构课程设计java代码

    在本Java数据结构课程设计项目中,主要涵盖了数组、链表和字符串等基本数据结构的操作。这个项目不仅涉及了理论知识的应用,还实践了GUI(图形用户界面)的设计,使用了Swing库来构建用户交互界面,并利用文件系统...

    数据结构课程设计—java通讯录管理系统

    《数据结构课程设计—Java通讯录管理系统》是一个典型的IT项目,它涵盖了计算机科学中的核心领域——数据结构和软件工程的基本原则。在这个系统中,我们利用Java编程语言来实现一个能够管理和操作联系人信息的系统,...

    java 毕业设计指南与项目实践源码

    在提供的"源代码"文件中,可能包含了一个完整的Java项目,学生们可以通过阅读和理解代码来学习如何组织一个项目,如何将理论知识应用到实际中。"光盘说明.txt"可能是项目说明文档,包含了关于项目背景、目的、系统...

    java数据结构课程设计

    在本“Java数据结构课程设计”项目中,主要涵盖了数据结构的基本应用,包括背包问题的解决、内部排序算法的性能分析以及停车场模拟管理程序的设计与实现。这些是计算机科学教育中至关重要的部分,特别是对于学习Java...

    适合练手、课程设计、毕业设计的Java项目源码:基于BS结构下的OA流程可视化的研究与实现(源代码+论文).rar

    适合练手、课程设计、毕业设计的Java项目源码:基于BS结构下的OA流程可视化的研究与实现(源代码+论文).rar 适合练手、课程设计、毕业设计的Java项目源码:基于BS结构下的OA流程可视化的研究与实现(源代码+论文).rar ...

    JAVA项目展示PPT

    【Java项目展示PPT】是一份详尽介绍Java编程语言在实际项目开发中的应用和实践的演示文稿。这份PPT旨在通过清晰的结构、直观的图表和实例,帮助观众理解Java技术在软件开发中的核心价值,以及如何利用Java进行项目...

    java项目开发实战案例

    在Java Web开发中,Spring MVC是一个广泛应用的实现框架,它能帮助开发者更好地组织代码,提高代码复用性。 除此之外,版本控制系统如Git的使用,也是项目开发中的重要一环。它能够帮助团队协作,追踪代码变更,...

    JAVA项目实践合集

    总的来说,这个"JAVA项目实践合集"是一个宝贵的资源,它提供了从基础到高级的Java编程实践经验,包括项目设计、开发流程、问题解决策略等多个方面。无论你是Java新手还是有经验的开发者,都可以从中找到提升自己技能...

    java项目设计与开发范例源码

    这个压缩包包含了多个Java项目的源代码,每个项目都是一个具体的应用实例,涵盖了各种编程技巧和设计模式。在使用这些源码时,初学者可以通过阅读和分析代码,提升自己的编程技能和解决问题的能力。 1. **Java基础...

    Java 项目经验汇总(简历项目素材)

    在Java开发领域,项目经验是衡量开发者技能和能力的重要标准,尤其对于简历的撰写来说,一个详实且具有深度的项目经验部分能显著提升求职者的竞争力。本资料"Java项目经验汇总(简历项目素材)"提供了丰富的实例和...

    学生毕业设计学生管理系统java项目源码

    《学生毕业设计学生管理系统java项目源码》是一个典型的IT毕业设计项目,主要涵盖了Java编程语言在构建学生管理系统中的应用。这个系统旨在实现对学生信息的高效管理和便捷查询,为教育机构提供一套数字化解决方案。...

    java项目的设计文档,很有参考价值

    在Java项目开发中,设计文档是至关重要的,它为项目的规划、实施和维护提供了清晰的指导。本设计文档集合包含了关于Java项目的关键知识点,对于学习和理解Java项目的架构设计、模块划分、接口定义以及实现策略等方面...

    java 读取表结构 mysql

    在给出的压缩包文件"DBInfoExport"中,可能包含了一个用于导出数据库信息的工具或类,具体功能可能涉及上述过程的实现。如果需要进一步了解这个工具,可以查看源代码或其相关文档以获取详细信息。

    java 架构设计示例文档

    综上所述,一个完整的Java架构设计示例文档会涵盖上述多个方面的内容,它不仅为开发者提供架构设计的蓝图,同时也为项目管理和后期维护提供了宝贵的参考。对于希望了解如何从零开始设计一个Java应用架构的人来说,...

Global site tag (gtag.js) - Google Analytics