第二讲 系统框架设计-后端篇

第二讲 系统框架设计-后端篇


一、前序准备

1、开发环境推荐

开发工具:IDEA 最新版

服务器:tomcat8 以上(tomcat8servlet api 版本不能太高,下面会提到)

项目管理工具:maven

2、My开发环境

开发工具:IDEA2021

tomcat服务器:tomcat8.5.69

项目管理工具:maven

二、新建项目

1、新建项目

打开 IDEA 2021 , 点击新建,选择 Java Enterprise, 输入项目名 news_frontend ,点击下一步,然后点击完成

在这里插入图片描述

在这里插入图片描述

注意:IDEA2021 新建Java Web项目必须使用项目管理工具maven或者gradle,我也推荐大家使用这两种工具中的其中一种管理项目依赖

2、目录结构调整

  • 删除 src 下的 test 目录
  • 删除 webapp 下的 index.jsp
  • 删除 项目包下的 HelloServlet
  • java 目录下新建以下包
    • dao :存放数据库访问对象
    • filter :存放过滤器
    • servlet:存放servlet
    • test:存放测试类
    • utils:存放工具类
    • vo:存放返回的值对象
  • resources 目录下新建 db.properties 文件,方便配置数据库连接信息

3、配置依赖

注意:servlet-api,现在用4.0.1没问题,后续出问题后我们会更换,为了演示问题,所以我们这里不改

<properties>
  <maven.compiler.target>1.8</maven.compiler.target>
  <maven.compiler.source>1.8</maven.compiler.source>
</properties>

<dependencies>
  <!-- servlet,现在用4.0.1没问题,后续出问题后我们会更换,为了演示问题,所以我们这里不改 -->
  <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
    <scope>provided</scope>
  </dependency>
  <!-- junit单元测试 -->
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
  </dependency>
  <!-- mysql驱动,如果是8.0请导入8.0的版本,同时记得修改db.properities -->
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
  </dependency>
  <!-- gson,用于对象转json字符串 -->
  <dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.5</version>
  </dependency>
</dependencies>
复制代码

4、配置数据库连接信息 db.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/news_demo?characterEncoding=utf8&useSSL=false&serverTimezone=UTC
username=root
password=root
复制代码

5、编写数据库工具类

utils 包下新建类 DatabaseUtil

注意:DatabaseUtil 的代码与之前相比有所变化,主要是读取resources目录下的 db.properties 逻辑有所改变,现在使用 maven 项目的话,我们采用类加载器的方式获取 db.properties

package com.example.demo.utils;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

/**
 * 数据库连接工具类
 */
public class DatabaseUtil {
  /**
     * 获得数据库的连接
     *
     * @return 数据库的连接
     */
  public static Connection getConnection() throws IOException {

    // 获取配置文件
    InputStream in = DatabaseUtil.class.getClassLoader().getResourceAsStream("db.properties");

    // 解析配置文件
    Properties properties = new Properties();
    properties.load(in);
    assert in != null;
    in.close();

    // 获取配置文件中数据库连接相应字段的值
    String driver = properties.getProperty("driver");
    String url = properties.getProperty("url");
    String username = properties.getProperty("username");
    String password = properties.getProperty("password");

    // 获取数据库连接
    Connection connection = null;
    try {
      Class.forName(driver);
      connection = DriverManager.getConnection(url, username, password);
    } catch (Exception e) {
      e.printStackTrace();
    }
    return connection;
  }

