0
点赞
收藏
分享

微信扫一扫

开源AI搜索平台Search4All

木匠0819 2024-08-15 阅读 32

我在极速一个月学完黑马的《java web》课程之后跟着他写了一个java后端项目,但是后面我才发现那只是为了巩固基础的一个简单课程项目,跟实际开发的项目根本不一样。然后后面我暑假去了超星的移动图书馆开发部实习(我主要做前端的),我还是问团队老大哥要了企业的后端项目学习,顺便一起跟着黑马的《苍穹外卖》项目一起学,那么我将讲解两种项目结构,深入了解企业级项目的结构。

《苍穹外卖》项目结构:

超星移动图书馆项目结构(只显示结构,不泄露代码内容):

一、《苍穹外卖》的分三大模块结构

1、回顾基础

那么我们回顾基础,javaweb里讲过一个基础的java项目必须分成三层架构,首先资源数据定义成叫pojo的类(作为接收前端发来的数据、存入数据库、再封装好返回给前端的数据),然后分别是【Controller控制层】、【Service业务层】、【Dao数据层】

2、分模块

具体为什么呢?他也没细说,我的理解是企业的大型项目时就会需要分模块开发,然后不同的模块可能是不同的人分工写,也或者是不同模块分不同时期阶段编写,同时分模块化的项目也便于维护,分得越细越容易找到哪里出问题。(至于这里苍穹外卖其实并不是一个大型项目,尤其是后台管理系统,只是为了方便大家学习这种模块化开发的理念)

那么这个项目资源可以去黑马那里下载,自行配置,这里不过多叙述,如果想自己照着它配置一个分模块化的项目时,可以参考这个博主的文章,讲得比较详细:《苍穹外卖》知识梳理P1-多模块项目的创建_sky-common-CSDN博客

首先整个项目的父工程项目叫“sky-take-out”,它自身是一个maven管理的父工程项目。sky-take-out中并没有任何内容,只是为了实现统一管理依赖版本,以及聚合其他子模块。

然后就是三大模块:sky-common模块,sky-pojo模块,sky-server模块,这三个模块分别也都是maven管理的工程项目,然后最终都从属于一个父模块:sky-take-out

模块名称模块作用
common子模块,存放公共类,例如:工具类、常量类、异常类等   
pojo子模块,存放实体类、VO、DTO等    
server 子模块,后端服务,存放配置文件、Controller、Service、Mapper等

那么我们可以从上面表格对三个模块的简单解释,可以看出,其实

service模块】是专门对应了【controller、service、mapper】这三层的业务处理;

然后我们之前学的javaweb里有自己手动创建了很多其他的目录,最基础的就是【pojo实体类】,放到【pojo模块】;

然后就是什么【utils工具类】、【config配置类】、【filter过滤器】、【Interceprter拦截器】......一堆杂七杂八的目录,那么这些公用的包目录就可分别放到【common模块】,不是公用的就还是放回【service模块

二、详细解析三大模块内容

这一部分我希望各位直接死记硬背,不要管他为什么这样,逻辑关系啥的,因为他就是一种规范而已,当然我也会简单讲一下为什么吧。

1、pojo模块

这个模块是最简单的,就是一堆实体类而已。

在我们之前学的内容里,为了对应数据库的每一个【表】的数据,我们对应在java也实现类对应的【类】,然后在接收前端接口传过来的数据、响应返回给前端的数据,我们也通常是封装在这些类里,当然也还有针对“分页查询类”、“规范的响应类”这种数据类。

但是在写代码里我们就会发现有的时候前端发来的数据、操作数据库的数据、返回响应的数据并不一致,打个比方:用户注册的业务里,前端可能只需要传“账户名”、“账户密码”、“姓名”;然后操作数据库需要对应生成并传入这个用户的“id”、“账户名”、“账户密码”、“创建时间”、“更新时间”......;最后返回前端的时候只需返回一个规范的Result类,有返回数据要求的就把整个用户信息返回、没有的就不用返回具体数据。

那这样直接用一个 “用户类” 接收、封装这些数据就会很乱,那就要专门再细分这个 “用户类” ,分出三个小类:【dto】、【entity】、【vo】

【dto】

【entity】

另外:把dto的值封装进entitty的快捷方法

【vo】

总结,pojo就是放除了【Result类】跟【PageBean类】的所有类的模块,然后所有类里又分三个小类:【dto】、【entity】、【vo】,【dto】一般是controller跟service方法的接收参数,【entity】是service的操作对象以及mapper的操作对象,【vo】是controller的返回响应数据

2、common模块

这个是个很重要的【公用配置】的模块,很细我将细细分析

整体结构简单来说就是:

名称说明
constant存放相关常量类
context存放上下文类
enumeration项目的枚举类存储
exception存放自定义异常类
json处理json转换的类
properties存放SpringBoot相关的配置属性类
result返回结果类的封装
utils常用工具类

(1)result

前面我在pojo模块已经讲过了,就不再多解释,这里注意展开讲一下【Result】和【PageResult】跟我们之前学的又有什么不同

【PageResult】

【Result类】

那么现在属性还是这三,方法也还是这三,有2个地方不一样:

