一、什么是三层架构?
1.1概述
三层架构(3-tier architecture) 就是将整个业务应用划分为:
- 界面层(User Interface layer) UI
- 业务逻辑层(Business Logic Layer) BLL
- 数据访问层(Data Access layer) DAL
1.2表示层
- 表示层,又称界面层,是与用户交互的界面。位于最外层/最上层,离用户最近,用于接收用户输入的数据和显示处理后用户需要的数据。
- 【接受请求,封装数据,调用业务逻辑层,响应数据(servlet,jsp)】
1.3业务逻辑层
- 业务逻辑层,又称服务层,是表示层和数据访问层之间的桥梁。位于中间层,用来处理业务逻辑,具体包含:验证、计算、业务规则等等。
1.4数据访问层
- 数据访问层,是对数据库访问的层。位于最里层/最下层,主要实现对数据的增、删、改、查。将存储在数据库中的数据提交给业务层,同时将业务层处理的数据保存到数据库。
1.5层与层的关系
- 表示层依赖于业务逻辑层
- 业务逻辑层依赖于数据访问层
既然三层各司其责,那么怎么将三层联系起来?
这个时候实体层(Entity) 来了
- Entity(实体层):它不属于三层中的任何一层,但是它是必不可少的一层
- Entity在三层架构中的作用:
- 1、实现面向对象思想中的"封装";
- 2、贯穿于三层,在三层之间传递数据;(注:确切的说实体层贯穿于三层之间,来连接三层)
- 3、每一层(UI—>BLL—>DAL)之间的数据传递(单向)是靠变量或实体作为参数来传递的,这样就构造了三层之间的联系,完成了功能的实现。
二、为什么需要三层架构?
三层架构的出现就是为了“高内聚,低耦合”
内聚:一个模块内各个元素的关联度。 耦合 :各个模块之间的关联度。
- 高内聚 :尽可能一个类/模块只负责一个功能(一个模块内的元素关联度,越容易理解、修改和维护,出错就越少)。
- 低耦合 :模块与模块之间的关联度要尽可能的低,避免牵一发而动全身
- 从类角度来看:高内聚低耦合:减少类内部,对其他类的调用
- 从功能块来看:高内聚低耦合:减少模块之间的交互复杂度
简单来说,就是解耦:减少模块之间的交互复杂度,自己做自己的事
三层: 发生在哪一层的变化,只需更改该层,不需要更改整个系统。层次清晰,分工明确,每层之间耦合度低——提高了效率,适应需求变化,可维护性高,可扩展性高
两层: 当任何一个地方发生变化时,都需要重新开发整个系统。“多层”放在一层,分工不明确耦合度高,难以适应需求变化,可维护性低、可扩展性低
三、层架构的应用
3.1原则
- 上层依赖其下层,依赖关系不跨层
- 表示层不能直接访问数据访问层
- 上层调用下层的结果,取决于下层的实现
- 下一层不能调用上一层
- 下一层不依赖上一层
- 上层的改变不会影响下一层
- 下层的改变会影响上一层得到的结果
- 在上一层中不能出现下一层的概念
- 分工明确,各司其职
3.2例子:实现登录功能
表现层:jsp页面
<body> <div class="container" style="margin-top:140px;height: 100px;"> <div class="row"> <div class="col-md-6 bg-primary"> <h2 class="text-white">欢迎登录</h2> <p class="text-white">商城后台管理系统</p> </div> <div class="col-6 bg-white py-5"> <form action="login.do" method="post" onsubmit="return myLogin()"> <div class="mb-3"> <label for="exampleInputEmail1" class="form-label">邮箱账号</label> <input placeholder="请输入邮箱账号" ptype="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" name="username"> </div> <div class="mb-3"> <label for="exampleInputPassword1" class="form-label">密码</label> <input placeholder="请输入密码" type="password" class="form-control" id="exampleInputPassword1" name="password"> </div> <div class="mb-3"> <label for="exampleInputPassword1" class="form-label">验证码</label> <div class="input-group mb-3 " style="width: 200px;"> <input id="yz" placeholder="请输入验证码" type="text" class="form-control"> <button id="login" type="button" class="btn btn-primary"> <span id="code" class="badge bg-danger">9888</span> </button> </div> </div> <button type="submit" class="btn btn-primary">登录</button> </form> </div> </div> </div> </body>
控制器:Servlet
Servler通常作为控制器位于服务层和表示层之间,处理请求并相应地调用服务层方法执行业务逻辑
@WebServlet("/login.do") public class LoginServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置编码 request.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); // 获取参数 String username = request.getParameter("username"); String password = request.getParameter("password"); // 处理请求 PrintWriter out = response.getWriter(); HttpSession session = request.getSession(); //实例化业务逻辑层对象 IUserService ius=new UserServiceImpl(); User login = ius.login(username, password); System.out.println(login); if (login != null) { session.setAttribute("Login", login); out.println("<script>alert('登录成功');location.href='good.jsp'</script>"); } else { out.println("<script>alert('账号或密码错误');location.href='login.jsp'</script>"); } } }
业务逻辑层:
/** * 业务逻辑层 * 定义接口类 * @author 许潜行 * */ public interface IUserService { /** * 用户登录方法 * @param uname * @param upwd * @return */ public User login(String uname,String upwd); } /** * 业务逻辑层 * 实现接口类 * @author 许潜行 * */ public class UserServiceImpl implements IUserService { //实例化数据访问层对象 private IUsersDao iud=new UsersDaoImpl(); @Override public User login(String uname, String upwd) { return iud.login(uname, upwd); } }
数据访问层:
/** * 数据访问层 * 定义接口 * @author 许潜行 * */ public interface IUsersDao { /** * 用户登录方法 * @param uname * @param upwd * @return */ public User login(String uname,String upwd); } /** * 数据访问层 * 实现接口类 * @author 许潜行 * */ public class UsersDaoImpl extends BaseDao implements IUsersDao { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; public User login(String uname,String upwd) { User u=null; String sql="select * from tb_user where username=? and password=?"; try { rs=this.executeQuery(sql, new Object[] {uname,upwd}); if(rs.next()) { u=new User(rs.getInt(1), rs.getString(2), rs.getString(3)); } } catch (Exception e) { e.printStackTrace(); } finally { DBHelper.myClose(conn, ps, rs); } return u; } public static void main(String[] args) { User login = new UsersDaoImpl().login("user123@qq.com", "a12345"); System.out.println(login); } }
实体类:
public class User { private Integer id; private String username; private String password; public User() { // TODO Auto-generated constructor stub } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public User(Integer id, String username, String password) { super(); this.id = id; this.username = username; this.password = password; } public User(String username, String password) { super(); this.username = username; this.password = password; } @Override public String toString() { return "User [id=" + id + ", username=" + username + ", password=" + password + "]"; } }
BaseDao
/** *增删改通用方法 * @param sql * @param obj * @return */ public int execute(String sql,Object...obj) { int n=0; try { //建立连接 conn=DBHelper.getConn(); //传入sql并执行 ps=conn.prepareStatement(sql); //占位符赋值 if (obj!=null) { for (int i = 0; i < obj.length; i++) { ps.setObject(i+1, obj[i]); } } //返回结果 n=ps.executeUpdate(); } catch (Exception e) { e.printStackTrace(); }finally { //关闭资源 DBHelper.myClose(conn, ps, rs); } return n; } /** * 查询通用方法 * @param sql * @param obj * @return */ public ResultSet executeQuery(String sql,Object...obj) { try { //建立连接 conn=DBHelper.getConn(); //传入sql并执行 ps=conn.prepareStatement(sql); //占位符赋值 if (null!=obj) { for (int i = 0; i < obj.length; i++) { ps.setObject(i+1, obj[i]); } } //返回结果 rs=ps.executeQuery(); } catch (Exception e) { e.printStackTrace(); } return rs; }