Java EE ----- Spring MVC (下)
文章目录
-
- 📕4. 响应
-
-
- ✏️4.1 返回静态页面
- ✏️4.2 返回数据@ResponseBody
- ✏️4.3 返回HTML代码片段
- ✏️4.4 返回JSON
- ✏️4.5 设置状态码
- ✏️4.6 设置Header(了解)
-
- 📕5. 案例练习
-
-
- ✏️5.1 加法计算器
- ✏️5.2 用户登录
- ✏️5.3 留言板
-
- 📕6. lombok介绍
- 📕7. 应用分层
-
-
- ✏️7.1 使用三层架构进行分层
- ✏️7.2 应用分层的好处
-
上接:从 0 到 1 认识 Spring MVC:核心思想与基本用法(上)
📕4. 响应
在我们前面的代码例子中,都已经设置了响应数据,Http响应结果可以是数据,也可以是静态页面,也可以针对响应设置状态码,Header信息等。
✏️4.1 返回静态页面
创建前端页面 index.html(注意路径)
<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"UTF-8\"><title>Index页面</title></head><body>Hello,Spring MVC,我是Index页面.</body></html>
@RestControllerpublic class indexController(){@RequestMapping(\"/index\")public String index(){return \"/index.html\";}}
结果却发现, 页面未正确返回, http响应把\"/index.html\" 当做了http响应正文的数据 那Spring MVC如何才能识别出来 index.html 是一个静态页面, 并进行返回呢?
我们需要把 @RestController 改为 @Controller
@RestController 和 @Controller 有着什么样的关联和区别呢?
之前我们介绍了MVC模式,后端会返回视图,这是早期时的概念。随着互联网的发展,目前项目开发流行\"前后端分离\"模式, Java主要是用来做后端项目的开发,所以也就 不再处理前端相关的内容了。MVC的概念也逐渐发生了变化,View不再返回视图,而是返回显示视图时需要的数据。如果想返回视图的话, 只需要把@ResponseBody 去掉就可以了, 也就是@Controller
所以前面使用的@RestController 其实是返回的数据. @RestController = @Controller + @ResponseBody
@Controller : 定义一个控制器, Spring 框架启动时加载, 把这个对象交给Spring管理.
@ResponseBody : 定义返回的数据格式为非视图, 返回一个 text/html 信息
✏️4.2 返回数据@ResponseBody
@Controller@ResponseBodypublic class indexController(){@RequestMapping(\"/index\")public String index(){return \"/index.html\";}}
加上@ResponseBody 注解, 该方法就会把 “/index.html” 当做一个数据返回给前端.
@ResponseBody 既是类注解,又是方法注解。
如果作用在类上,表示该类的所有方法,返回的都是数据,如果作用在方法上,表示该方法返回的是数据。也就是说: 在类上添加@ResponseBody 就相当于在所有的方法上添加了@ResponseBody 注解。同样,如果类上有@RestController 注解时 : 表示所有的方法上添加了@ResponseBody 注解,也就是当前类下所有的方法返回值做为响应数据。
✏️4.3 返回HTML代码片段
后端返回数据时, 如果数据中有HTML代码, 也会被浏览器解析
@RestMapping(\"/return/html\")@ReponseBodypublic class returnHtml(){public String return(){return \"Hello,HTML~\"
;}}
通过Fiddler观察响应结果, Content-Type 为text/html
响应中的 Content-Type 常见取值有以下几种:
- text/html : body 数据格式是 HTML
- text/css : body 数据格式是 CSS
- application/javascript : body 数据格式是 JavaScript
- application/json : body 数据格式是 JSON
✏️4.4 返回JSON
Spring MVC 也可以返回JSON,后端方法返回结果为对象。
@RequestMapping(\"/m11\") public Map<String,String> m11(){ Map<String,String> hash = new HashMap<>(); hash.put(\"name\",\"zhuxulong\"); hash.put(\"name1\",\"lijiufang\"); return hash; }
通过Fiddler观察响应结果, Content-Type 为 application/json
✏️4.5 设置状态码
Spring MVC会根据我们方法的返回结果自动设置响应状态码, 程序员也可以手动指定状态码。通过Spring MVC的内置对象HttpServletResponse 提供的方法来进行设置。
@RequestMapping(\"/m12\") public String m12(HttpServletResponse response){ response.setStatus(401); return \"yes\"; }
状态码不影响页面的展示
我们通过Proxyman来抓包发现状态码是401
✏️4.6 设置Header(了解)
Http响应报头也会向客户端传递一些附加信息,比如服务程序的名称,请求的资源已移动到新地址等,如: Content-Type,Local等。这些信息通过@RequestMapping 注解的属性来实现。
设置Content-Type
@RequestMapping(value = \"/m13\",produces = \"application/json\") public String m13(){ return \"{\\\"success\\\":true}\"; }
如果不设置produces , 方法返回结果为String时, Spring MVC默认返回类型, 是text/html.
设置其他Header
设置其他Header的话, 需要使用Spring MVC的内置对象HttpServletResponse 提供的方法来进行设置
@RequestMapping(value = \"/m14\") public String m14(HttpServletResponse response){ response.setHeader(\"myHeader\",\"hahaha\"); return \"yes\"; }
void setHeader(String name, String value) 设置一个带有给定的名称和值的 header. 如果 name 已经存在, 则覆盖旧的值.
通过Proxyman来查看我们设置的结果
📕5. 案例练习
✏️5.1 加法计算器
需求
输入两个整数, 点击\"点击相加\"按钮, 显示计算结果
定义接口
请求路径 : calc/sum
请求方式 : GET/POST
接口描述 : 计算两个整数相加
请求参数
响应数据
Content-Type : text/html
响应内容 : 计算机计算结果:
前端代码
<!DOCTYPE html><html lang=\"en\"><head> <meta charset=\"UTF-8\"> <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"> <title>Document</title></head><body><form action=\"calc/sum\" method=\"post\"> <h1>计算器</h1> 数字1:<input name=\"num1\" type=\"text\"><br> 数字2:<input name=\"num2\" type=\"text\"><br> <input type=\"submit\" value=\" 点击相加 \"></form></body></html>
后端代码
package xulong.com;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestController@RequestMapping(\"/calc\")public class CalcController { @RequestMapping(\"/sum\") public String add(Integer num1,Integer num2){ if(num1==null||num2==null){ return \"输入不合法\"; } return \"计算机计算结果:\"+(num1+num2); }}
✏️5.2 用户登录
1.校验接口
需求
用户输入账号和密码, 后端进行校验密码是否正确
- 如果不正确, 前端进行用户告知
- 如果正确, 跳转到首页. 首页显示当前登录用户
- 后续再访问首页, 可以获取到登录用户信息
定义接口
请求路径 : /user/login
请求方式 : POST
接口描述 : 校验账号密码是否正确
请求参数
响应数据
Content-Type : text/html
响应内容 : true //账号密码验证成功 false//账号密码验证失败
2.查询接口
定义接口
请求路径 : /user/getLoginUser
请求方式 : GET
接口描述 : 查询当前登录的用户
响应数据
Content-Type : text/html
响应内容 : zhangsan
===================================================================================
后端代码
package xulong.com.Login;import jakarta.servlet.http.HttpSession;import org.springframework.util.StringUtils;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestController@RequestMapping(\"/user\")public class LoginController { @RequestMapping(\"/login\") public boolean login(String userName, String password, HttpSession session ){ if(!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)){ return false; } if(\"zhangsan\".equals(userName)&&\"123456\".equals(password)){ session.setAttribute(\"username\",userName); return true; } return false; } @RequestMapping(\"/getLoginUser\") public String getLoginUser(HttpSession session){ String username = (String)session.getAttribute(\"username\"); if(StringUtils.hasLength(username)) { return username; } return \"\"; }}
前端代码
<!DOCTYPE html><html lang=\"en\"><head> <meta charset=\"UTF-8\"> <title>登录页面</title></head><body> <h1>用户登录</h1> 用户名:<input name=\"userName\" type=\"text\" id=\"userName\"><br> 密码:<input name=\"password\" type=\"password\" id=\"password\"><br> <input type=\"button\" value=\"登录\" onclick=\"login()\"> <script src=\"https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js\"></script> <script> function login() { $.ajax({ type: \"post\", url: \"/user/login\", data: { \"userName\": $(\"#userName\").val(), \"password\": $(\"#password\").val() }, success: function (result) { if (result) { location.href = \"/index.html\" } else { alert(\"账号或密码有误.\"); } } }); } </script></body></html>
✏️5.3 留言板
需求
- 输入留言信息, 点击提交. 后端把数据存储起来.
- 页面展示输入的表白墙的信息
接口定义
- 获取全部留言
请求:GET /message/getList
响应: JSON 格式
浏览器给服务器发送一个 GET /message/getList 这样的请求, 就能返回当前一共有哪些留言 记录. 结果以 json 的格式返回过来.
- 发表新留言
请求: body 也为 JSON 格式.
响应: JSON 格式.
我们期望浏览器给服务器发送一个 POST /message/publish 这样的请求, 就能把当前的留言提 交给服务器.
前端代码
<!DOCTYPE html><html lang=\"en\"><head> <meta charset=\"UTF-8\"> <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"> <title>留言板</title> <style> .container { width: 350px; height: 300px; margin: 0 auto; /* border: 1px black solid; */ text-align: center; } .grey { color: grey; } .container .row { width: 350px; height: 40px; display: flex; justify-content: space-between; align-items: center; } .container .row input { width: 260px; height: 30px; } #submit { width: 350px; height: 40px; background-color: orange; color: white; border: none; margin: 10px; border-radius: 5px; font-size: 20px; } </style></head><body> <div class=\"container\"> <h1>留言板</h1> <p class=\"grey\">输入后点击提交, 会将信息显示下方空白处</p> <div class=\"row\"> <span>谁:</span> <input type=\"text\" name=\"\" id=\"from\"> </div> <div class=\"row\"> <span>对谁:</span> <input type=\"text\" name=\"\" id=\"to\"> </div> <div class=\"row\"> <span>说什么:</span> <input type=\"text\" name=\"\" id=\"say\"> </div> <input type=\"button\" value=\"提交\" id=\"submit\" onclick=\"submit()\"> <!-- A 对 B 说: hello --> </div> <script src=\"https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js\"></script> <script> load(); function load() { $.ajax({ type: \"get\", url: \"/message/getList\", success: function (result) { for (var message of result) { var divE = \"\" + message.from + \"对\" + message.to + \"说:\" + message.message + \"\"; $(\".container\").append(divE); } } }); } function submit() { //1. 获取留⾔的内容 var from = $(\'#from\').val(); var to = $(\'#to\').val(); var say = $(\'#say\').val(); if (from == \'\' || to == \'\' || say == \'\') { return; } $.ajax({ type: \"post\", url: \"/message/publish\", data: { from: from, to: to, message: say }, success: function (result) { if (result) { //2. 构造节点 var divE = \"\" + from + \"对\" + to + \"说:\" + say + \"\"; //3. 把节点添加到⻚⾯上 $(\".container\").append(divE); //4. 清空输⼊框的值 $(\'#from\').val(\"\"); $(\'#to\').val(\"\"); $(\'#say\').val(\"\"); } else { alert(\"发表留⾔失败!\"); } } }); } </script></body></html>
后端代码
package xulong.com.Message;import org.springframework.util.StringUtils;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.ArrayList;import java.util.List;@RestController@RequestMapping(\"/message\")public class MessageController { List<MessageInfo> list = new ArrayList<>(); @RequestMapping(\"/getList\") public List<MessageInfo> getList(){ return list; } @RequestMapping(\"/publish\") public boolean punish(@RequestBody MessageInfo messageInfos){ if(StringUtils.hasLength(messageInfos.getFrom()) &&StringUtils.hasLength(messageInfos.getTo()) &&StringUtils.hasLength(messageInfos.getMessage())){ list.add(messageInfos); return true; } return false; }}
📕6. lombok介绍
Lombok是一个Java工具库,通过添加注解的方式,简化Java的开发.
引入依赖
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>
使用
lombok通过一些注解的方式, 可以帮助我们消除一些冗长代码, 使代码看起来简洁一些
比如之前的Person对象 就可以改为
@Datapublic class Person {private int id;private String name;private String password;}
@Data 注解会帮助我们自动一些方法, 包含getter/setter, equals, toString等
更多使用
如果觉得@Data比较粗暴(生成方法太多), lombok也提供了一些更精细粒度的注解
@Data = @Getter + @Setter + @ToString + @EqualsAndHashCode + @RequiredArgsConstructor + @NoArgsConstructor
📕7. 应用分层
通过上面的练习,我们学习了Spring MVC简单功能的开发,但是我们也发现了一些问题,目前我们程序的代码有点\"杂乱\",然而当前只是\"一点点功能\"的开发。如果我们把整个项目功能完成呢? 代码会更加的\"杂乱无章\"(文件乱, 代码内容乱)。也基于此, 咱们接下来学习应用分层.
为什么需要应用分层?
在最开始的时候,为了让项目快速上线,我们通常是不考虑分层的。但是随着业务越来越复杂,大量的代码混在一起,会出现逻辑不清晰、各模块相互依赖、代码扩展性差、改动一处就牵一发而动全身等问题。所以学习对项目进行分层就是我们程序员的必修课了。
类似公司的组织架构
公司初创阶段, 一个人身兼数职, 既做财务, 又做人事, 还有行政.
随着公司的逐渐壮大, 会把岗位进行细分, 划分为财务部门, 人事部门, 行政部门等. 各个部门内部还会 再进行细分.
项目开发也是类似, 最开始功能简单时, 我们前后端放在一起开发, 随着项目功能的复杂, 我们分为前 端和后端不同的团队, 甚至更细粒度的团队. 后端开发也会根据功能再进行细分. MVC就是其中的一种 拆分方式.
但是随着后端人员不再涉及前端, 后端开发又有了新的分层方式.
✏️7.1 使用三层架构进行分层
之前介绍的 “MVC”,就是把整体的系统分成了 Model(模型),View(视图)和Controller (控制器)三个层次,也就是将用户视图和业务处理隔离开,并且通过控制器连接起来,很好地实现了表现和逻辑的解耦,是一种标准的软件分层架构。
目前现在更主流的开发方式是 “前后端分离” 的方式,后端开发工程师不再需要关注前端的实现, 所以对 于Java后端开发者,又有了一种新的分层架构:把整体架构分为表现层、业务逻辑层和数据层。这种分层方式也称之为\"三层架构\"。
- 表现层: 就是展示数据结果和接受用户指令的,是最靠近用户的一层。
- 业务逻辑层:负责处理业务逻辑,里面有复杂业务的具体实现。
- 数据层:负责存储和管理与应用程序相关的数据
按照上面的层次划分, Spring MVC 站在后端开发人员的角度上, 也进行了支持, 把上面的代码划分为三 个部分:
✏️7.2 应用分层的好处
- 降低层与层之间的依赖, 结构更加的明确, 利于各层逻辑的复用
- 开发人员可以只关注整个结构中的其中某一层, 极大地降低了维护成本和维护时间
- 可以很容易的用新的实现来替换原有层次的实现
- 有利于标准化