zoukankan      html  css  js  c++  java
  • JavaWeb中的MVC

    不使用什么MVC的案例分析:

    利用Servlet与jsp实现登陆请求,数据库查询,以及页面的跳转逻辑

    具体流程如下:

    image-20191214113351941

    不做任何结构上的考虑,可以简单的做如下实现:

    目录结构

    image-20191214113859994

    LoginServlet
    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.sql.*;
    
    @WebServlet("/Login")
    public class LoginServlet extends HttpServlet {
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req,resp);//转发到get 执行相同逻辑
        }
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
            //获取参数
            String name = req.getParameter("username");
            String password = req.getParameter("pwd");
    
    
            String error = null; //错误信息
            boolean flag = false; //是否登陆成功
            //验证数据可靠性
            if(name == null || password == null || name.equals("") || password.equals("")){
                error="用户名和密码不能为空!";
            }else {
                //查询数据库
                try {
                    Class.forName("com.mysql.jdbc.Driver");
                    Connection connect = DriverManager.getConnection("jdbc:mysql://127.0.0.1/db1?charsetEncoding=utf-8&user=root&password=admin");
                    PreparedStatement statement = connect.prepareStatement("select *from user where name = ? and password = ?");
                    statement.setObject(1,name);
                    statement.setObject(2,password);
                    ResultSet set = statement.executeQuery();
                    if(set.next()){
                        flag = true;
                    }else{
                        error="用户名和密码错误!";
                    }
                } catch (ClassNotFoundException | SQLException e1) {
                    error = "服务器忙,请稍后再试!";
                }
            }
    
            if(flag){
                //删除已存在的错误信息,避免后续登录页面展示重复展示
                req.getSession().removeAttribute("error");
                //添加用户名称到session
                req.getSession().setAttribute("user",name);
                //跳转页面
                req.getRequestDispatcher("./index.jsp").forward(req,resp);
            }else{
                //添加错误信息到session
                req.getSession().setAttribute("error",error);
                //跳转页面
                req.getRequestDispatcher("./login.jsp").forward(req,resp);
            }
        }
    }
    
    login.jsp
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
      <head>
        <title>$Title$</title>
      </head>
      <body>
    <span style="color: red">${error}</span>
      <form action="./Login" method="post">
        用户名:<input name="username"/>
        <br>
        密码:<input name="pwd">
        <br>
        <input type="submit" value="登录">
      </form>
      </body>
    </html>
    
    index.jsp
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>主页</title>
    </head>
    <body>
    <h1>你好${user},欢迎登录</h1>
    </body>
    </html>
    

    增加需求

    通过简单的逻辑判断和JDBC就实现了上述需求,接下来我们继续增加需求

    假设现在要增加注册功能,可以这样:

    1. 创建register.jsp
    2. 创建 RegisterServlet
    3. 在RegisterServlet,获取参数,链接数据库,处理注册逻辑

    思考,这样写有什么问题?

    • 按照这样的思路,一个功能就需要一个Servlet,而Servlet的生命周期是非常长的,将会大量消耗内存
    • 如果不创建其他Servlet,那只能将所有URL交个同一个Servlet处理,这样的话就不可避免的需要对请求路劲进行判断,以确定客户端要执行的功能
    • 每个功能都要重复的进行数据库的相关操作,例如连接数据库,提取结果等,出现了大量重复代码

    上述列出的问题,在程序开发中是经常遇到的,所以我们不得不对程序的整体结构进行重新思考和设计

    MVC

    MVC是一种软件构架模式,所谓模式,就是前辈们给我们总结出的针对固定类型问题的已有解决方案,这写方案是经过大量时间,以及大量项目实践,最终总结出的良好的方案;给开发者,提供了非常好的指导思想,在漫长的技术发展过程中,开发者们总结出了,很多好的模式,如<<23中常见模式>>一书中讲到;

    注意:严格来说MVC不属于设计模式,而是构架模式,因为MVC是为程序划分层了次结构,使得程序结构更规范,更合理,降低了耦合度

    针对web项目,MVC就是非常通用的构架模式,到底如何去设计一款MVC的程序呢?

    MVC分层

    为了降低各个功能间的耦合度,提高代码的可维护性,MVC将程序分为了三个层次

    • M:Model 模型,用于作为数据的载体的Bean,通常不包含复杂逻辑,一个Bean对应数据库一条记录
    • V:View 视图,用于展示Model中的数据,和处理用户交互
    • C :Controller 控制器,接受View的输入,根据需要调用Mode,获取数据后再交给View层进行展示

    补充POJO和Bean的区别,POJO更加简单只要求由set/get,而Bean为了满足可重用性,有更多的规范要求

    图示:

    image-20191214123329331

    注意:

    View与Model层之间不允许直接交互,必须由Controller来调度

    Service层

    按照这样的层次结构,将servlet用于Controller,在增加一个Bean类,来装载数据,看起来不错,但是没有解决根本的问题,业务逻辑和数据库操作依然挤在Servlet中,必须在对其进行拆分,于是在MVC基础结构上增加Service层,用于处理业务逻辑,你也可以叫它业务逻辑层(business)

    看起来就像下面这样:

    image-20191214131855074

    你可能会问,Service属于M还是C,它既不是C也不是M,是我们在MVC基础上扩展出来的,我们经常会在已有模式上扩展新的内容这很正常

    使用MVC

    现在对之前的程序进行改造

    • 增加JavaBean,其中的属性与对应的表相同,拥有ID,name,password
    • 增加service包,并创建UserService类,用作业务逻辑层
    • 在控制器Servlet中,调用Service来完成业务功能
    • 根据Service的返回结果来决定页面的跳转地址
    项目结构:

    image-20191214151229600

    UserLoginServlet:
    package com.xx.controller;
    
    import com.xx.exceptions.LoginException;
    import com.xx.models.UserBean;
    import com.xx.services.UserService;
    
    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.sql.SQLException;
    
    @WebServlet("/Login")
    public class UserLoginServlet extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doGet(request,response);
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          	//获取参数
            String name = request.getParameter("username");
            String pwd = request.getParameter("pwd");
            UserBean reqBean = new UserBean(name,pwd);
    				//实例化业务逻辑对象
            UserService service = new UserService();
    
            try {
              	//调用对应方法 根据结构跳转页面
                UserBean resBean = service.CheckLogin(reqBean);
                request.getSession().setAttribute("user",resBean.getName());
                response.sendRedirect(request.getServletContext().getContextPath() +"/index.jsp");
            } catch (LoginException e) {
                request.getSession().setAttribute("error",e.getMessage());
                response.sendRedirect(request.getServletContext().getContextPath() +"/login.jsp");
            }
        }
    }
    
    UserService:
    package com.xx.services;
    
    import com.xx.exceptions.LoginException;
    import com.xx.models.UserBean;
    
    import java.sql.*;
    
    public class UserService {
    
        public UserBean CheckLogin(UserBean userBean) throws LoginException {
            //验证参数有效性
            if (userBean.getPassword() == null || userBean.getPassword() == null ||
                userBean.getPassword().equals("") || userBean.getName().equals("")){
                throw new LoginException("用户名和密码不能为空");
            }
            UserBean bean = null;
            //查询数据库
            try {
                Class.forName("com.mysql.jdbc.Driver");
                Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/db1?characterEncoding=utf8&user=root&password=admin");
                PreparedStatement preparedStatement = connection.prepareStatement("select *from user where name  = ? and password = ?");
                preparedStatement.setObject(1, userBean.getName());
                preparedStatement.setObject(2, userBean.getPassword());
                ResultSet set = preparedStatement.executeQuery();
                if (set.next()) {
                    bean = new UserBean();
                    bean.setId(set.getInt(1));
                    bean.setName(set.getString(2));
                    bean.setPassword(set.getString(3));
                }else {
                    throw new LoginException("用户名或密码错误!");
                }
            }catch (SQLException | ClassNotFoundException e){
                e.printStackTrace();
                throw new LoginException("服务器忙,(数据库炸了)",e);
            }
            return bean;
        }
    }
    

    UserBean仅包含构造器,和setget方法

    LoginException,也只是简单的继承了Exception

    小结:

    MVC是Web应用常用的设计模式,其增强了代码的扩展性,可维护性,降低了各组件的耦合度

    在上述案例中通过MVC分层,代码结构得到了优化,但是细心的你可能已经发现了,

    • 业务逻辑层Service中夹杂着与数据库相关的所有代码,导致代码冗余.重复代码问题依然没有得到解决
    • UserLoginServlet依然只能处理用户的登录请求
  • 相关阅读:
    小说下载器【追书接口】
    C#调用大漠插件,发送QQ和微信消息
    C# 终本案件、综合执行人、裁判文书爬虫
    追书神器API
    一个CookieContainer的拓展类
    利用BlockingCollection实现生产者和消费者队列,实现写文本
    EF SQLite的Like语句,生成为CHARINDEX的解决办法
    ClientKey实现登录QQ空间,并设置背景音乐
    DataTable转换为Model实体对象
    开通博客第一天!
  • 原文地址:https://www.cnblogs.com/yangyuanhu/p/12039682.html
Copyright © 2011-2022 走看看