博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java Web应用的三层架构
阅读量:4090 次
发布时间:2019-05-25

本文共 5856 字,大约阅读时间需要 19 分钟。

发表在

什么是三层架构

现在,我们已经实现了博客系统的一些基本功能,但是,很明显,我们的代码依然存在很多的问题,有很多的相似代码,有些逻辑是通用的,但是写在了各个Controller中,每个Controller都有一大坨代码,不利于维护等等。

接下来,我们就来使用经典的三层架构重构我们的代码,使我们的代码更加优雅、简洁、规范。

三层架构共包含以下三层:

  • 表示层

    表示层属于最接近用户的一层,用于展示数据、接收用数据以及为用户提供交互的界面。我们之前写的所有Controller都属于展示层的内容。

  • 业务逻辑层

    业务逻辑层里包含了所有的业务逻辑。它在表示层与数据访问层之间起到了承上启下的作用,是三层架构中的重要组成部分。

  • 数据访问层

    数据访问层负责对数据库进行访问控制,所有与数据库交互的逻辑在这一层当中。

为什么要使用三层架构

目前我们的系统功能特别的简单,但是我们的代码已经特别的复杂,而且特别不利于维护。举个例子来说,此时,我们需要改变数据库的名称,我们需要修改所有Controller中有关数据库连接的代码。很明显,我们需要将数据库相关的代码放到一起,这就是三层架构中的数据访问层。除此之外,使用三层架构还有以下优点:

  • 开发人员可以只关注整个结构中的其中某一层
  • 可以很容易的用新的实现来替换原有层次的实现
  • 可以降低层与层之间的依赖
  • 有利于标准化
  • 利于各层逻辑的复用
  • 结构更加的明确
  • 在后期维护的时候,极大地降低了维护成本和维护时间

数据访问层的设计

数据访问层负责和数据库之间进行交互,对数据进行增删改查,也就是我们常说的CRUD。

在写具体的类之前,我们先来看看数据库操作的公共逻辑:

  • 建立数据库连接,获取Statement
  • 处理返回结果
  • 处理数据库访问错误
  • 释放连接

我们将这些公共逻辑抽成一个公共的DaoHelper类:

