三层架构,用户注册邮箱激活代码流程

模式的发展

最开始,在使用Servlet实现动态页面时,都要在Servlet中嵌入HTML代码输出显示,使得维护成本极大,为了解决这种问题就有了Jsp做页面输出,然而Jsp本质也是Servlet,所以不可避免的要写一些Java代码做脚本,为了不让Jsp内容混乱,就增加了标签,EL表达式来尽量代替Jsp中的Java代码。

MVC模式
  • 模型(Model)
    代表业务逻辑代码与数据库代码,通过JavaBean,传递存储数据。

  • 视图(View)
    代表对数据的展示代码,用于显示模型中的数据,向控制器提交数据,如Jsp。

  • 控制器(Controller)
    通常由Servlet充当控制器,连接着View和Model。

1.  从页面View中获得请求参数
2.  再从Model中通过业务逻辑代码获取需要的数据,通过Bean传递
3.  然后再交给View去显示,更新页面
下面通过一个简单的在A界面输入用户信息,传递给B界面显示,看一下MVC的工作流程。

Model
public class UserBean {

    private String name;

    public UserBean() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}


View:界面A.jsp,用于输入用户名,发起请求
<html>
  <head>
    <title>A</title>
  </head>
  <body>
    <form action="/a" method="get">
      <input type="text" name="name" placeholder="用户名">
      <input type="submit" value="提交">
    </form>
  </body>
</html>


Controller:用于获取界面A中用户输入的用户名,将数据封装到JavaBean中,转发给显示界面。
@WebServlet(value = "/a")
public class AServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {

        UserBean userBean = new UserBean();
        userBean.setName(req.getParameter("name"));
        req.getSession().setAttribute("user",userBean);
        resp.sendRedirect("show_user_info.jsp");
    }
}

View:显示界面B.jsp,用于显示界面A传递过来的用户输入的名称
<jsp:useBean id="user" type="com.bean.UserBean" scope="session"/>
<html>
<head>
    <title>JSPDemo</title>
</head>
<body>
    <p>
        提交的用户名为:<jsp:getProperty name="user" property="name" />
    </p>
</body>
</html>
三层架构
  • Web层(表示层)
    用于获取用户输入的参数,通知Service层处理数据,根据Service的返回结果,控制跳转页面展示数据。

  • service层(业务逻辑层)
    连接Web层与Dao层,根据Dao返回的数据库结果,设置数据返回给Web层的请求结果。

  • dao层(数据访问层)
    操作数据库的CRUD,将结果返回给Service。

用户注册邮箱激活代码流程

注册激活流程图
前置工作
用户数据表的设计
domain用来传递用户数据
public class UserBean {
    private String uid; //用户ID
    private String username;
    private String password;
    private String name;
    private String email;
    private String telephone;
    private Date birthday;
    private String sex;
    private int state; //账号激活状态
    private String code; /
}
分包
注册页面
前台表单提交及校验
<form id="register_form" action="/register" method="post">
      <div class="form-group input-group">
         <span class="input-group-addon">昵称</span>
         <input type="text" name="username" class="form-control" placeholder="用户名">
       </div>
</form>

注意
控件的name最好和domain里面bean的名称一致
bean的属性名称和数据库的字段名称一致
使用jquery.validate.min.js进行前台校验

1.  通过表单id找到表单进行校验
2.  用ajax向后台请求查询数据库,用户数量,通过Json将数据返回给前台