1、属性data、方法返回值都是【泛型】;而类也不再是【普通类】,而是【泛型类】

前面我们学过,Result类的data用来接收任意一种类型是数据结果,不过我们之前学的是用【Object】来接收任意类型的数据:

泛型规定了一种规范、安全性,一旦规定了这个【泛型】具体指什么,你就得按这个标准来填数据

那么我们这里的Result类就得换成泛型,更加的安全、规范,这样一来就能在外面定义了Result类的时候,一旦规定了这个Result类的【T】是什么类型,那后面这个【Result<T>】实例化的对象就只能用定义时规定好的数据!而且会有代码提示!

参考详细文章:https://zhuanlan.zhihu.com/p/331620060

2、第二个不同的地方,跟PageResult一样,要加一个【implement Serializable】,实现【序列化】接口

那么关于序列化、反序列化的作用解释,我也专门写了一篇文章,还请自行阅读完再回来搭配理解,文章链接:(可以暂时只看到第二大点就够了)java里的序列化反序列化、HttpMessageConverter、Jackson、消息转化器、对象转化器...都是啥?-CSDN博客

(2)constant

这个目录存放的就是常量了,在企业项目开发需要用到很多很多常量,我们不能像写一个java文件时那样直接写默认值,否则要更改默认值的时候那么文件里找就会很痛苦。

比如一个已注册用户有两种状态,1代表正常用户,可以使用软件;0表示禁用的用户,可能发了什么不当言论啊、看黄片啊啥的导致他被封了,不能正常使用软件;

那很多地方我们都会对应用户的状态进行判断,如果全都是0、1,那后期我们想把正常状态改成true、禁用状态改成false怎么办,一个一个找着改吗?而且下一个接你代码的人看着一堆0、1肯定懵逼啊?这啥啊?那就要换成【常量】

根据自己、企业具体业务需求写入你的常量文件

(3)context

网上的解说是 “存放上下文”,那具体上下文是啥?以我的理解,这就是前端Vue里面的 “VueX” 或者 uniapp里的 “uni.setStorageSync”,可以保存各种临时数据信息,并处理它们;

比如我们前端开发时用vueX可以在单独某个页面获取到一些获得的数组、列表数据之后,通过vueX保存到整个项目中,然后在别的页面要用的时候取出,还可以进行增删查改;uni.setStorageSync也是,这种业务最常见的就是在登陆的时候,保存【当前登录的用户信息】,然后在其他地方要用【当前用户信息】的时候再取出来用

那么java后端这里也一样,比如最常有的就是【BaseContext】记录登录的用户信息,那么它的原理其实是用【ThreadLocal 线程变量】,在java当前线程里开辟了一个存储空间,然后当多个其他线程需要访问这个变量,就可以来 “共享空间” 共享这个变量了(至于这个ThreadLocal不是重点我不打算讲,有需要自己去看相关知识点:史上最全ThreadLocal 详解(一)-CSDN博客)

例子:

 

 4、enumeration

存放各种枚举类,老实说我不知道有啥用,暂时没用到......

(4)exception

很有用的一个目录,存放各种【自定义异常类型的类

那么我们可以留意到,【GlobalException】里基本的一个【全局异常处理器】里,它的参数也就是捕捉的异常类型,叫【BaseException】。

那么回到【common模块】,在exception目录下的这些【自定义异常类型的类】里,最基础的、必须得有的一个就是【BaseException】,它用于继承java异常错误【Exception类】里最最特殊的【RuntimeException】类的异常。

【RuntimeException】是Exception中的一个子类,是由程序逻辑错误引起的异常情况,即在代码编写过程中产生的错误。它为什么特殊?因为【Exception】中除了他所有子类都必须强制性的做出异常处理,也就是我们常见的“爆红”报错;但是【RuntimeException】不是显性的异常报错,它允许用户自定义对他的异常种类进行处理,处理也行、不处理放着不管也行。

那么【BaseException】继承了【RuntimeException】,他代表【全局异常】,在不确定具体什么异常的情况下都可以用它。而这样一来,也就成为【exception】里其他所有【捕捉异常类】的 “爹”(父类),基本所有错误异常都在它的基础上。

那么细心的朋友就会发现,代码里抛出异常并传入【自定义报错信息】的并不是BaseException啊,你看

但是,它们都无一例外是继承了【BaseException

看到这里,应该很多人已经乱了,能坚持看到这的人都很牛逼了,我当时也是很懵,看着代码跳来跳去研究,终于搞明白了这之间的逻辑,让我用个图画给大家看(可以点击图片放大观看)

(5)json

这里放的是用于将【Java对象与json格式之间相互转换】用的【JacksonObjectMapper】

还是一样,我在之前讲序列化的文章里讲过了这部分,具体文章在下面文章的第三大点:java里的序列化反序列化、HttpMessageConverter、Jackson、消息转化器、对象转化器...都是啥?-CSDN博客   

(6)properties

我们之前学javaweb的时候,可能【配置类】都是放在一个叫 “config” 的目录下,但是当时可以留意到这里面有的文件叫 “xxxProperties”,有的叫 “xxxConfig”,【properties】和【config】这两文件其实有不同作用的

