接:Servlet的日常开发(基于场景)_林纾໌້ᮨ的博客-CSDN博客
目录
观察上个博客中的代码可知,拼接HTML是比较麻烦的(都是字符串操作),需要手写写出所有字符串,可以有两种技术来改善:
1.模板技术(jsp、thymeleaf...)
2.前后端分离(后端只负责提供数据,以JSON格式,前端JS修改DOM树)
提前把HTML的内容放在一个文件中
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>模板</title> </head> <body> <h1>你好 {{ target }}</h1> </body> </html><!--这里写在项目的目录下,不写在webapp目录下-->
- //Template:模板
- //模板模式:提前写好一个文件(template.html)作为模板
- public class WithTemplate {
- public static void main(String[] args) throws IOException {
- try(FileInputStream is=new FileInputStream("template.html")){
- try(Reader reader=new InputStreamReader(is,"UTF-8")){
- char[] buf=new char[1024];
- int n=reader.read(buf);
- String template=new String(buf,0,n);
- String s=template.replace("{{ target }}","世界");
- System.out.println(s);
- }
- }
- }
- }
前端通过ajax技术,从后端(通过HTTP协议)读取数据(数据的格式是JSON为主)
eg:对录入成绩前后端分离
静态资源:

- <!DOCTYPE html>
- <html lang="zh-hans">
- <head>
- <meta charset="UTF-8">
- <title>成绩列表</title>
- </head>
- <body>
- <table>
- <tbody>
- </tbody>
- </table>
- <!--前端要去后端去取一定需要一个js-->
- <script>
- function render(students) {
- var tbody = document.querySelector('tbody');
- for (var i in students) {
- var s = students[i];
- var tr = `<tr><td>${s.姓名}</td><td>${s.成绩}</td></tr>`;
-
- tbody.innerHTML = tbody.innerHTML + tr;
- }
- }
-
- var xhr = new XMLHttpRequest();//构建一个ajax出来
- xhr.open("GET", "/data/grade-list.json");//把这个目录下的数据给我//注意下面动态时的路径写的是/grade-list.json
- //前端只用读取json数据,读取出来后通过DOM树的修改操作把它加到tbody中
- xhr.onload = function() {
- // this.responseText 是 JSON 格式的字符串,是我们得到的响应体
- // 进行 JSON 的反序列(进行JSON解码)
- var students = JSON.parse(this.responseText);
-
- // 进行渲染操作 (渲染:render)(进行DOM树的修改,即渲染操作)
- render(students);
- }
- xhr.send();//发送
- </script>
- </body>
- </html>
-
- /**
- * .json下的静态资源改为动态资源
- **/
- @WebServlet("/grade-list.json")
- public class GradeListJsonServlet extends HttpServlet {
- private final HashMap<String,Integer> gradeMap=new HashMap<>();
-
- //初始化
- @Override
- public void init() throws ServletException {
- gradeMap.put("小A",100);
- gradeMap.put("小B",90);
- gradeMap.put("小C",80);
- }
-
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- //输出:json格式
- resp.setCharacterEncoding("utf-8");
- resp.setContentType("application/json");//json格式的输出
- PrintWriter writer = resp.getWriter();//把小A...这组数据类似拼接成开心超人那些属性那样的格式
- //调用getJSON方法进行拼接操作
- String json=getJSON();
-
- writer.println(json);
- }
-
- //手动写拼接方法
- private String getJSON() {
- StringBuilder sb=new StringBuilder();
- sb.append("[");
- for (Map.Entry<String,Integer> entry: gradeMap.entrySet()) {
- String name= entry.getKey();
- int value=entry.getValue();
- sb.append("{")
- .append("\"姓名\": ")
- .append("\"")
- .append(name)
- .append("\",")
- .append("\"成绩\": ")
- .append(value)
- .append("},");
- }
- sb.delete(sb.length()-1,sb.length());
- sb.append("]");
- return sb.toString();
- }
-
- }
上面改动态资源是用拼接json格式自己拼接的,比较麻烦,可以引用别人提供好的方法去做:引入一个第三方包:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.4</version>
</dependency>
- /**
- * .json下的静态资源改为动态资源
- **/
- @WebServlet("/grade-list.json")
- public class GradeListJsonServlet extends HttpServlet {
- // private final HashMap<String,Integer> gradeMap=new HashMap<>();
- List<Map<String,Object>> gradeList=new ArrayList<>();//list里面放map
- //初始化
- @Override
- public void init() throws ServletException {
- // gradeMap.put("小A",100);
- // gradeMap.put("小B",90);
- // gradeMap.put("小C",80);
- {
- Map<String,Object> s=new HashMap<>();
- s.put("姓名","小王");
- s.put("成绩",100);
- gradeList.add(s);
- }
- {
- Map<String,Object> s=new HashMap<>();
- s.put("姓名","小李");
- s.put("成绩",90);
- gradeList.add(s);
- }
- {
- Map<String,Object> s=new HashMap<>();
- s.put("姓名","小张");
- s.put("成绩",80);
- gradeList.add(s);
- }
- }
-
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- //输出:json格式
- resp.setCharacterEncoding("utf-8");
- resp.setContentType("application/json");//json格式的输出
- PrintWriter writer = resp.getWriter();//把小A...这组数据类似拼接成开心超人那些属性那样的格式
- //调用getJSON方法进行拼接操作
- String json=getJSON();
-
- writer.println(json);
- }
-
- //2.调用别人写好的东西
- //数组或线性表方式存储(要按协议来,map方式存储是不遵守协议的,打印不一样)//期望打印达到的效果是上面静态.json文件中哪个效果
- private String getJSON() {
- ObjectMapper om=new ObjectMapper();
- try{
- // return om.writeValueAsString(gradeList);
- return om.writerWithDefaultPrettyPrinter().writeValueAsString(gradeList);//与上面一样,只是网页显示上带一些换行和缩进,对数据格式没影响
- }catch (JsonProcessingException e){
- throw new RuntimeException(e);
- }
- }
-
- }