<script type="text/javascript">

        //自定义校验
        $.validator.addMethod(
            "checkUserName", //自定义校验rules
            function (value, element, params) {

                var flag = false;
                $.ajax({
                    "async": false,
                    "url": "/checkUserName",
                    "data": {"username": value},
                    "type": "POST",
                    "dataType": "json",
                    "success": function (data) {
                        flag = data.isExists;
                    }
                })
                //如果返回false代表该校验器不通过
                return !flag;
        });

        
    $(function () {
    $("#register_form").validate({
            rules:{
                "username":{
                    "required": true,
                    "checkUserName": true
                },
                "password": {
                    "required": true,
                    "rangelength": [6, 12]
                },
                "repassword": {
                    "required": true,
                    "rangelength": [6, 12],
                    "equalTo": "#password_id"
                },
                "email": {
                    "required": true,
                    "email": true
                },
                "sex": {
                    "required": true
                }
            },
            messages:{
                "username": {
                    "required": "用户名不能为空",
                    "checkUserName": "该昵称已存在"
                },
                "password": {
                    "required": "密码不能为空",
                    "rangelength": "密码长度6-12位"
                },
               "repassword": {
                    "required": "密码不能为空",
                    "rangelength": "密码长度6-12位",
                    "equalTo": "两次密码不一致"
               },
               "email": {
                    "required": "邮箱不能为空",
                    "email": "邮箱格式不正确"
                },
                "sex": {
                    "required": "没有第三种选择"
                }
            }
        });
    });
</script>
1.  获取到用户输入的用户名
2.  调用UserService去查询数据库,Service会返回Boolean告知成功失败
3.  根据Service返回的Boolean将数据传递给前提
public class CheckUserServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {

        String userName = req.getParameter("username");
        UserService userService = new UserService();
        boolean isExists = userService.checkUserExists(userName);
        resp.getWriter().println("{\"isExists\":" + isExists + "}");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        doGet(req, resp);
    }
}


调用UserDao去查询数据库,根据UserDao的返回值判断返回Boolean
public class UserService {

    private UserDao userDao = new UserDao();
    public boolean checkUserExists(String userName) {

        Long aLong = 0L;
        try {
            aLong = userDao.checkUserExists(userName);
        } catch (SQLException e) {
            e.printStackTrace();
        }

        return aLong > 0 ? true : false;
    }
}


查询数据库中的此用户数量,将结果返回给Service
public class UserDao {
    public Long checkUserExists(String userName) throws SQLException {
        QueryRunner runner = new QueryRunner(DataSourceUtils.getDataSource());
        String sql = "select count(*) from t_user where username = ?";
        Long aLong = (Long) runner.query(sql,new ScalarHandler(),userName);
        return aLong;
    }
}

用户注册
Web层

注意
1.  封装Bean时birthday用的是Date数据,BeanUtils框架需要转换parse

public class RegisterServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {

        req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=UTF-8");

        Map<String, String[]> parameters = req.getParameterMap();
        UserBean userBean = new UserBean();
        try {

            //前面封装的Bean,日期是Date类型
            //BeanUtils需要转换一下类型
            ConvertUtils.register(new Converter() {
                @Override
                public Object convert(Class aClass, Object o) {
                    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
                    Date date = null;
                    try {
                        //额外注意
                        date = format.parse(o.toString());
                    } catch (ParseException e) {
                        e.printStackTrace();
                    }
                    return date;
                }
            }, Date.class);

            BeanUtils.populate(userBean, parameters);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }

        //将用户没传递的数据补全
        userBean.setUid(CommonsUtils.getUUID());
        userBean.setState(0);
        String activeCode = CommonsUtils.getUUID();
        userBean.setCode(activeCode);

        //调用Service注册
        UserService userService = new UserService();
        boolean isRegisterSuccess = userService.registerUser(userBean);
        if (isRegisterSuccess) {

            try {
                String emailMsg = "恭喜您注册成功,请点击激活账户" +
                        "<a href='http://localhost:8080/active?activeCode=" + activeCode + "'>" +
                        "http://localhost:8080/active?activeCode=" + activeCode +
                        "</a>";

                MailUtils.sendMail(userBean.getEmail(),emailMsg);
            } catch (MessagingException e) {
                e.printStackTrace();
            }
            resp.sendRedirect("/login.jsp");
        } else {
            resp.getWriter().println("注册失败");
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        doGet(req, resp);
    }
}
Service层
public class UserService {