public class DaoHelper {    private static String DRIVER_NAME = "com.mysql.jdbc.Driver";    private static String DB_URL = "jdbc:mysql://127.0.0.1:3306/tianmayingblog";    private static String DB_USERNAME = "root";    private static String DB_PASSWORD = "";    public static ResultSet executeQuery(String sql) {        System.out.println(sql);        Connection con = null; // 定义一个MYSQL链接对象        try {            Class.forName(DRIVER_NAME).newInstance(); // MYSQL驱动            con = DriverManager.getConnection(DB_URL, DB_USERNAME, DB_PASSWORD); // 链接本地MYSQL            Statement stmt; // 创建声明            stmt = con.createStatement();            return stmt.executeQuery(sql);        } catch (Exception e) {            System.out.print("MYSQL ERROR:" + e.getMessage());            return null;        }    }    public static boolean executeUpdate(String sql) {        System.out.println(sql);        Connection con = null; // 定义一个MYSQL链接对象        Statement stmt = null; // 创建声明        try {            Class.forName(DRIVER_NAME).newInstance(); // MYSQL驱动            con = DriverManager.getConnection(DB_URL, DB_USERNAME, DB_PASSWORD); // 链接本地MYSQL            stmt = con.createStatement();            stmt.executeUpdate(sql);            return true;        } catch (Exception e) {            System.out.print("MYSQL ERROR:" + e.getMessage());            return false;        } finally {            try {                if (stmt != null) stmt.close();                if (con != null) con.close();            } catch (SQLException ignored) {            }        }    }}

有了DaoHelper后,我们在Dao层中就能只关注具体的SQL语句以及返回结果的处理。

在Dao层中我们需要实现基本的增删改查方法以及其他查询方法,需要哪些方法可以根据具体的业务逻辑而定,例如在UserDao中,除了增删改查外,还需要findByUsername方法:

public class UserDao {    public boolean create(User t) {        return DaoHelper.executeUpdate(String.format(                "insert into user(username, password, email, title, description, avatar) values('%s', '%s', '%s', '%s', '%s', 'images/default-avatar.jpeg')",                t.getUsername(), t.getPassword(), t.getEmail(), t.getTitle(), t.getDescription()));    }    protected User resultToUser(ResultSet rs) throws SQLException {        User user = new User();        user.setAvatar(rs.getString("avatar"));        user.setDescription(rs.getString("description"));        user.setEmail(rs.getString("email"));        user.setId(rs.getLong("id"));        user.setPassword(rs.getString("password"));        user.setTitle(rs.getString("title"));        user.setUsername(rs.getString("username"));        return user;    }    protected List
result2Users(ResultSet rs) { List
users = new ArrayList<>(); try { while (rs.next()) { User user = resultToUser(rs); if (user != null) { users.add(user); } } } catch (SQLException e) { e.printStackTrace(); } return users; } public User findByUsername(String username) { ResultSet resultSet = DaoHelper .executeQuery(String.format("select * from `user` where username = '%s'", username)); List
users = result2Users(resultSet); if (users.isEmpty()) { return null; } return users.get(0); } public User findById(long id) { ResultSet resultSet = DaoHelper.executeQuery(String.format("select * from `user` where id = %s", id)); List
users = result2Users(resultSet); if (users.isEmpty()) { return null; } return users.get(0); } public List
findAll() { ResultSet resultSet = DaoHelper.executeQuery("select * from `user`"); return result2Users(resultSet); }}

业务逻辑层设计

业务逻辑层只关注具体的业务逻辑,同时调用Dao层的方法进行数据库访问,我们需要根据具体的业务来实现它,例如在UserService中,我们需要实现下列方法:

public class UserService {    private UserDao userDao = new UserDao();    public List
findAll() { return userDao.findAll(); } public User login(String username, String password) { User user = userDao.findByUsername(username); if (user == null) { return null; } if (!user.getPassword().equals(password)) { return null; } return user; } public boolean register(User user) { return userDao.create(user); } public User findByUsername(String username) { return userDao.findByUsername(username); }}

由于我们目前业务逻辑都特别简单,因此我们的方法基本都是对Dao层的简单调用。

表示层

实现完数据访问层和业务逻辑层后,我们的Controller就能专注于与页面相关的逻辑,而不再关心数据访问和其他的业务逻辑了:

@WebServlet("")public class IndexController extends HttpServlet {    private static final long serialVersionUID = 6367588690510002694L;    private UserService userService = new UserService();    @Override    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {        req.setAttribute("users", userService.findAll());        RequestDispatcher dispatcher = req.getRequestDispatcher("/WEB-INF/templates/index.jsp");        dispatcher.forward(req, resp);    }}

更多文章请访问

你可能感兴趣的文章
react-native-wechat
查看>>
基于云信的react-native聊天系统
查看>>
网易云音乐移动客户端Vue.js
查看>>
ES7 await/async
查看>>
ES7的Async/Await
查看>>
React Native WebView组件实现的BarCode(条形码)、(QRCode)二维码
查看>>
每个人都能做的网易云音乐[vue全家桶]
查看>>
JavaScript专题之数组去重
查看>>
Immutable.js 以及在 react+redux 项目中的实践
查看>>
Vue2.0全家桶仿腾讯课堂(移动端)
查看>>
React+Redux系列教程
查看>>
react-native 自定义倒计时按钮
查看>>
19 个 JavaScript 常用的简写技术
查看>>
ES6这些就够了
查看>>
微信小程序:支付系列专辑(开发指南+精品Demo)
查看>>
iOS应用间相互跳转
查看>>
iOS开发之支付宝集成
查看>>
iOS开发 支付之银联支付集成
查看>>
iOS开发支付集成之微信支付
查看>>
浅谈JavaScript--声明提升
查看>>