  /**
     * 关闭数据库连接
     *
     * @param resultSet   结果集
     * @param statement   sql执行器
     * @param connection 连接
     */
  public static void close(ResultSet resultSet, Statement statement, Connection connection) {
    if (resultSet != null) {
      try {
        resultSet.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
    if (statement != null) {
      try {
        statement.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
    if (connection != null) {
      try {
        connection.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
  }

  public static void main(String[] args) throws IOException {
    // 测试数据库连接
    System.out.print(DatabaseUtil.getConnection());
  }
}
复制代码

6、编写 Gson 工具类

这里使用 gson ,同类型的还有 fastjsonjackson ,大家都可以尝试

package com.example.demo.utils;

import com.google.gson.Gson;

/**
 * Gson工具类,主要用于获取单例gson
 */
public class GsonUtil {
    private static final Gson gson = new Gson();

    public static Gson getGson() {
        return gson;
    }
}
复制代码

此处运用类似单例模式的设计模式,确保运行时只有一个gson ,避免重复new ,因为Gson 只是个工具,不会经常变化,所以整个应用有一个就够了

7、编写值对象ValueObject

vo 包下新建 ArticleVo 类和 SearchResultVo 类,并生成gettersettertoString 和有参空参构造方法

ArticleVo

package com.example.demo.vo;

public class ArticleVo {
    private long id;
    private String url;
    private String docno;
    private String title;
    private String content;

    @Override
    public String toString() {
        return "ArticleVo{" +
                "id=" + id +
                ", url='" + url + '\'' +
                ", docno='" + docno + '\'' +
                ", title='" + title + '\'' +
                ", content='" + content + '\'' +
                '}';
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getDocno() {
        return docno;
    }

    public void setDocno(String docno) {
        this.docno = docno;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}
复制代码

SearchResultVo

package com.example.demo.vo;

import java.util.List;

public class SearchResultVo {
    private long duration;
    private int size;
    private List<ArticleVo> list;

    public SearchResultVo() {
    }

    public SearchResultVo(long duration, int size, List<ArticleVo> list) {
        this.duration = duration;
        this.size = size;
        this.list = list;
    }

    @Override
    public String toString() {
        return "SearchResultVo{" +
                "duration=" + duration +
                ", size=" + size +
                ", list=" + list +
                '}';
    }

    public long getDuration() {
        return duration;
    }

    public void setDuration(long duration) {
        this.duration = duration;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }

    public List<ArticleVo> getList() {
        return list;
    }

    public void setList(List<ArticleVo> list) {
        this.list = list;
    }
}
复制代码

8、编写数据库访问类DAO

dao 包下新建 ArticleDao

在开发dao过程中发现经常会写大量重复的代码,比如数据库连接,查询一个或者多个,此时可以借助一些工具如 jdbcTemplate , DBUtils 以及连接池,比如阿里的 druid,不过我不打算引入这么多工具,增加学习成本,而且由于业务不是很多很复杂,所以还是尽量用自己写的方式,感兴趣或者有能力的同学可以自行升级~

我们可以封装两个常用的方法,一个是查询一个的,一个是查询多个的

  • 查询一个:
/**
  * 查询一个ArticleVo的通用方法
  *
  * @param sql 查询语句
  * @return 查询到的新闻
  */
public ArticleVo selectOne(String sql) throws Exception {
  try {
    connection = DatabaseUtil.getConnection();
    ps = connection.prepareStatement(sql);
    resultSet = ps.executeQuery();
    ArticleVo articleVo = new ArticleVo();
    while (resultSet.next()) {
      articleVo.setId(resultSet.getLong(1));
      articleVo.setUrl(resultSet.getString(2));
      articleVo.setDocno(resultSet.getString(3));
      articleVo.setTitle(resultSet.getString(4));
      articleVo.setContent(resultSet.getString(5));
    }
    return articleVo;
  } finally {
    DatabaseUtil.close(resultSet, ps, connection);
  }
}
复制代码
  • 查询多个
/**
  * 查询多个ArticleVo通用方法
  *
  * @param sql 查询语句
  * @return 查询到的新闻列表
  */
public List<ArticleVo> selectList(String sql) throws Exception {
  try {
    connection = DatabaseUtil.getConnection();
    ps = connection.prepareStatement(sql);
    resultSet = ps.executeQuery();
    List<ArticleVo> articleVoList = new ArrayList<>();
    while (resultSet.next()) {
      ArticleVo articleVo = new ArticleVo();
      articleVo.setId(resultSet.getLong(1));
      articleVo.setUrl(resultSet.getString(2));
      articleVo.setDocno(resultSet.getString(3));
      articleVo.setTitle(resultSet.getString(4));
      articleVo.setContent(resultSet.getString(5));
      articleVoList.add(articleVo);
    }
    return articleVoList;
  } finally {
    DatabaseUtil.close(resultSet, ps, connection);
  }
}
复制代码

然后定义三个业务方法,并且执行相应的查询语句

/**
  * 根据id查询
  *
  * @param id 新闻id
  * @return 新闻
  */
public ArticleVo getOneById(long id) throws Exception {
  String sql = "select * from article where id = " + id;
  return selectOne(sql);
}

/**
  * 随机获取n条新闻
  *
  * @param n 新闻数量
  * @return  随机获取得到的n条新闻
  */
public List<ArticleVo> getRandomList(int n) throws Exception {
  String sql = "select * from article where id >= (select floor(RAND() * (select MAX(id) from article))) order by id limit " + n;
  return selectList(sql);
}

/**
  * 根据关键词模糊查询新闻
  *
  * @param word 关键词
  * @return 与关键词匹配的文章
  */
public List<ArticleVo> getArticleListByWord(String word) throws Exception {
  String sql = "select * from article where title like '%" + word + "%' or content like '%" + word + "%'";
  return selectList(sql);
}
复制代码

怎么样,系不系比较简洁亿点了

9、测试一下 DAO

这么好的代码怎么能不测试一下?赶紧写几个单元测试~

package com.example.demo.test;

import com.example.demo.dao.ArticleDao;
import org.junit.Test;

public class ArticleDaoTest {
  /**
    * 测试根据id查询
    */
  @Test
  public void testGetOneById() throws Exception {
    ArticleDao articleDao = ArticleDao.getArticleDao();
    System.out.println(articleDao.getOneById(1));
  }

  /**
    * 测试随机推荐10条新闻
    */
  @Test
  public void testRandomList() throws Exception {
    ArticleDao articleDao = ArticleDao.getArticleDao();
    System.out.println(articleDao.getRandomList(10));
  }

  /**
    * 测试根据关键词模糊查询新闻
    */
  @Test
  public void testGetArticleListByWord() throws Exception {
    ArticleDao articleDao = ArticleDao.getArticleDao();
    System.out.println(articleDao.getArticleListByWord("河源").size());
  }
}
复制代码

结果就不截图了,反正都通过了

10、编写 Servlet

由于我们采用注解的方式配置 Servlet,比较方便,所以呢,web.xml 可以不配置,Servlet 的逻辑也比较简单,主要就是获取请求参数,利用DAO 查询数据库,然后利用 GSON 把那个结果转为 JSON,返回结果即可

1、ArticleSearchServlet

package com.example.demo.servlet;

import com.example.demo.dao.ArticleDao;
import com.example.demo.utils.GsonUtil;
import com.example.demo.vo.ArticleVo;
import com.example.demo.vo.SearchResultVo;
import com.google.gson.Gson;

import java.io.*;
import java.util.List;
import javax.servlet.http.*;
import javax.servlet.annotation.*;

@WebServlet(value = "/article-search")
public class ArticleSearchServlet extends HttpServlet {
  	
  // 根据关键词查询数据库
  public List<ArticleVo> searchByWord(String word) throws Exception {
    ArticleDao articleDao = ArticleDao.getArticleDao();
    return articleDao.getArticleListByWord(word);
  }
	
  // 计时、查询和返回查询结果
  public String getSearchResult(String word) throws Exception {
    // 开始计时
    long start = System.currentTimeMillis();
    Gson gson = GsonUtil.getGson();
    List<ArticleVo> articleVoList = searchByWord(word);
    // 结束计时
    long duration = System.currentTimeMillis() - start;
    return gson.toJson(new SearchResultVo(duration, articleVoList.size(), articleVoList));
  }

  public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
    doPost(request, response);
  }

  public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
    response.setContentType("application/json;charset=utf8");
    // 获取关键词
    String word = request.getParameter("word");
    PrintWriter out = response.getWriter();
    try {
      out.println(getSearchResult(word));
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      out.flush();
      out.close();
    }
  }
}
复制代码

response.setContentType("application/json;charset=utf8"); :设置响应体的格式,我们响应的是 JSON 所以这里设置为 application/json,字符编码是 utf8;

request.getParameter(key):获取key参数的值

2、ArticleViewServlet

package com.example.demo.servlet;

import com.example.demo.dao.ArticleDao;
import com.example.demo.utils.GsonUtil;
import com.example.demo.vo.ArticleVo;
import com.google.gson.Gson;

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.PrintWriter;

@WebServlet(value = "/article-view")
public class ArticleViewServlet extends HttpServlet {

  public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
    doPost(request, response);
  }

  public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
    response.setContentType("application/json;charset=utf8");
    // 获取参数,要获取的新闻id
    long id = Long.parseLong(request.getParameter("id"));
    Gson gson = GsonUtil.getGson();
    ArticleDao articleDao = ArticleDao.getArticleDao();
    PrintWriter out = response.getWriter();
    try {
      // 根据id获取新闻
      ArticleVo articleListByWord = articleDao.getOneById(id);
      out.println(gson.toJson(articleListByWord));
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      out.flush();
      out.close();
    }
  }
}
复制代码

3、ArticleRecommendByRandomServlet

package com.example.demo.servlet;

import com.example.demo.dao.ArticleDao;
import com.example.demo.utils.GsonUtil;
import com.example.demo.vo.ArticleVo;
import com.google.gson.Gson;

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.PrintWriter;
import java.util.List;

@WebServlet(value = "/article-random")
public class ArticleRecommendByRandomServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        doPost(request, response);
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("application/json;charset=utf8");
        // 获取参数,要获取的随机新闻条数
        int size = Integer.parseInt(request.getParameter("size"));
        Gson gson = GsonUtil.getGson();
        ArticleDao articleDao = ArticleDao.getArticleDao();
        PrintWriter out = response.getWriter();
        try {
            // 获取随机新闻
            List<ArticleVo> articleListByWord = articleDao.getRandomList(size);
            out.println(gson.toJson(articleListByWord));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            out.flush();
            out.close();
        }
    }

}
复制代码

细心的同学可能会发现,有些代码挺相似的,比如设置返回的内容类型和编码,对象转成json字符串等,而且,对于一些错误情况也没有考虑,只是简单假设情况都是理想的,至于该怎么改进,大家可以动动脑筋,虽说手把手,但是也要留点自行完善和改进的空间~

11、测试

首先测试随机推荐

http://localhost:8080/demo/article-random?size=10
复制代码

在这里插入图片描述

其次测试根据新闻id获取新闻

http://localhost:8080/demo/article-view?id=1
复制代码

在这里插入图片描述

最后测试根据关键词模糊查询新闻

http://localhost:8080/demo/article-search?word=河源
复制代码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xjd9Vu8U-1628140607643)(2.系统框架设计_后端篇.assets/image-20210727131126639.png)]

咦,我的页面怎么和你们不一样,那是因为我装了csdnChrome 插件,会自动格式化 json,当然,装 FeHelper Web 前端助手也可以~

12、项目目录结构

在这里插入图片描述

OK,接下来可以整合前端了

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享