小结:index.html通过构建ajax GET(用户直接GET)请求动态资源/grade-list.json,动态资源根据数据修改DOM树(将index.html内容按所想要的方式呈现)
继续改造:
对象和Map其实是一回事,JSON直接支持定义好一个类往里面放东西,所以可以:
class Grade{
姓名;
成绩;
}
List<Grade>
- public class Grade {
- public String 姓名;
- public int 成绩;
- }
- /**
- * .json下的静态资源改为动态资源(动态获取json的一个工程)
- **/
- @WebServlet("/grade-list.json")
- public class GradeListJsonServlet extends HttpServlet {
- // private final HashMap<String,Integer> gradeMap=new HashMap<>();
- List<Map<String,Object>> gradeList=new ArrayList<>();//list里面放map
- List<Grade> gradeList2=new ArrayList<>();
- //初始化
- @Override
- public void init() throws ServletException {
- {
- Grade g=new Grade();
- g.姓名="胡图图";
- g.成绩=60;
- gradeList2.add(g);
- }
- {
- Grade g=new Grade();
- g.姓名="张小丽";
- g.成绩=98;
- gradeList2.add(g);
- }
-
-
- // gradeMap.put("小A",100);
- // gradeMap.put("小B",90);
- // gradeMap.put("小C",80);
- {
- Map<String,Object> s=new HashMap<>();
- s.put("姓名","小王");
- s.put("成绩",100);
- gradeList.add(s);
- }
- {
- Map<String,Object> s=new HashMap<>();
- s.put("姓名","小李");
- s.put("成绩",90);
- gradeList.add(s);
- }
- {
- Map<String,Object> s=new HashMap<>();
- s.put("姓名","小张");
- s.put("成绩",80);
- gradeList.add(s);
- }
- }
-
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- //输出:json格式
- resp.setCharacterEncoding("utf-8");
- resp.setContentType("application/json");//json格式的输出
- PrintWriter writer = resp.getWriter();//把小A...这组数据类似拼接成开心超人那些属性那样的格式
- //调用getJSON方法进行拼接操作
- String json=getJSON();
-
- writer.println(json);
- }
-
- //2.调用别人写好的东西
- //数组或线性表方式存储(要按协议来,map方式存储是不遵守协议的,打印不一样)//期望打印达到的效果是上面静态.json文件中哪个效果
- private String getJSON() {
- ObjectMapper om=new ObjectMapper();
- try{
- //将gradeList2序列化
- return om.writerWithDefaultPrettyPrinter().writeValueAsString(gradeList2);
-
- return om.writeValueAsString(gradeList);
- // return om.writerWithDefaultPrettyPrinter().writeValueAsString(gradeList);//与上面一样,只是网页显示上带一些换行和缩进,对数据格式没影响
- }catch (JsonProcessingException e){
- throw new RuntimeException(e);
- }
- }
- }


