之前写了几个简单的练习(图书管理系统、留言板...),这些只是一点简单的开发,但代码也显得比较乱,下图是几个类的分区,如图:
那如果要把完整的图书管理系统开发完了呢?那就会更加乱了。所以下面要学习应用分层,把代码管理好。
应用分层类似公司的组织架构:
公司初创阶段,人比较少,一个人会身兼数职,即做财务,又做人事等等。随着公司的逐渐壮大,会把岗位进行细分,会划为财务部门、人事部门、行政部门等等。
项目开发也是类似,最开始功能简单时,前后端放在一起开发,但随着项目功能的复杂,我们分为前端和后端不同的团队,甚至更细粒度的团队。后端开发也会根据功能再进行细分。MVC就是其中的一种拆分方式。
随着后端人员不再涉及前端,后端开发又有了新的分层方式。
一、应用分层
1、介绍
阿里开发手册中,关于工程结构部分,定义了常见工程的应用分层结果,如图:
应用分层 是一种软件开发设计思想,它将应用程序分成N个层次,这N个层次分别负责各自的职责,多个层次之间协同提供完整的功能。根据项目的复杂度,把项目分成三层,四层或者更多层。其中常见的MVC设计模式,就是应用分层的一种具体体现。
(1)为什么需要应用分层?
在最开始的时候,为了让项目快速上线,我们通常是不考虑分层的。但是随着业务越来越复杂,大量的代码混在一起,会出现逻辑不清楚、代码扩展性插、改一处导致处处改等问题。学习对项目的分层也是程序员的必修课。
(2)如何分层?(三层架构)
前面学习了MVC,把整体分成了三个层次:View(视图)、Controller(控制器)、Model(模型),也就是将用户视图和业务处理隔离开,并且通过控制器连接起来,很好的实现了表现和逻辑的解耦。是一种标准的软件分层架构。如图:
目前现在更主流的开发方式是 “前后端分离” 的方式,后端开发工程师不再需要关注前端的实现,所以对于Java后端开发者,又有了一种新的分层架构:把整体架构分为表现层、业务逻辑层、数据层。这种分层方式也称之为 “三层架构”。
1、表现层:就是展示数据结果和接受用户指令的,是最靠近用户的一层。
2、业务逻辑层:负责处理业务逻辑,里面有复杂业务的具体实现。
3、数据层:负责存储和管理应用程序相关的数据。
之前写的图书管理系统,就没有按照上述的设计思想,而是代码大杂烩,如图:
按照上面的层次划分,Spring MVC 站在后端开发人员的角度上,也进行了支持,把上述代码划分为三个部分:
1、请求处理、响应数据:负责接受页面的请求,给页面响应数据。
2、逻辑处理:负责业务逻辑处理的代码。
3、数据访问:负责业务数据的维护操作,包括增、删、改、查。
这三个部分,在Spring的实现中,都有体现,如图:
1、Controller:控制器。接收前端发送的请求,对请求进行处理,并且响应数据。
2、Service:业务逻辑层。处理具体的业务逻辑。
3、Dao:数据访问层,也称为持久层。负责数据访问操作,包括数据的增、删、改、查。
MVC 和 三层架构的区别和联系
从概念上来说,二者都是软件工程领域中的架构模式。
MVC架构模式由三部分组成:View(视图)、Controller(控制器)、Model(模型)
三层架构将业务应用划分为:表现层、业务逻辑层、数据访问层
MVC中的视图和控制器对应三层架构中的表现层。MVC中的模型对应三层架构中的业务逻辑层、数据层、实体类。
二者都是从不同角度对软件工程进行了抽象。
MVC模式强调数据和视图分离,将数据展示和数据处理分开。控制器是它们之间的桥梁,通过控制器对两者进行组合。
三层架构强调不同维度数据处理的高内聚和低耦合,将交互界面、业务处理、数据库操作的逻辑分开。
角度不同也就谈不上互相替代了,在日常的开发中,可以经常看到两种共存的情况。比如我们设计模式的时候往往也会拆分出业务逻辑层(Service层)和数据访问层(到层)。
但两者的目的都是相同的:都是为了“解耦,分层,代码复用”。、
软件设计原则:高内聚低耦合
高内聚:
一个模块中各个元素之间的联系的紧密程度,如果各个元素(语句、程序段)之间的联系程度越高,则内聚性越高,即 “高内聚”。
低耦合:
软件中各个层、模块之间的依赖关联程序越低越好。修改一处代码,其他模块的代码改动越少越好。
那么高内聚和低耦合矛盾吗?其实不矛盾,高内聚是指一个模块中的各个元素之间的紧密程度,低耦合是指各个模块之间的精密程度。如图:
好比在公司里,不同部门之间的关联性要尽可能小,一个小部分发生问题了,要尽可能降低对其他部门的影响,这就是低耦合;而当一个部门里出现问题时,这个部门之间的员工关系要经可能的紧密,遇到问题一起解决、克服,这就是高内聚。
2、代码重构
上篇博客写了图书管理系统,但是代码非常乱,现在进行代码重构,先创建对应包的路径,如图:
controller:表现层
@RestController @RequestMapping("/book") public class BookController { @RequestMapping("/getBookList") public List<BookInfo> getBookList() { BookService bookService = new BookService(); return bookService.getBookList(); } }
@RestController @RequestMapping("/user") public class UserController { @RequestMapping("/login") public String login(String userName, String password, HttpSession session) { //1、校验参数 //2、校验密码是否正确 //3、返回响应结果 System.out.println(userName + " " + password); if(!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)) { return "用户名或者密码为空"; } if(!"admin".equals(userName) || !"admin".equals(password)) { return "账号或密码错误"; } session.setAttribute("userName", userName); return ""; } }
原本这里面的数据也是要从数据库里拿的,还有逻辑判断,但是还没学到数据库的使用,先不管。
service:业务逻辑层
public class BookService { public List<BookInfo> getBookList() { BookDao bookDao = new BookDao(); List<BookInfo> bookInfos = bookDao.mockData(); for(BookInfo bookInfo : bookInfos) { if(bookInfo.getStatus() == 2) { bookInfo.setStatusCN("不可借阅"); } else { bookInfo.setStatusCN("可借阅"); } } return bookInfos; } }
dao:数据层
public class BookDao { public List<BookInfo> mockData() { //理论上应该从数据库中获取数据,当前采用mock方式 List<BookInfo> bookInfos = new ArrayList<>(); for (int i = 1; i <= 15; i++) { BookInfo bookInfo = new BookInfo(); bookInfo.setId(i); bookInfo.setBookName("图书" + i); bookInfo.setAuthor("作者" + i); bookInfo.setNum(i * 2 + 1); bookInfo.setPrice(new BigDecimal(i * 3)); bookInfo.setPublishName("出版社" + i); if(i % 5 == 0) { bookInfo.setStatus(2); // bookInfo.setStatusCN("不可借阅"); } else { bookInfo.setStatus(1); // bookInfo.setStatusCN("可借阅"); } bookInfos.add(bookInfo); } return bookInfos; } }
model:实体类
@Data public class BookInfo { private Integer id; private String bookName; private String author; private Integer num; private BigDecimal price; private String publishName; private Integer status;//1-可借阅 2-不可借阅 private String statusCN;//状态的中文含义 }
3、应用分层的好处
(1)降低层与层之间的依赖,结构更加的明确,利于各层逻辑的复用。
(2)开发人员可以只关注整个结构中的其中某一层,极大地降低了维护成本和维护时间。
(3)可以很容易的用新的实现来替换原有层次的实现。
(4)有利于标准化。
二、企业规范
1、类名使用大驼峰风格,但以下情形例外:DO / BO / DTO / VO / AO。
2、方法名、参数名、成员变量、局部变量统一使用小驼峰风格。
3、包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。
常见命名风格:
大驼峰:所有单词首字母都需要大写,又叫帕斯卡命名发,比如:UserController。
小驼峰:除了第一个单词,其他单词首字母大写,比如:userController。
蛇形:用下划线(_)作用单词间的分隔符,一般小写,又叫下划线命名法,比如user_controller。
串形:用短横线(-)作用单词间的分隔符,又叫脊柱命名法,例如:user-controller。
三、学习Spring MVC的总结
1、学校Spring MVC,其实就是学习各种Web开发需要用到的注解:
a、@RequestMapping:路由映射
b、@RequestParam:后端参数重命名
c、@RequestBody:接收JSON类型的参数
d、@PathVariable:接收路径参数
e、@RequestPart:上传文件
f、@ResponseBody:返回数据
g、@CookieValue:从Cookie中获取值
h、@SessionAttribute:从Session中获取值
i、@RequestHeader:从Header中获取值
j、@Controller:定义一个控制器,Spring框架启动时加载,把这个对象交给Spring管理。默认返回视图。
k、@RestController:@ResponseBody + @Controller 返回数据
2、Cookie和Session都是会话机制,Cookie是客户端机制,Session是服务端机制。二者通过SessionId来关联。Spring MVC 内置 HttpServletRequest,HttpServletResponse两个对象。需要使用时,直接在方法中添加对应参数即可,Cookie和Session可以从HttpServletRequest中来获取,也可以直接使用HttpServletResponse设置Http响应状态码。
3、JavaEE 学习阶段会涉及较多工具,插件的学习,来帮助我们提高开发效率,比如Postman、lombok、EditStarter,后面还会继续学习其他的工具或插件。