所以这里企业项目开发,要把【properties】跟【config】单独分开,其中各种【properties】放到【common模块】的【properties】目录下;【config】放到【service模块】的【config】目录下

那么【properties】跟【config】到底有什么区别?


properties目录下的类通常用于封装外部配置文件中的属性,并提供一种方便的方式来访问这些属性。它们通常与业务逻辑或特定服务无关,而只是提供一些全局配置信息,更侧重于配置属性的读取和封装。

这里我当时其实是忘了之前的基础.......其实我写过关于【properties】的文章,其实他就是 “最便捷的spring boot工程配置” :《后端之路——最规范、便捷的spring boot工程配置_springboot 配置文件规范性-CSDN博客》,

它是为了跟连接数据库、配置端口号...配置的那个【application.yml】文件搭配的,用来方便管 理、配置各种【工具所需要的属性、参数】例如:                                                                         


config目录下的类则更倾向于定义特定于应用程序某一部分的配置或行为。可能更侧重于配置web层、Redis数据库连接......等等的实际应用和Bean的创建。不需要搭配【application.yml】配置,基本以后都不会变,扫描的时候会自动被执行)                                                                        

比如:                                                                                                                                                

这些专业知识到后面再讲,这里只需要知道,这些不需要搭配【application.yml】配置,基本以后都不会变,自己本质就是一个【@Bean对象】可以在外面被【@Autowired】注入使用。(当然除了拦截器配置,拦截器配置不用在外面被【@Autowired】注入使用,扫描的时候会自动被执行)  

(7)utils

这个不用我过多解释,很好理解,就是【工具类】,哪里用到它们就调用,常用的工具类就:阿里云oss上传工具、HttpClient后端发送网络请求的接口类、jwt令牌加密解密的工具类、以及一些什么微信支付宝接口类啥的......前三个是最常用的,可以直接cv源代码:

 3、service模块

终于来到了最重要最重要的一个模块,所有业务逻辑都是在这处理,那么除了controller、service、mapper三层之外,其他的包基本都是与它们直接相关联所需要的东西,或者可以说跟web层相关的东西。

基本的目录结构是这样:

名称说明
config存放配置类
controller存放controller类
interceptor存放拦截器类
mapper存放mapper接口
service存放service类
SkyApplication启动类

那么这里有两个类基本要有,一个是【Redis的RedisTemplate配置类】一个是【web层配置类】

(1)config

web层配置类

web层配置类起名可以叫【WebMvcConfiguration】,这里设置的都是【web层的相关配置】,

比如:拦截器类写好之后需要的【注册拦截器配置】来开启拦截器

Swagger自动生成接口文档所需要的【knife4j】的配置

对应将knife4j的Swagger生成的接口文档的静态资源映射(为了让页面渲染显示接口文档数据)

这里提一下一个知识点:

Redis的RedisTemplate

先不说,因为这里好没讲到Redis,看我以后的文章会讲。

(2)controller、service、mapper、intercepter

这几个为什么放一块,因为以及熟悉得不能再熟悉了,学完javaweb的都应该知道这四个是啥了,三层架构加拦截器嘛......

(3)handler

基本有一个GolobalExceptionHandler就够了,这个目录就是一个放全局异常捕获器的目录。

这些全局捕获器可以自动捕捉到报错,然后根据自己的喜好编写报错时要错的事逻辑

(4)xxxApplication

xxx是你的项目名字,通常这样命名xxxApplication,这就是你整个项目的启动类,加上【@SpringBootApplication】和【@EnableTransctionManagement】才可以变成启动类,整个项目就以他为入口

(5)最后一个,resourse目录

都知道resourse是放静态资源的地方,那么我们一般就放【动态sql—mapper包】、【application.yml】、【application-dev.yml】

【动态sql—mapper包】没什么好说的,就是xml文件的mapper层文件,可以动态操作sql

那为啥会有两个yml配置文件??

因为写过后端项目的朋友一定会有这样的经历,如果你要将这个项目在自己的本地环境运行、然后通过局域网跟别人联调、然后放到服务器联调,那么数据库、网络监听的地址、端口号这些肯定都是不一样的,你需要重复“N的N次方次”、“一万一亿次”的“无限循环”地进行这些配置数据的修改。

那么在公司是绝对不允许这么低效率开发的,因为企业开发规定了开发有三个阶段:【开发环境dev】、【测试环境test】、【生产环境prod】

那么我这里个人开发学习没那么复杂,就先创建一个【开发环境dev】的【aaplication-dev.yml】,然后在里面写上真实的数据值

然后在基础的真正的那个【application.yml】里,首先在spring.profiles.active指定要用的是哪个环境,注意对应环境的yml文件必须得写成【application-xxx.yml】,这样才能直接根据【xxx来查找定位到是哪一个环境的yml文件。

然后通过【变量】的形式来引入【aaplication-dev.yml】的真实数据值,使用的语法:【${ xxx.xxx.xxx }】

自此,暂时讲完《苍穹外卖》的这种项目结构,下一篇讲超星移动图书馆的另一种项目结构

举报

相关推荐

0 条评论