问题:一般java中不直接用中文起名,如Grade类中的姓名成绩,一般用英文起名,但是修改代码会出问题,即你在Grade修改成英文时,访问grade-list.json显示修改成功,但是调用index显示是错误显示的(因为html中写的是姓名成绩两个中文)。


解决:通过注解的方式


此时数据全部处理OK,可以将数据改到数据库中(利用之前数据库grades表,直接在数据库中查询),此时代码:
-
- /**
- * GetGradeList:从数据源(目前是MySQL)中,获取对应的数据
- * 只要调用这个方法就可以从数据库把数据查出来
- */
- public class GetGradeList {
- List<Grade> list=new ArrayList<>();
- public List<Grade> getList(){
- try (Connection c= DBUtil.connection()){
- String sql="select name,grade from grades order by id";
- try(PreparedStatement ps=c.prepareStatement(sql)){
- try (ResultSet rs= ps.executeQuery()){
- while (rs.next()){
- Grade grade=new Grade();
- grade.name=rs.getString("name");
- grade.score=rs.getInt("grade");
- list.add(grade);
- }
- }
- }
- }catch (SQLException e){
- throw new RuntimeException(e);
- }
- return list;
- }
- }
- @WebServlet("/grade-list.json")
- public class GradeListJsonServlet extends HttpServlet {
-
- //初始化GetGradeList类:(该类负责从数据库中查出数据并放在了该类的list中返回)
- private final GetGradeList getGradeList=new GetGradeList();
-
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- //输出:json格式
- resp.setCharacterEncoding("utf-8");
- resp.setContentType("application/json");//json格式的输出
- PrintWriter writer = resp.getWriter();//把小A...这组数据类似拼接成开心超人那些属性那样的格式
- //调用getJSON方法进行拼接操作
- String json=getJSON();
-
- writer.println(json);
- }
- //调用别人写好的东西
- //将数据来源改为从数据库拿(与其他没有差别,只是这里数据源出现区别,变成了数据库中读到的数据)
- private String getJSON() {
- ObjectMapper om=new ObjectMapper();
- try{
- List<Grade> list=getGradeList.getList();//获取GetGradeList类的对象getGradeList调用到的数据(name,score)给list
- return om.writerWithDefaultPrettyPrinter().writeValueAsString(list);//将数据序列化(调用别人写好的方法将数据转化为了对应的json格式)
- }catch (JsonProcessingException e){
- throw new RuntimeException(e);
- }
- }
- }


java中一切皆对象,可分成两种:
eg:宫保鸡丁、鱼香肉丝、麻婆豆腐:是加工出来的产品
承载数据重要的是属性,如上面Grade类,类中只有属性name score,完全无方法,这就是承载数据的对象,以数据位主。
| 数据承担的是各种各样的序列化 | 如上面代码json格式的序列化 |
| 数据为什么序列化 | 序列化的目标是存储和搬运(酒厂中的酒才需要搬运,厂房不要) |
| 一般承载数据的对象中的方法 | equals、comparable、hashCode(产品间才有比较的价值) |
eg:前台、服务员、厨师:流程,是职责中的一部分
承载流程重要的是方法,如上面代码GetGradeList和GradeListJsonServlet就是流程,GetGradeList就是从数据库中把对象查出来这个过程,就是个流程/方法。
总而言之,实际开发中,有偏向数据的对象,有偏向属性的对象,一般也没有纯数据或纯方法,讲求一个偏向性来理解。
一般使用流程时主要是单例的
1)MVC:Model(模型-厨师)+View(展示)+Controller(控制器)
2)Controller(前台)+Service(服务员)+DataAccessObject(数据访问对象DAO(厨师))
Controller负责承接HTTP,即对接过来的客户点什么菜,DAO,即dao对象,库管,从数据库(仓库)里面把东西拿出来,Service上面代码无,我们流程简单,只是拿出来就给前台了,若业务复杂,就需要服务员参与。
GetGradeList放在dao层/包下,改名GradeDao,可以给看的人快速知道这是从数据库取东西的一个类,数据放在model层下,model包下放承载数据职责的类,util包下放DBUtil工具。

