MVC与三层架构分层
1. 应用分层
应用分层类似公司的组织架构。我们进行项目开发时,最开始功能简单,前后端放在一起开发,但随着项目功能的复杂,项目会被前端和后端不同的团队接手,甚至更细粒度的团队。甚至后端开发也会根据功能再进行细分。MVC就是其中的一种拆分方式。
随着后端人员不再涉及前端,后端开发又有了新的分层方式。
1.1 介绍
阿里开发手册中,关于工程结构部分,定义了常见工程的应用分层结果,如下图所示:
应用分层是一种软件开发设计思想,它将应用程序分成N个层次,这N个层次分别负责各自的职责,多个层次之间协同提供完整的功能。根据项目的复杂度,把项目分成三层,四层或者更多层。其中常见的MVC设计模式,就是应用分层的一种具体体现。
1.1.1 为什么需要应用分层
在最开始的时候,为了让项目快速上线,通常是不考虑分层的。但是随着业务越来越复杂,大量的代码混在一起,会出现逻辑不清楚、代码扩展性插、改一处导致处处改等问题。
1.1.2 如何分层?(三层架构)
前面学习了MVC,把整体分成了三个层次:View(视图)、Controller(控制器)、Model(模型),也就是将用户视图和业务处理隔离开,并且通过控制器连接起来,很好的实现了表现和逻辑的解耦。是一种标准的软件分层架构。如图:
当前更主流的开发方式是 “前后端分离” 的方式,后端开发工程师不再需要关注前端的实现,所以对于Java后端开发者,又有了一种新的分层架构:把整体架构分为表现层、业务逻辑层、数据层。这种分层方式也称之为 “三层架构”。
1、表现层:就是展示数据结果和接受用户指令的,是最靠近用户的一层。
2、业务逻辑层:负责处理业务逻辑,里面有复杂业务的具体实现。
3、数据层:负责存储和管理应用程序相关的数据。
可以看到, 咱们前⾯的代码, 并不符合这种设计思想, ⽽是所有的代码堆砌在⼀起,如下图所示:
按照上面的层次划分,Spring MVC 站在后端开发人员的角度上,把上述代码划分为三个部分:
1、请求处理:响应数据:负责接受页面的请求,给页面响应数据。
2、逻辑处理:负责业务逻辑处理的代码。
3、数据访问:负责业务数据的维护操作,包括增、删、改、查。
这三个部分,在Spring的实现中,都有体现,如下图所示:
1、Controller:控制器。接收前端发送的请求,对请求进行处理,并且响应数据。
2、Service:业务逻辑层。处理具体的业务逻辑。
3、Dao:数据访问层,也称为持久层。负责数据访问操作,包括数据的增、删、改、查。
1.2 MVC和三层架构的区别和联系
从概念上来说,二者都是软件工程领域中的架构模式。
1、MVC架构模式由三部分组成:View(视图)、Controller(控制器)、Model(模型)
2、三层架构将业务应用划分为:表现层、业务逻辑层、数据访问层
MVC中的视图和控制器对应三层架构中的表现层。MVC中的模型对应三层架构中的业务逻辑层、数据层、实体类;二者架构图如下所示:
由上图可知,二者都是从不同角度对软件工程进行了抽象。
MVC模式强调数据和视图分离,将数据展示和数据处理分开。控制器是它们之间的桥梁,通过控制器对两者进行组合。
三层架构强调不同维度数据处理的高内聚和低耦合,将交互界面、业务处理、数据库操作的逻辑分开。
二者开发角度角度不同,故此也就谈不上互相替代了,在日常的开发中,可以经常看到两种共存的情况。比如我们设计模式的时候往往也会拆分出业务逻辑层(Service层)和数据访问层(dao层)。
但两者的目的都是相同的:都是为了“解耦,分层,代码复用”。
1.3 软件设计原则:高内聚低耦合
1.3.1 高内聚
一个模块中各个元素之间的联系的紧密程度,如果各个元素(语句、程序段)之间的联系程度越高,则内聚性越高,即 “高内聚”。
1.3.2 低耦合
软件中各个层、模块之间的依赖关联程序越低越好。修改一处代码,其他模块的代码改动越少越好。
那么高内聚和低耦合矛盾吗?其实不矛盾,高内聚是指一个模块中的各个元素之间的紧密程度,低耦合是指各个模块之间的精密程度。如图:
好比在大明1566朝堂里,清流和严党之间的关联性要尽可能小,严党出问题了,清流党要尽可能减少两党之间的关系,反之被牵连,这就是低耦合;而当严党自己内部出了问题时,整个党派之间的所有成员要紧密的团结在一起,一起解决、克服已经出现的问题,这就是高内聚。
2、代码重构
上篇写了图书管理系统,但是代码非常乱,现在进行代码重构,先创建对应包的路径,如图:
2.1 表现层
接收前端发送的请求,对请求进⾏处理,并响应数据;
package com.example.zxslzw2014_8_11.controller;import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpSession;@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(!"smy".equals(userName) || !"111".equals(password)) {return "账号或密码错误";}session.setAttribute("userName", userName);return "登录成功";}
}
package com.example.zxslzw2014_8_11.controller;import com.example.zxslzw2014_8_11.model.BookInfo;
import com.example.zxslzw2014_8_11.service.BookService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController
@RequestMapping("/book")
public class BookController {@RequestMapping("/getBookList")public List<BookInfo> getBookList() {BookService bookService = new BookService();return bookService.getBookList();}
}
2.2 service业务逻辑层
package com.example.zxslzw2014_8_11.service;import com.example.zxslzw2014_8_11.dao.BookDao;
import com.example.zxslzw2014_8_11.model.BookInfo;import java.util.List;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;}
}
2.3 dao:数据访问层
数据访问层: 负责数据访问操作,包括数据的增、删、改、查
package com.example.zxslzw2014_8_11.dao;import com.example.zxslzw2014_8_11.model.BookInfo;import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;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;}
}
2.4 model:实体类
package com.example.zxslzw2014_8_11.model;import lombok.Data;import java.math.BigDecimal;@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;//状态的中文含义
}
2.5 应用分层的好处
(1)降低层与层之间的依赖,结构更加的明确,利于各层逻辑的复用。
(2)开发人员可以只关注整个结构中的其中某一层,极大地降低了维护成本和维护时间。
(3)可以很容易的用新的实现来替换原有层次的实现。
(4)有利于标准化。
3.企业规范
1、类名使用大驼峰风格,但以下情形例外:DO / BO / DTO / VO / AO。
2、方法名、参数名、成员变量、局部变量统一使用小驼峰风格。
3、包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。
常见命名风格:
大驼峰:所有单词首字母都需要大写,又叫帕斯卡命名发,比如:UserController。
小驼峰:除了第一个单词,其他单词首字母大写,比如:userController。
蛇形:用下划线(_)作用单词间的分隔符,一般小写,又叫下划线命名法,比如user_controller。
串形:用短横线(-)作用单词间的分隔符,又叫脊柱命名法,例如:user-controller。
4. 学习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响应状态码。
ps:本次的内容就到这里了,如果对你有所帮助的话,就请一键三连哦!!!
本文的封面来自:bilibili苏杉杉的pv,侵权删 url:https://www.bilibili.com/video/BV1vo4y167eh/?spm_id_from=333.999.0.0&vd_source=866da5be2ef0ddd213b053523da53138
————————————————
电子签名:上嘉路