Web开发分层思想
MVC开发模式:
M:Model模型 JavaBean&四种作用域
V:View视图 JSP
C:Controller控制器 Servlet
不使用框架,使用JSP+JavaBean+Servlet进行开发
但是在实际开发中,我们进行更为细致的划分:
分层思想:强内聚,弱耦合
整个业务流程是这样的:
浏览器即用户向服务器端发起请求,服务器端的控制器Servlet接收到用户的请求,做三件事:
1.获取表单的数据2.调用业务层的业务逻辑3.分发转向,其中调用的业务逻辑,业务层会去调用DAO层的基本增删改查方法,每一层都可以对JavaBean|三个域对象进行操作。最终结果通过JSP,返回给用户浏览器显示。
对于每一层,我们通常会创建如下包:
domain:JavaBean,实体bean,用于封装数据
service:业务层接口,如注册,登录……
service.impl:业务层接口的实现类
dao:dao接口,基本的增删改查方法
dao.impl:dao接口的实现类
servlet:servlet类
utils:工具类
exception:自定义异常类
jsp
其中接口可以看作是功能说明书,其中囊括了所有的方法。
下面写一个用户登录注册的Web小程序
根据上述的分层思想,JavaWeb程序的架构如上图所示。
User:实体Bean,封装数据,其中字段的名称需与数据库字段保持一致
Dao:数据访问层接口
其中的方法都是基本的增删改查,可访问数据库中的数据
Dao.impl:Dao接口的实现类
package com.itdream.dao.impl;
import com.itdream.dao.UserDao;
import com.itdream.domain.User;
import com.itdream.exception.UserExcisException;
//自己写的操作数据库的工具类,具体实现方法见之前写的博文《JDBC技术》
import com.itdream.utils.DBUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.text.SimpleDateFormat;
/**
* Created by Dream on 2017/12/1.
*/
public class UserDaoImpl implements UserDao {
public void insert(User user)throws Exception{
Connection con = null;
PreparedStatement ps = null;
try{
con = DBUtils.getConnection();
String sql = "insert into users (username,password,email,birthday) values(?,?,?,?)";
ps = con.prepareStatement(sql);
ps.setString(1,user.getUsername());
ps.setString(2,user.getPassword());
ps.setString(3,user.getEmail());
SimpleDateFormat spf = new SimpleDateFormat("yyyy-MM-dd");
String date = spf.format(user.getBirthday());
ps.setString(4,date);
int result = ps.executeUpdate();
}catch (Exception e){
e.printStackTrace();
throw new RuntimeException("插入失败!");
}finally {
DBUtils.closeAll(null,ps,con);
}
}
public User select(User user)throws Exception{
Connection con = null;
ResultSet rs = null;
PreparedStatement ps = null;
User u = null;
try{
con = DBUtils.getConnection();
String sql = "select * from users where username = ? and password = ?";
ps = con.prepareStatement(sql);
ps.setString(1,user.getUsername());
ps.setString(2,user.getPassword());
rs = ps.executeQuery();
if(rs.next()){
u = new User();
u.setId(rs.getInt(1));
u.setUsername(rs.getString(2));
u.setPassword(rs.getString(3));
u.setEmail(rs.getString(4));
u.setBirthday(rs.getDate(5));
}
}catch (Exception e){
e.printStackTrace();
}
return u;
}
public boolean selectByName(String name){
Connection con = null;
ResultSet rs = null;
PreparedStatement ps = null;
try{
con = DBUtils.getConnection();
String sql = "select * from users where username = ?";
ps = con.prepareStatement(sql);
ps.setString(1,name);
rs = ps.executeQuery();
if(rs.next()){
return true;
}
}catch (Exception e){
e.printStackTrace();
}
return false;
}
}
service:业务层
service.impl:业务层的实现类
package com.itdream.service.impl;
import com.itdream.dao.UserDao;
import com.itdream.dao.impl.UserDaoImpl;
import com.itdream.domain.User;
import com.itdream.exception.UserExcisException;
import com.itdream.service.UserService;
/**
* Created by Dream on 2017/12/1.
*/
public class UserServiceImpl implements UserService {
//通过调用Dao接口实现类的方法去操纵底层数据库
UserDao userDao = new UserDaoImpl();
public void register(User user)throws Exception{
//注册也就是用户的插入
userDao.insert(user);
}
public User login(User user){
User u = null;
try{
//登录也就是用户的查找
u = userDao.select(user);
}catch (Exception e){
e.printStackTrace();
}
return u;
}
public boolean findUserByName(String name)throws UserExcisException{
boolean b = userDao.selectByName(name);
//用户已经存在,需要抛出异常
if(b){
throw new UserExcisException("用户已经存在!");
}
return b;
}
}
servlet:控制器Servlet类
登录:
public class LogServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)throws IOException,ServletException{
request.setCharacterEncoding("utf-8");
//获取表单数据
User user = new User();
try{
BeanUtils.populate(user,request.getParameterMap());
//调用业务逻辑
UserService us = new UserServiceImpl();
User u = us.login(user);
//如果u不为空,说明登录成功
if(u != null){
request.getSession().setAttribute("u",user);
request.getRequestDispatcher("/index.jsp").forward(request,response);
}else { //否则登录失败,重新登录
response.sendRedirect(request.getContextPath()+"/log.jsp");
}
}catch (Exception e){
e.printStackTrace();
}
}
public void doPost(HttpServletRequest request,HttpServletResponse response)throws IOException,ServletException{
doGet(request,response);
}
}
注册:
我们增加了用户表单注册信息的验证功能,增加了一个实体对象UserForm用于封装用户注册提交的表单数据,其中该对象的valid方法实现对用户注册信息的校验
UserForm类作为实体对象,我们也是放在domain包下,具体实现如下:
package com.itdream.domain;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;
/**
* Created by Dream on 2017/12/2.
*/
public class UserForm {
private int id;
private String username;
private String password;
private String repassword;
private String email;
private String birthday;
Map<String,String> msg = new HashMap<>();
//如果返回的msg不为空,证明是有错误消息的;否则,注册验证通过
public boolean valid(){
if("".equals(username)){
msg.put("username","用户名不能为空!");
}else if(!username.matches("\\w{3,8}")){
msg.put("username","用户名必须为3~8位字母组成!");
}
if("".equals(password)){
msg.put("password","密码不能为空!");
}else if(!password.matches("\\d{3,8}")){
msg.put("password","密码必须为3~8位数字!");
}
if(!password.equals(repassword)){
msg.put("repassword","两次密码必须输入一致!");
}
if("".equals(email)){
msg.put("email","邮箱不能为空!");
}else if(!email.matches("^[a-z0-9]+([._\\\\-]*[a-z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$")){
msg.put("email","必须要符合邮箱的格式输入");
}
if("".equals(birthday)){
msg.put("birthday","生日不能为空!");
}else {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try{
sdf.parse(birthday);
}catch (Exception e){
msg.put("birthday","生日格式不对!");
}
}
return msg.isEmpty();
}
//省略实例域的get,set方法
所以在注册实现的时候,需要加入表单验证操作:
public class RegServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)throws IOException,ServletException{
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=utf-8");
//获取表单数据
User user = new User();
UserForm uf = new UserForm();
try{ // 将用户提交的注册信息注入到UserForm类中,首先进行校验,如果校验失败,则请求转发,同时将uf传递
BeanUtils.populate(uf,request.getParameterMap());
if(!uf.valid()){ //msg不为空,有错误
request.setAttribute("uf",uf);
request.getRequestDispatcher("/reg.jsp").forward(request,response);
return;
}
}catch (Exception e){
e.printStackTrace();
}
//如果执行到这里,说明校验成功,则将数据注入到User类中
UserService us = new UserServiceImpl();
try{
/**使用BeanUtils实现对象数据自动set,但是时间设置会出现问题
org.apache.commons.beanutils.converters.DateTimeConverter.toDate
DateConverter does not support default String to 'Date' conversion.**/
ConvertUtils.register(new DateLocaleConverter(), Date.class);
BeanUtils.populate(user,request.getParameterMap());
//调用业务逻辑
//注册的用户如果存在,抛出异常UserExcisException
boolean b = us.findUserByName(user.getUsername());
us.register(user);
}catch (UserExcisException e){
request.setAttribute("error",e.getMessage());
request.getRequestDispatcher("/reg.jsp").forward(request,response);
}catch (Exception e){
e.printStackTrace();
}
//分发转向
response.getWriter().write("注册成功!1s后跳到主页...");
response.setHeader("refresh","1;url="+request.getContextPath()+"/index.jsp");
}
public void doPost(HttpServletRequest request,HttpServletResponse response)throws IOException,ServletException{
doGet(request,response);
}
}
注销:这个很简单,只需要将session销毁即可
public class LogOutServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)throws IOException,ServletException{
request.getSession().invalidate(); //session销毁
response.sendRedirect(request.getContextPath()+"/index.jsp");
}
public void doPost(HttpServletRequest request,HttpServletResponse response)throws IOException,ServletException{
doGet(request,response);
}
}
JSP:
index.jsp:登录的时候将用户信息以变量u保存在session对象中,所以根据u是否为空来判断用户是否登录
log.jsp:
reg.jsp:注册时候,如果用户已经存在,则将该错误信息保存在request域对象的error变量里,校验信息放在uf变量里
一些补充:
ConvertUtils.register注册转换器:当用到BeanUtils的populate方法(该方法可以实现用户提交的表单数据自动注入到实体对象中,动态获取,不需要我们通过set进行设置),其实都会调用convert进行转换,但Converter只支持一些基本的类型,甚至连java.util.Date类型也不支持。而且它比较笨的一个地方是当遇到不认识的类型时,居然会抛出异常来。
这个时候就需要给类型注册转换器。比如:意思是所以需要转成Date类型的数据都要通过DateLocaleConverter这个转换器的处理。
ConvertUtils.register(new DateLocaleConverter(), Date.class);