Model类型对象也被称为DataObject(DO),注意不要和DAO混起来。

之前代码是服务器以json格式把数据给浏览器,浏览器以表单(form)形式将数据交给服务器,但还可以以json方式发送过去,如下:
- <!DOCTYPE html>
- <html lang="zh-hans">
- <head>
- <meta charset="UTF-8">
- <title>只做json数据的发送,即浏览器以json形式把数据发给服务器</title>
- </head>
- <body>
- <button>发送 JSON 到服务器</button>
- <script>
- // 只能使用 ajax 做到
-
- var btn = document.querySelector('button');
- //绑定一个事件
- btn.onclick = function() {
- var students = [
- { 姓名: "小赵", 成绩: 83 },
- { 姓名: "小钱", 成绩: 84 },
- { 姓名: "小李", 成绩: 85 }
- ];
- //写js代码,key可以不加引号,所以姓名那里没加引号
-
- // 1. 先 JS 的数据变成 String 类型 —— JSON 序列化【准备数据】
- // 反序列化: JSON.parse(...);
- // 序列化: JSON.stringify
-
-
- //对students序列化为json格式的内容
- var s = JSON.stringify(students);
- console.log(s);//通过打印观察,s就是一串字符串(JSON格式的)
- console.log(students);//通过打印观察,students就是上面所写的那一组数据
-
- // 2. 发送,因为要在请求体中携带 JSON 数据,所以只能使用 POST 方法
- var xhr = new XMLHttpRequest();
- xhr.open("POST", "/get-json-from-request-body");
- xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8");//设置请求头
- xhr.send(s); // 发送。send 的参数,就是请求体(传入s就代表作为请求体发送)
- }
- </script>
- </body>
- </html>
分析:students和json之间是相互转化的,String(JSON下的/JSON格式)到数据(json格式解析后的内容)是在JS中调的是JSON.parse(...),叫做解序列化过程,数据到String,在JS中是JSON.stringify(...),叫做序列化过程。(数据->String在java中用ObjectMapper.writeValueAsString(...),String->数据在java中用ObjectMapper.readValue(...))

- package com.wy4.servlet;
-
- import com.fasterxml.jackson.databind.ObjectMapper;
- import com.wy4.model.Grade;
-
- import javax.servlet.ServletException;
- import javax.servlet.annotation.WebServlet;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
- import java.io.InputStream;
- import java.util.List;
- /**
- * @author 美女
- * @date 2022/06/28 21:58
- **/
- @WebServlet("/get-json-from-request-body")
- public class GetJsonFromRequestBodyServlet extends HttpServlet {
- //支持的方法是POST方法,因为是POST请求提交的
-
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- //1.请求体放在req.getInputStream()的输入流中
- InputStream inputStream = req.getInputStream();
- //2.利用Jackson进行解析(这个需要导依赖)
- ObjectMapper om=new ObjectMapper();
- //3.3
- List<Grade> list = om.readValue(inputStream, om.getTypeFactory().constructParametricType(List.class, Grade.class));
- for (Grade grade:list) {
- System.out.println(grade);
- }
- //结果:Grade{name='小赵', score=83}
- //Grade{name='小钱', score=84}
- //Grade{name='小李', score=85}
-
- // //3.2. 读一行
- // List list1 = om.readValue(inputStream, List.class);//数据里主要是List.class。至于外面的list会自己帮我解析,最终就会解析出来
- //写的话操作是write,在GradeListJsonServlet类里有
- // //结果:[{姓名=小赵, 成绩=83}, {姓名=小钱, 成绩=84}, {姓名=小李, 成绩=85}]
- // System.out.println(list1);
-
-
- // //3.1.多个读
- // Object o = om.readValue(inputStream, om.getTypeFactory().constructParametricType(List.class, Grade.class));
- // System.out.println(o);
- //结果:[Grade{name='小赵', score=83}, Grade{name='小钱', score=84}, Grade{name='小李', score=85}]
- }
- }
public class Grade {
//注解意思是写到json里面是用姓名or成绩作为它的key
@JsonProperty("姓名")
public String name;
@JsonProperty("成绩")
public int score;
@Override
public String toString() {
return "Grade{" +
"name='" + name + '\'' +
", score=" + score +
'}';
}
}
在前后端分离的场景下:
xxx.html是用户看到的主页面,是入口。页面由多个资源组成,一般就是css做样式资源,js做逻辑资源,除此之外,在前后端分离场景下,数据往往是后端以xxx.json形式提供好的。