    private UserDao userDao = new UserDao();
    public boolean registerUser(UserBean userBean) {

        int row = 0;
        try {
            row = userDao.registerUser(userBean);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return row > 0 ? true : false;
    }
}


Dao层
public class UserDao {

    public int registerUser(UserBean userBean) throws SQLException {
        QueryRunner runner = new QueryRunner(DataSourceUtils.getDataSource());
        String sql = "insert into t_user values(?,?,?,?,?,?,?,?,?,?)";
        int row = runner.update(sql,
                userBean.getUid(),
                userBean.getUsername(),
                userBean.getPassword(),
                userBean.getName(),
                userBean.getEmail(),
                userBean.getTelephone(),
                userBean.getBirthday(),
                userBean.getSex(),
                userBean.getState(),
                userBean.getCode());
        return row;
    }
}
邮箱激活注册账号
注册成功后用Mail发送邮件,附带着请求激活地址,和UUID来做激活码。
所谓的邮箱账号激活,其实就是修改用户表中用户的state

public class MailUtils {

    public static void sendMail(String email, String emailMsg)
            throws AddressException, MessagingException {

        // 1.创建一个程序与邮件服务器会话对象 Session
        Properties props = new Properties();
        props.setProperty("mail.transport.protocol", "SMTP");
        props.setProperty("mail.host", "smtp.163.com"); //发邮件的服务器地址
        props.setProperty("mail.smtp.auth", "true");//需要验证,不验证会提示没有权限发送
        //指定验证为true

        // 创建验证器
        Authenticator auth = new Authenticator() {
            public PasswordAuthentication getPasswordAuthentication() {
                //发送邮件的邮箱账户 + 授权码 
                return new PasswordAuthentication("133******53", "******");
            }
        };

        Session session = Session.getInstance(props, auth);

        // 2.创建一个Message,它相当于是邮件内容
        Message message = new MimeMessage(session);

        message.setFrom(new InternetAddress("133******53@163.com")); // 设置发送者

        message.setRecipient(RecipientType.TO, new InternetAddress(email)); // 设置发送方式与接收者

        message.setSubject("用户激活");
        // message.setText("这是一封激活邮件,请<a href='#'>点击</a>");

        message.setContent(emailMsg, "text/html;charset=utf-8");

        // 3.创建 Transport用于将邮件发送
        Transport.send(message);
    }
}
@WebServlet(value = "/active")
public class ActiveServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {

        resp.setContentType("text/html; charset=UTF-8");

        String activeCode = req.getParameter("activeCode");
        UserService userService = new UserService();
        boolean isActiveUser = userService.activeUser(activeCode);
        if (isActiveUser){
            resp.sendRedirect("/login.jsp");
        }else{
            resp.getWriter().println("用户未激活");
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        doGet(req,resp);
    }
}
Service层
public class UserService {

    private UserDao userDao = new UserDao();
    public boolean activeUser(String activeCode) {
        int row = 0;
        try {
            row = userDao.activeUser(activeCode);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return row > 0 ? true : false;
    }
}


Dao层
public class UserDao {
    public int activeUser(String activeCode) throws SQLException {
        QueryRunner runner = new QueryRunner(DataSourceUtils.getDataSource());
        String sql = "update t_user set state = ? where code = ?";
        int update = runner.update(sql, 1, activeCode);
        return update;
    }
}

推荐阅读更多精彩内容

  • IOC 控制反转容器控制程序对象之间的关系,而不是传统实现中,有程序代码之间控制,又名依赖注入。All 类的创建,...
    irckwk1阅读 440评论 0 0
  • 你看远边的海 他方唱罢 彼方登场 没什么不同 风平浪静时 一样是春暖花开 狂风骤雨时 一样是汹涌澎湃 台风?飓风?...
    净安了阅读 189评论 4 4
  • 焰国的一个偏远村庄下沙村,环境优美,土地肥沃,这里的村民都过着自给自足的生活。女人们出门不多,只靠着男人们去...
    11dc85b718a8阅读 109评论 0 1