模式的发展
最开始,在使用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;
}
}