eg:其他代码小练习:
- <!DOCTYPE html>
- <html lang="zh-hans">
- <head>
- <meta charset="UTF-8">
- <title>成绩列表</title>
- </head>
- <body>
- <div>
- <input type="text" id="name" name="name" placeholder="姓名">
- <input type="text" id="score" name="score" placeholder="成绩">
- <!--button的type是button时,代表不是提交form表单的功能,我们通过js添加点击事件处理自行控制流程
- (即框架中浏览器->服务器的操作方法2类似,通过js手动把数据序列化,通过POST传出去)-->
- <button type="button">保存</button><!--由于button本身是提交表单,现在不需要,默认type处是submit,
- 现在改成button,默认就不提交了(失去了提交表单功能),type可写可不写【此时就需要js添加点击事件处理来处理它】-->
- </div>
- <table>
- <tbody>
- </tbody>
- </table>
- <!--前端要去后端去取一定需要一个js-->
- <script>
- <!-- function render(students) {-->
- <!-- var tbody = document.querySelector('tbody');-->
- <!-- for (var i in students) {-->
- <!-- var s = students[i];-->
- <!-- var tr = `<tr><td>${s.姓名}</td><td>${s.成绩}</td></tr>`;-->
-
- <!-- tbody.innerHTML = tbody.innerHTML + tr;-->
- <!-- }-->
- <!-- }-->
-
- <!-- var xhr = new XMLHttpRequest();//构建一个ajax出来-->
- <!-- xhr.open("GET", "/grade-list.json");//把这个目录下的数据给我-->
- <!-- //前端只用读取json数据,读取出来后通过DOM树的修改操作把它加到tbody中-->
- <!-- xhr.onload = function() {-->
- <!-- // this.responseText 是 JSON 格式的字符串,是我们得到的响应体-->
- <!-- // 进行 JSON 的反序列(进行JSON解码)-->
- <!-- var students = JSON.parse(this.responseText);-->
-
- <!-- // 进行渲染操作 (渲染:render)(进行DOM树的修改,即渲染操作)-->
- <!-- render(students);-->
- <!-- }-->
- <!-- xhr.send();//发送-->
-
- //通过添加事件处理进行的处理:
- var btn=document.querySelector('button');//找到button
- btn.onclick=function(){
- var name=document.querySelector('#name').value;
- var score=document.querySelector('#score').value;
- var data={
- 姓名:name,
- 成绩:score
- };
- var s=JSON.stringify(data);
- var xhr2=new XMLHttpRequest();
- xhr2.open('post','/save.json');
- xhr2.onload=function(){
- //不考虑错的情况了,所以我们手动重定向【一旦返回代表保存成功,保存成功用一个重定向(这是前端重定向,直接把location一改就会自动重定向)】
- location='/';
- }
- xhr2.send(s);
- }
-
- </script>
- </body>
- </html>
- @WebServlet("/save.json")
- public class SaveServlet extends HttpServlet {
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- ObjectMapper om=new ObjectMapper();
- Grade grade=om.readValue(req.getInputStream(),Grade.class);
- System.out.println(grade);
- //TODO:保存到数据库中
- try(Connection c= DBUtil.connection()){
- String sql="insert into grades(name,grade) values(?,?)";
- try(PreparedStatement ps=c.prepareStatement(sql)){
- ps.setString(1,grade.name);
- ps.setInt(2,grade.score);
- ps.executeUpdate();
- }
- }catch (SQLException e){
- throw new RuntimeException(e);
- }
-
- resp.setCharacterEncoding("utf-8");
- resp.setContentType("application/json");
- PrintWriter writer = resp.getWriter();
- HashMap<String,String> result=new HashMap<>();
- result.put("结果","正确");
- String s=om.writeValueAsString(result);
- writer.println(s);
- }
- }